狠狠撸
Submit Search
罢丑谤颈蹿迟多路复用的设计与实现
?
1 like
?
1,302 views
B
billowqiu
1 of 6
Download now
Downloaded 12 times
More Related Content
罢丑谤颈蹿迟多路复用的设计与实现
1.
Thrift 服务(Server)复用的设计与实现 作者:billowqiu@163.com 介绍 Thrift 作为一个跨语言的
rpc 框架,为后端服务间的多语言混合开发提供了高可靠,可 扩展,以及高效的实现。但是自从 2007 年 Facebook 开源后的 6 年时间内一直缺少一个多路 复用的功能(Multiplexing Services),也就是在一个 Server 上面实现多个 Service 调用。比如 要实现一个后端服务,很多时候不仅仅只是实现业务服务接口,往往还会预留一些调试, 监 控服务接口,以往要实现多个 Service 在一个 Server 上面调用,要么将多个服务糅合成一个 庞大的服务(图 1) ,要么将多个 Service 分摊到不同的 Server 上(图 2) 。最近的版本 0.9.1 终于实现内置的多路复用, 本文简要分析一下该功能的设计与实现, 并提供一个简单的示例。 图1
2.
图2 ? 第一种方式将所有的接口放到一个服务中,会导致新接口增加时的代码维护成本 ? 第二种方式采取将不同的 Service 绑定到不同的
Server,因为多个 Server 会监听多个端 口,最后导致运维方面的管理和维护成本增加(端口在某些时候是一个有限资源) 设计 Thrift 的架构如下: 图3 Thrift 采取了很优雅的分层设计,下面简述各层的主要功能:
3.
? Transport 负责传输数据的接口,有文件,内存,套接字等实现。 ? Protocol 负责数据的协议交互接口,有二进制,Json 等实现。 ? Processor 负责输入输出数据处理的接口,其实就是对 Protocol
的处理。 ? Server 包括了上面各层的创建和管理, 并提供网络服务的功能, 网络服务这块目前有四个实现, 分别是最简单的单线程阻塞,多线程阻塞,线程池阻塞和基于 libevent 的非阻塞模式。 实现多路复用有以下几个需要注意的地方: ? 代码接口兼容 ? 协议实现兼容 ? 不依赖任何 Server 层代码,也不需要新的 Server 实现 0.9.1 之前版本不能多个 Service 在同一个 Server 上面调用, 主要是因为在协议层只把 Service 的函数名打包, 没有将 ServiceName 打包进去, 所以在 Processor 层默认只能处理一个 Service。 新版本中通过新增以下设计(红色部分)完成了 Service 的多路复用:
4.
图4 图5 ? Client 增加一个 MultiplexProtcol
实现,在原有协议基础上增加 ServiceName 的打包 ? Server 增加一个 MultiplexProssor 实现,分离不同的具体服务 实现 从 Release-Notes 看到目前已经有 Java,Delphi,C#,C++几个语言实现了该功能,具体到 C++实现按照 THRIFT-1902 的介绍,是直接移植的 java 版本。 新增代码 protocol/TMultiplexedProtocol.h protocol/TMultiplexedProtocol.cpp
5.
protocol/TProtocolDecorator.h processor/TMultiplexedProcessor.h 主要逻辑 1) TMultiplexedProtocol 类重写 writeMessageBegin_virt
方法, Service 的函数名前新增了 在 ServiceName,并附带了一个分隔符;该类采取了 Decorator 模式将绝大部分操作转发到 实际的 protocol 对象。 2) TMultiplexedProcessor 类增加了一个存放 service 的 map, typedef std::map< std::string, shared_ptr<TProcessor> > services_t; key 为 ServiceName , 这 样 在 process 方 法 中 先 解 出 TMultiplexedProtocol 传 递 过 来 的 ServiceName, 通过该 key 查找到对应的 Processor 对象, 再调用该 Processor 的 process 方法, 这样就完美的实现了多个 serivice 的共存。 使用示例 IDL: namespace cpp thrift.multiplex.demo service FirstService { void blahBlah() } service SecondService { void blahBlah() } Server: int port = 9090; shared_ptr<TProcessor> processor1(new FirstServiceProcessor (shared_ptr<FirstServiceHandler>(new FirstServiceHandler()))); shared_ptr<TProcessor> processor2(new SecondServiceProcessor (shared_ptr<SecondServiceHandler>(new SecondServiceHandler()))); //使用 MultiplexedProcessor
6.
shared_ptr<TMultiplexedProcessor> processor(new TMultiplexedProcessor()); //注册各自的
Service processor->registerProcessor("FirstService", processor1); processor->registerProcessor("SecondService", processor2); shared_ptr<TServerTransport> serverTransport(new TServerSocket(port)); shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory()); shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory()); TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory); server.serve(); Client: shared_ptr<TSocket> transport(new TSocket("localhost", 9090)); transport->open(); shared_ptr<TBinaryProtocol> protocol(new TBinaryProtocol(transport)); shared_ptr<TMultiplexedProtocol> mp1(new TMultiplexedProtocol(protocol, "FirstService")); shared_ptr<FirstServiceClient> service1(new FirstServiceClient(mp1)); shared_ptr<TMultiplexedProtocol> mp2(new TMultiplexedProtocol(protocol, "SecondService")); shared_ptr<SecondServiceClient> service2(new SecondServiceClient(mp2)); service1->blahBlah(); service2->blahBlah(); 总结 Thrift 通过在客户端采取的 decorator 模式巧妙的在协议层将 ServiceName 传递到服务端,服 务端通过常见的手段将 ServiceName 和具体的 Processor 进行映射,完美的解决了 Server 对 Service 的多路复用问题。 参考资料 1. 2. 3. 4. http://thrift.apache.org/docs/concepts/ https://issues.apache.org/jira/browse/THRIFT-1902 https://issues.apache.org/jira/browse/THRIFT-563 http://bigdata.impetus.com/whitepaper
Download