Thrift源码剖析
Thrift源码分析及一个完整的例子
CSDN Thrift源码分析
Thrift二进制序列化TCompactProtocol与TBinaryProtocol的原理和区别
一个thrift的例子
thrift 分为四个模块
TransPort
将Socket包装成各种TransPort使用。
TIOStreamTransport和TSocket这两个类的结构对应着阻塞同步IO, TSocket封装了Socket接口
TNonblockingTrasnsort,TNonblockingSocket这两个类对应着非阻塞IO
TMemoryInputTransport封装了一个字节数组byte[]来做输入流的封装
TMemoryBuffer使用字节数组输出流ByteArrayOutputStream做输出流的封装
TFramedTransport则封装了TMemoryInputTransport做输入流,封装了TByteArryOutPutStream做输出流,作为内存读写缓冲区的一个封装。TFramedTransport的flush方法时,会先写4个字节的输出流的长度作为消息头,然后写消息体。和FrameBuffer的读消息对应起来。FrameBuffer对消息时,先读4个字节的长度,再读消息体
TFastFramedTransport是内存利用率更高的一个内存读写缓存区,它使用自动增长的byte,而不是每次都new一个byte[],提高了内存的使用率。其他和TFramedTransport一样,flush时也会写4个字节的消息头表示消息长度。
Protocol
协议和编解码是一个网络应用程序的核心问题之一,客户端和服务器通过约定的协议来传输消息(数据),通过特定的格式来编解码字节流,并转化成业务消息,提供给上层框架调用。
Thrift的协议比较简单,它把协议和编解码整合在了一起。抽象类TProtocol定义了协议和编解码的顶层接口。
抽象类 TProtocol,构造方法需要一个Transport类型的对象
方法分为两大类,write方法和read方法
方法名的结构:
(write/read) + (Struct,Message,File,Map,List,Set) + (Begin/End)
(write/read) + (Bool,I16,I32,I64,Double,String,Binary)
Processor
基类TProcessor,只有一个process(TProtocol in, TProtocol out)
方法
TBaseProcessor继承自TProcessor:
private final I iface;
private final Map> processMap;
实现process方法,去map找到对应的方法,并执行:
public boolean process(TProtocol in, TProtocol out) throws TException {
TMessage msg = in.readMessageBegin();
ProcessFunction fn = processMap.get(msg.name);
fn.process(msg.seqid, in, out, iface);
return true;
}
TProcessorFactory有两个方法getProcessor(TTransport trans)
和isAsyncProcessor()
两个方法。
Server
共有以下几种类型:
server的工作可以分为:监听socket链接、数据的读写和业务逻辑的处理三部分。
- TSimpleServer
TSimpleServer的工作模式只有一个工作线程,循环监听新请求的到来并完成对请求的处理,它只是在简单的演示时候使用
- TNonBlockingServer
TNonblockingServer工作模式,该模式也是单线程工作,但是该模式采用NIO的方式,所有的socket都被注册到selector中,在一个线程中通过seletor循环监控所有的socket,每次selector结束时,处理所有的处于就绪状态的socket,对于有数据到来的socket进行数据读取操作,对于有数据发送的socket则进行数据发送,对于监听socket则产生一个新业务socket并将其注册到selector中。
- THsHaServer
THsHaServer类是TNonblockingServer类的子类,在5.2节中的TNonblockingServer模式中,采用一个线程来完成对所有socket的监听和业务处理,造成了效率的低下,THsHaServer模式的引入则是部分解决了这些问题。THsHaServer模式中,引入一个线程池来专门进行业务处理。但监听和读写数据还是主线程来做,因此可能有性能问题。
- TThreadPoolServer
线程池模式中,数据读取和业务处理都交由线程池完成,主线程只负责监听新连接,因此在并发量较大时新连接也能够被及时接受。线程池模式比较适合服务器端能预知最多有多少个客户端并发的情况,这时每个请求都能被业务线程池及时处理,性能也非常高。
线程池模式的处理能力受限于线程池的工作能力,当并发请求数大于线程池中的线程数时,新请求也只能排队等待。
- TThreadedSelectorServer
(1)一个AcceptThread线程对象,专门用于处理监听socket上的新连接;
(2) 若干个SelectorThread对象专门用于处理业务socket的网络I/O操作,所有网络数据的读写均是有这些线程来完成;
(3) 一个负载均衡器SelectorThreadLoadBalancer对象,主要用于AcceptThread线程接收到一个新socket连接请求时,决定将这个新连接请求分配给哪个SelectorThread线程。
(4) 一个ExecutorService类型的工作线程池,在SelectorThread线程中,监听到有业务socket中有调用请求过来,则将请求读取之后,交个ExecutorService线程池中的线程完成此次调用的具体执行;
FrameBuffer
FrameBuffer是ThriftNIO服务器端的一个核心组件,它一方面承担了NIO编程中的缓冲区的功能,另一方面还承担了RPC方法调用的职责。
方法调用
(1)自动生成的Iface接口,是远程方法的顶层接口
(2)自动生成的Processor类及相关父类,包括TProcessor接口,TBaseProcess抽象类
(3)ProcessFunction抽象类,抽象了一个具体的方法调用,包含了方法名信息,调用方法的抽象过程等
(4)TNonblcokingServer,是NIO服务器的默认实现,通过Args参数来配置Processor等信息
(5)FrameBuffer类,服务器NIO的缓冲区对象,这个对象在服务器端收到全包并解码后,会调用Processor去完成实际的方法调用
服务器端的方法的具体实现类,实现Iface接口
- 通过IDL来定义接口: DemoService.thrift
namespace java com.thrift.test
service DemoService{
string sayHi(1:string name);
}
根据IDL自动生成代码
thrift -r --gen java DemoService.thrift先生成一个IFace和IAsynceFace。
public interface Iface {
public String sayHi(String name) throws org.apache.thrift.TException;
}
生成args对象
生成一个client对象,包括发送请求和接收结果的方法。
public String sayHi(String name) throws org.apache.thrift.TException{
send_sayHi(name);
return recv_sayHi();
}
public void send_sayHi(String name) throws org.apache.thrift.TException{
sayHi_args args = new sayHi_args();
args.setName(name);
sendBase("sayHi", args);
}
public String recv_sayHi() throws org.apache.thrift.TException
{
sayHi_result result = new sayHi_result();
receiveBase(result, "sayHi");
if (result.isSetSuccess()) {
return result.success;
}
throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "sayHi failed: unknown result");
}
- 生成processor对象
protected sayHi_args getEmptyArgsInstance() {
return new sayHi_args();
}
protected sayHi_result getResult(I iface, sayHi_args args) throws org.apache.thrift.TException {
sayHi_result result = new sayHi_result();
result.success = iface.sayHi(args.name);
return result;
}
- 实现IFace接口
package com.thrift.test;
import org.apache.thrift.TException;
public class DemoServiceImpl implements DemoService.Iface{
@Override
public String sayHi(String name) throws TException {
return "Hi " + name + ", from Thrift Server";
}
}
- Client
public class Client {
public static void main(String[] args) throws Exception{
TSocket socket = new TSocket("127.0.0.1", 9090);
socket.setTimeout(3000);
TTransport transport = new TFramedTransport(socket);
TProtocol protocol = new TCompactProtocol(transport);
transport.open();
System.out.println("Connected to Thrfit Server");
DemoService.Client client = new DemoService.Client.Factory()
.getClient(protocol);
String result = client.sayHi("ITer_ZC");
System.out.println(result);
}
}
- server
public class Server {
public static void main(String[] args){
TNonblockingServerSocket socket;
try {
socket = new TNonblockingServerSocket(9090);
TNonblockingServer.Args options = new TNonblockingServer.Args(socket);
TProcessor processor = new DemoService.Processor(new DemoServiceImpl());
options.processor(processor);
options.protocolFactory(new TCompactProtocol.Factory());
TServer server = new TNonblockingServer(options);
System.out.println("Thrift Server is running at 9090 port");
server.serve();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}