这是我参与「第四届青训营 」笔记创作活动的第3天。
流计算项目的实现需要用到 RPC 技术。在上一篇笔记中,对thrift 的基础和使用步骤做了介绍。下面的笔记是对 thrift Server 的更深入学习。
Thrift 网络栈
分为四层,自底向上分别为:Transport,Protocol,Processor,Server
- Transport
提供了一个简单的网络数据读写抽象层,使得thrift 底层的传输和其他系统解耦。 方法:open, close, read, write, flush
- Protocol
定义transport层的传输的数据格式,作序列化和反序列化的格式要求。 几种protocol:
- TBinaryProtocol : 二进制格式.
- TCompactProtocol : 压缩格式
- TJSONProtocol : JSON格式
- TSimpleJSONProtocol : JSON只写协议
- Processor
封装了从输入数据流中读数据和向数据数据流中写数据的操作。读写数据流用Protocol对象表示。
- Server
Server 层做一个上述功能特性的聚合。Server实现的几个步骤:
- 创建一个transport对象
- 为transport对象创建输入输出protocol
- 基于输入输出protocol创建processor
- 等待连接请求并将之交给processor处理
Server代码示例:
/** * 1. 创建Transport */
TServerSocket serverTransport = new TServerSocket(SERVER_PORT);
TServer.Args tArgs = new TServer.Args(serverTransport);
/** * 2. 为Transport创建Protocol */
tArgs.protocolFactory(new TBinaryProtocol.Factory());
// tArgs.protocolFactory(new TCompactProtocol.Factory());
// tArgs.protocolFactory(new TJSONProtocol.Factory());
/** * 3. 为Protocol创建Processor */
TProcessor tprocessor = new UserService.Processor<UserService.Iface>(new UserServiceImpl());
tArgs.processor(tprocessor);
/** * 4. 创建Server并启动
TServer server = new TSimpleServer(tArgs);
logger.info("UserService TSimpleServer start ....");
server.serve();
Server 类型
下面主要介绍四种类型: TSimpleServer、TNonblockingServer、TThreadPoolServer、THsHaServer
-
TSimpleServer -单线程服务器端,使用标准的堵塞式I/O。
TSimpleServer的工作模式只有一个工作线程,循环监听新请求的到来并完成对请求的处理,一次只能接收和处理一个Socket连接,效率比较低。
-
TNonblockingServer – 多线程服务器端,使用非堵塞式I/O。
TNonblockingServer的工作模式也是单线程工作,但是该模式采用NIO的方式,所有的Socket都被注册到Selector中,在一个线程上通过Selector循环监控所有的Socket,每次Selector结束时,处理所有的处于就绪状态的Socket,对于有数据到来的Socket进行数据读取操作,对于有数据发送的Socket则进行数据发送,对于监听Socket则产生一个新业务Socket并将其注册到Selector中。
相比于TSimpleServer效率,提升主要体现在I/O多路复用上,TNonblockingServer采用非阻塞I/O,同时监控多个Socket的状态变化。TNonblockingServer模式在业务处理上还是采用单线程顺序来完成。
-
TThreadPoolServer - 多线程服务器端,使用标准的堵塞式I/O。
TThreadPoolServer模式采用阻塞Socket方式工作,主线程负责阻塞式监听是否有新Socket到来,业务处理交由一个线程池来处理。
线程池模式中,数据读取和业务处理都交由线程池完成,主线程只负责监听新连接,因此在并发量较大时新连接也能够被及时接受。
线程池模式比较适合服务器端能预知最多有多少个客户端并发的情况,这时每个请求都能被业务线程池及时处理,性能也非常高。
-
THsHaServer - TNonblockingServer类的子类
在TNonblockingServer模式中,采用一个线程来完成对所有Socket的监听和业务处理,造成了效率的底下,而THsHaServer模式中,引入一个线程池来专门进行业务处理。
与TNonblockingServer模式相比,THsHaServer在完成数据读取之后,将业务处理过程交由一个线程池来完成,主线程直接返回进行下一次循环操作,效率大大提升。但是主线程需要完成对所有Socket的监听以及数据读写的工作。
thrift 多线程
在rpc 的过程中,服务端应该能同时响应多个客户端的请求,即具备多线程机制。
TThreadPoolServer
TThreadPoolServer 作为一种多线程的 Server 类型,对于每个连接都从线程池分配一个线程去处理。从而提高了并发处理能力。
其serve()方法如下:
流程:accept一个Transport=>把这个Transport转成WorkerProcess => 将WorkerProcess放到executorService执行。
使用示例
TThreadPoolServer参数
- minWorkerThreads:核心工作线程数。建议设置成CPU个数的倍数。
- maxWorkerThreads:在资源允许的情况下,可设置成跟minWorkerThreads相同或其两倍。
- stopTimeoutVal:保持默认。
- requestTimeout:一个请求被Accept到能够被execute(只是被接收,而不是执行成功)成功的时间。该值可以小一点,因为pool满了,再加也意义不大。
- beBackoffSlotInMillis:保持默认。
- pool等待队列:默认用的是SynchronousQueue,因此实际上该队列不会缓存任务,而是直接将任务分配给线程池。如果线程池没有空闲线程且已经到了最大线程数,那么就会reject。
TThreadPoolServer优缺点
优点:
-
拆分了监听线程(Accept Thread)和处理客户端连接的工作线程(Worker Thread),数据读取和业务处理都交给线程池处理。因此在并发量较大时新连接也能够被及时接受。
-
线程池模式比较适合服务器端能预知最多有多少个客户端并发的情况,这时每个请求都能被业务线程池及时处理,性能也非常高。
缺点:
- 线程池模式的处理能力受限于线程池的工作能力,当并发请求数大于线程池中的线程数时,新请求也只能排队等待。