狠狠撸

狠狠撸Share a Scribd company logo
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

?

第一种方式将所有的接口放到一个服务中,会导致新接口增加时的代码维护成本

?

第二种方式采取将不同的 Service 绑定到不同的 Server,因为多个 Server 会监听多个端
口,最后导致运维方面的管理和维护成本增加(端口在某些时候是一个有限资源)

设计
Thrift 的架构如下:

图3

Thrift 采取了很优雅的分层设计,下面简述各层的主要功能:
?

Transport
负责传输数据的接口,有文件,内存,套接字等实现。

?

Protocol
负责数据的协议交互接口,有二进制,Json 等实现。

?

Processor
负责输入输出数据处理的接口,其实就是对 Protocol 的处理。

?

Server
包括了上面各层的创建和管理,
并提供网络服务的功能,
网络服务这块目前有四个实现,

分别是最简单的单线程阻塞,多线程阻塞,线程池阻塞和基于 libevent 的非阻塞模式。
实现多路复用有以下几个需要注意的地方:

?

代码接口兼容

?

协议实现兼容

?

不依赖任何 Server 层代码,也不需要新的 Server 实现

0.9.1 之前版本不能多个 Service 在同一个 Server 上面调用,
主要是因为在协议层只把 Service
的函数名打包,
没有将 ServiceName 打包进去,
所以在 Processor 层默认只能处理一个 Service。
新版本中通过新增以下设计(红色部分)完成了 Service 的多路复用:
图4

图5

?

Client 增加一个 MultiplexProtcol 实现,在原有协议基础上增加 ServiceName 的打包

?

Server 增加一个 MultiplexProssor 实现,分离不同的具体服务

实现
从 Release-Notes 看到目前已经有 Java,Delphi,C#,C++几个语言实现了该功能,具体到
C++实现按照 THRIFT-1902 的介绍,是直接移植的 java 版本。

新增代码
protocol/TMultiplexedProtocol.h
protocol/TMultiplexedProtocol.cpp
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
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

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