Thrift
什么是Thrift?
Thrift
是一个轻量级、跨语言的远程服务调用框架,支持C++
、Java
、Python
、PHP
、Ruby
等。通过代码生成引擎自动生成RPC接口Thrift IDL
文件对应的各种主流语言的RPC
服务端/客户端模板代码,省去自定义和维护接口编解码、消息传输、服务器多线程模型等基础工作,服务端只需要编写接口实现类,客户端根据服务对象调用远端服务。
Thrift架构
Thrift
架构从下往上为传输层、协议层、处理层和服务层。
- 传输层主要负责从网络中读/写数据,定义了网络传输协议。
- 协议层定义了数据传输格式,负责处理网络数据的序列化和反序列化。
- 处理层由
IDL
生成,封装具体的底层网络传输和序列化方式,并委托给用户实现的Handle
进行处理。 - 服务层提供网络I/O服务模型。
Thrift协议有哪些?
Thrift
可以让用户选择客户端与服务端之间传输通信协议的类别。
TBinaryProtocol
:使用二进制编码格式传输,Thrift
的默认传输协议。TCompactProtocol
:使用压缩格式传输。TJSONProtocol
:使用JSON
格式传输。TDebugProtocol
:使用文本格式传输,便于debug
。TSimpleJSONProtocol
:提供JSON
只写的协议,适用于通过脚本语言解析。
Thrift传输层有哪些?
TSocket
:阻塞式I/O,用在客户端。TServerSocket
:非阻塞式I/O,用于服务器端监听TSocket
。TNonblockingSocket
:非阻塞式I/O,用于构建异步客户端。TMemoryInputTransport
:封装了一个字节数组byte[]
做输入流。TFramedTransport
:非阻塞式I/O,按块的大小进行传输(类似于NIO
)。
Thrift服务器端有哪些?
TServer
TServer
定义了静态内部类Args
,Args
继承自抽象类AbstractServerArgs
。AbstractServerArgs
采用了建造者模式,向TServer
提供各种工厂。
属性 | 类型 | 作用 |
---|---|---|
processorFactory | TProcessorFactory | 处理层工厂类,用于创建TProcessor对象 |
inputTransportFactory | TTransportFactory | 传输层输入工厂类,用于创建TTransport对象 |
outputTransportFactory | TTransportFactory | 传输层输出工厂类,用于创建TTransport对象 |
inputProtocolFactory | TProtocolFactory | 协议层输入工厂类,用于创建TProtocol对象 |
outputProtocolFactory | TProtocolFactory | 协议层输出工厂类,用于创建TProtocol对象 |
TServer
核心方法:
serve():启动服务。serve()为抽象方法,不同实现类的启动方式不一样,可各自实现。
stop():关闭服务。
isServing():检测服务状态(启动/关闭)。
setServing(boolean serving):设置服务状态。
TSimpleServer
1. 特点
单线程,阻塞I/O。
2. 设计思想
由主线程负责监听、数据读写、业务处理(一次只能接收和处理一个socket
连接)。
3. 使用
客户端:
public class HelloClient {
private static final Logger LOGGER = Logger.getLogger(HelloClient.class.getName());
public static void main(String[] args) {
TTransport transport = null;
try {
//传输层使用阻塞I/O
transport = new TSocket("127.0.0.1", 9090);
transport.open();
//使用二进制协议传输数据
TProtocol protocol = new TBinaryProtocol(transport);
//使用同步客户端
GreetingService.Client client = new GreetingService.Client(protocol);
String name = "XuDT";
LOGGER.info("HelloClient 请求参数[name]=" + name);
//调用接口
String result = client.sayHello(name);
LOGGER.info("Server 返回结果为" + result);
} catch (TException e) {
e.printStackTrace();
} finally {
transport.close();
}
}
}
服务端:
public class SimpleServer {
private static final Logger LOGGER = Logger.getLogger(SimpleServer.class.getName());
public static void main(String[] args) {
try {
//监听端口9090
TServerSocket serverTransport = new TServerSocket(9090);
//使用二进制协议传输数据
TBinaryProtocol.Factory proFactory = new TBinaryProtocol.Factory();
//关联处理器与HelloService服务实现
TProcessor processor = new HelloService.Processor(new HelloServiceImpl());
TSimpleServer.Args serverArgs = new TSimpleServer.Args(serverTransport);
serverArgs.processor(processor);
serverArgs.protocolFactory(proFactory);
//使用TSimpleServer服务端
TServer server = new TSimpleServer(serverArgs);
LOGGER.info("Start SimpleServer on port 9090...");
//启动服务
server.serve();
} catch (TTransportException e) {
e.printStackTrace();
}
}
}
Processor为HelloService的内部类,调用HelloService.Processor(new HelloServiceImpl())会生成一个processMap,key为接口名称,value为该方法调用对象,后续TBaseProcessor.process()就是通过对processMap进行processMap.get(接口名称)操作获取接口。
4. TSimpleServer源码分析
TSimpleServer
继承了TServer
,实现了TServer
的serve()
和stop()
方法。
public class TSimpleServer extends TServer {
private static final Logger LOGGER = LoggerFactory.getLogger(TSimpleServer.class.getName());
public TSimpleServer(AbstractServerArgs args) {
super(args);
}
/**
* 启动服务
*/
public void serve() {
try {
//监听端口
serverTransport_.listen();
} catch (TTransportException ttx) {
LOGGER.error("Error occurred during listening.", ttx);
return;
}
// Run the preServe event
if (eventHandler_ != null) {
eventHandler_.preServe();
}
//开启服务
setServing(true);
//循环等待客户端请求
while (!stopped_) {
TTransport client = null;
TProcessor processor = null;
TTransport inputTransport = null;
TTransport outputTransport = null;
TProtocol inputProtocol = null;
TProtocol outputProtocol = null;
ServerContext connectionContext = null;
try {
//接受连接
client = serverTransport_.accept();
if (client != null) {
//TProcessorFactory处理器
processor = processorFactory_.getProcessor(client);
//获取客户端输入通道
inputTransport = inputTransportFactory_.getTransport(client);
//获取客户端输出通道
outputTransport = outputTransportFactory_.getTransport(client);
//获取客户端输入协议
inputProtocol = inputProtocolFactory_.getProtocol(inputTransport);
//获取客户端输出协议
outputProtocol = outputProtocolFactory_.getProtocol(outputTransport);
if (eventHandler_ != null) {
connectionContext = eventHandler_.createContext(inputProtocol, outputProtocol);
}
//处理请求
while (true) {
if (eventHandler_ != null) {
eventHandler_.processContext(connectionContext, inputTransport, outputTransport);
}
//处理请求
processor.process(inputProtocol, outputProtocol);
}
}
} catch (TTransportException ttx) {
// Client died, just move on
} catch (TException tx) {
if (!stopped_) {
LOGGER.error("Thrift error occurred during processing of message.", tx);
}
} catch (Exception x) {
if (!stopped_) {
LOGGER.error("Error occurred during processing of message.", x);
}
}
if (eventHandler_ != null) {
//删除事件
eventHandler_.deleteContext(connectionContext, inputProtocol, outputProtocol);
}
//关闭输入通道
if (inputTransport != null) {
inputTransport.close();
}
//关闭输出通道
if (outputTransport != null) {
outputTransport.close();
}
}
//关闭服务
setServing(false);
}
/**
* 停止服务
*/
public void stop() {
stopped_ = true;
serverTransport_.interrupt();
}
}
TBaseProcessor.process()
:处理请求信息,调用处理方法。
public void process(TProtocol in, TProtocol out) throws TException {
//获取请求信息:参数、调用函数名等
TMessage msg = in.readMessageBegin();
//根据函数名获取处理函数
ProcessFunction fn = processMap.get(msg.name);
//异常处理
if (fn == null) {
TProtocolUtil.skip(in, TType.STRUCT);
in.readMessageEnd();
TApplicationException x = new TApplicationException(TApplicationException.UNKNOWN_METHOD, "Invalid method name: '"+msg.name+"'");
out.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));
x.write(out);
out.writeMessageEnd();
out.getTransport().flush();
} else {
//处理请求
fn.process(msg.seqid, in, out, iface);
}
}
ProcessFunction是一个抽象类,子类也是根据IDL自动生成,与IDL中的函数一一对应,为代理处理器。
ProcessFunction.process()
:调用接口处理业务请求并返回结果。
public final void process(int seqid, TProtocol iprot, TProtocol oprot, I iface) throws TException {
//获取一个空的参数封装
T args = getEmptyArgsInstance();
try {
//从inputProtocol中获取参数赋给args
args.read(iprot);
} catch (TProtocolException e) {
//异常处理
}
iprot.readMessageEnd();
TSerializable result = null;
byte msgType = TMessageType.REPLY;
try {
//根据参数args调用接口
result = getResult(iface, args);
} catch (TTransportException ex) {
//异常处理
}
if(!isOneway()) {
//输出调用结果到outputProtocol
oprot.writeMessageBegin(new TMessage(getMethodName(), msgType, seqid));
result.write(oprot);
oprot.writeMessageEnd();
oprot.getTransport().flush();
}
}
5. 时序图
6. 不足
TSimpleServer
一次只能处理一个socket
连接,效率较低。
TSimpleServer
效率低,那有什么办法能提高效率呢?
TThreadPoolServer
1. 特点
多线程,阻塞I/O,主线程负责阻塞监听socket
连接,具体的业务处理交由一个线程池来处理。
2. 设计思想
主线程负责阻塞监听socket
,当有socket
就绪时,将其封装成一个WorkerProcess
对象提交到线程池,由线程池负责业务处理后将结果返回给客户端。
线程池默认最小线程数为5,最大线程数为Integer.MAX_VALUE。
3. 使用
客户端同
TSimpleServer
。
服务端:
public class ThreadPoolServer {
private static final Logger LOGGER = Logger.getLogger(ThreadPoolServer.class.getName());
public static void main(String[] args) {
try {
//监听端口9090
TServerSocket serverTransport = new TServerSocket(9090);
//使用二进制协议传输数据
TBinaryProtocol.Factory proFactory = new TBinaryProtocol.Factory();
//关联处理器与HelloService服务实现
TProcessor processor = new HelloService.Processor(new HelloServiceImpl());
TThreadPoolServer.Args serverArgs = new TThreadPoolServer.Args(serverTransport);
serverArgs.processor(processor);
serverArgs.protocolFactory(proFactory);
//使用TThreadPoolServer服务端
TServer server = new TThreadPoolServer(serverArgs);
LOGGER.info("Start ThreadPoolServer on port 9090...");
//启动服务
server.serve();
} catch (TTransportException e) {
e.printStackTrace();
}
}
}
4. 源码分析
TThreadPoolServer
继承了TServer
,实现了TServer
的serve()
和stop()
方法。
public class TThreadPoolServer extends TServer {
private static final Logger LOGGER = LoggerFactory.getLogger(TThreadPoolServer.class.getName());
//参数初始化
public static class Args extends AbstractServerArgs<Args> {
//线程池参数
public int minWorkerThreads = 5;
public int maxWorkerThreads = Integer.MAX_VALUE;
public ExecutorService executorService;
public int stopTimeoutVal = 60;
public TimeUnit stopTimeoutUnit = TimeUnit.SECONDS;
public int requestTimeout = 20;
public TimeUnit requestTimeoutUnit = TimeUnit.SECONDS;
public int beBackoffSlotLength = 100;
public TimeUnit beBackoffSlotLengthUnit = TimeUnit.MILLISECONDS;
public Args(TServerTransport transport) {
super(transport);
}
public Args minWorkerThreads(int n) {
minWorkerThreads = n;
return this;
}
public Args maxWorkerThreads(int n) {
maxWorkerThreads = n;
return this;
}
public Args stopTimeoutVal(int n) {
stopTimeoutVal = n;
return this;
}
public Args stopTimeoutUnit(TimeUnit tu) {
stopTimeoutUnit = tu;
return this;
}
public Args requestTimeout(int n) {
requestTimeout = n;
return this;
}
public Args requestTimeoutUnit(TimeUnit tu) {
requestTimeoutUnit = tu;
return this;
}
//Binary exponential backoff slot length
public Args beBackoffSlotLength(int n) {
beBackoffSlotLength = n;
return this;
}
//Binary exponential backoff slot time unit
public Args beBackoffSlotLengthUnit(TimeUnit tu) {
beBackoffSlotLengthUnit = tu;
return this;
}
public Args executorService(ExecutorService executorService) {
this.executorService = executorService;
return this;
}
}
//工作线程池
private ExecutorService executorService_;
private final TimeUnit stopTimeoutUnit;
private final long stopTimeoutVal;
private final TimeUnit requestTimeoutUnit;
private final long requestTimeout;
private final long beBackoffSlotInMillis;
private Random random = new Random(System.currentTimeMillis())