# 介绍
Thrift作为一个跨语言的rpc框架,为后端服务间的多语言混合开发提供了高可靠,可扩展,以及高效的实现。但是自从2007年facebook开源后的6年时间内一直缺少一个多路复用的功能(Multiplexing Services),也就是在一个Server上面实现多个Service调用。比如要实现一个后端服务,很多时候不仅仅只是实现业务服务接口,往往还会预留一些调试,监控服务接口,以往要实现多个Service在一个Server上面调用,要么将多个服务糅合成一个庞大的服务(图1),要么将多个Service分摊到不同的Server上(图2)。最近的版本0.9.1终于实现内置的多路复用,本文将就该功能简要分析一下该功能的设计与实现,并提供一个简单的示例。
图1
图2
## 设计
Thrift的架构如下:
Thrift采取了很优雅的分层设计,下面简述各层的主要功能:
Ø Transport
负责传输数据的接口,有文件,内存,套接字等实现。
Ø Protocol
负责数据的协议交互接口,有二进制,Json等实现。
Ø Processor
负责输入输出数据处理的接口,其实就是对Protocol的处理。
Ø Server
包括了上面各层的创建和管理,并提供网络服务的功能,网络服务这块目前有四个实现,分别是最简单的单线程阻塞,多线程阻塞,线程池阻塞和基于libevent的非阻塞模式。
实现多路复用有以下几个需要注意的地方:
Ø 不修改现有的Thrift代码,主要是指向后兼容,旧代码可以在新版本中编译
Ø 不修改白皮书上说的协议
Ø 不依赖任何Server层代码,也不需要新的Server实现
0.9.1之前版本不能多个Service在同一个Server上面调用,主要是因为在协议层只把Service的函数名打包,没有将ServiceName打包进去,所以在Processor层默认只能处理一个Service。
新版本中通过新增以下设计完成了Service的多路复用:
深色部分为新增部分,需要多个Service复用Server的客户端需要使用新的Protocol实现,支持Service复用的服务端需要通过新的Processor实现注册不同的Service。
## 实现
从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进行映射,完美的解决了Service的多路复用问题。
## 参考资料
1. http://thrift.apache.org/docs/concepts/
2. https://issues.apache.org/jira/browse/THRIFT-1902
3. https://issues.apache.org/jira/browse/THRIFT-563
4. http://bigdata.impetus.com/whitepaper