Thrift RPC实战(二) Thrift 网络服务模型
限于篇幅关系,在观察源码的时候,只列举了部分源代码
TServer类层次体系
TSimpleServer/TThreadPoolServer是阻塞服务模型
TNonblockingServer/THsHaServer/TThreadedSelectotServer是非阻塞服务模型(NIO)
1 TServer抽象类的定义
内部静态类Args的定义, 用于TServer类用于串联软件栈(传输层, 协议层, 处理层)
public abstract class TServer {
public static class Args extends AbstractServerArgs<Args> {
public Args(TServerTransport transport) {
super(transport);
}
}
public static abstract class AbstractServerArgs<T extends AbstractServerArgs<T>> {
public AbstractServerArgs(TServerTransport transport);
public T processorFactory(TProcessorFactory factory);
public T processor(TProcessor processor);
public T transportFactory(TTransportFactory factory);
public T protocolFactory(TProtocolFactory factory);
}
}
TServer类定义的抽象类
public abstract class TServer {
public abstract void serve();
public void stop();
public boolean isServing();
public void setServerEventHandler(TServerEventHandler eventHandler);
}
评注:
抽象函数serve由具体的TServer实例来实现, 而并非所有的服务都需要优雅的退出, 因此stop没有被定义为抽象
2 TSimpleServer
TSimpleServer的工作模式采用最简单的阻塞IO,实现方法简洁明了,便于理解,但是一次只能接收和处理一个socket连接,效率比较低,主要用于演示Thrift的工作过程,在实际开发过程中很少用到它。
工作方式如图:
抽象的代码可简单描述如下:
// *) server socket进行监听
serverSocket.listen();
while ( isServing() ) {
// *) 接受socket链接
client = serverSocket.accept();
// *) 封装处理器
processor = factory.getProcess(client);
while ( true ) {
// *) 阻塞处理rpc的输入/输出
if ( !processor.process(input, output) ) {
break;
}
}
}
3 ThreadPoolServer
TThreadPoolServer模式采用阻塞socket方式工作,,主线程负责阻塞式监听“监听socket”中是否有新socket到来,业务处理交由一个线程池来处
工作模式图:
ThreadPoolServer解决了TSimple不支持并发和多连接的问题, 引入了线程池. 实现的模型是One Thread Per Connection
线程池代码片段:
private static ExecutorService createDefaultExecutorService(Args args) {
SynchronousQueue<Runnable> executorQueue =
new SynchronousQueue<Runnable>();
return new ThreadPoolExecutor(args.minWorkerThreads,
args.maxWorkerThreads,
args.stopTimeoutVal,
TimeUnit.SECONDS,
executorQueue);
}
评注:
采用同步队列(SynchronousQueue), 线程池采用能线程数可伸缩的模式.
主线程循环简单描述代码:
setServing(true);
while (!stopped_) {
try {
TTransport client = serverTransport_.accept();
WorkerProcess wp = new WorkerProcess(client);
executorService_.execute(wp);
} catch (TTransportException ttx) {
}
}
TThreadPoolServer模式优点:
线程池模式中,拆分了监听线程(accept)和处理客户端连接的工作线程(worker),数据读取和业务处理都交由线程池完成,主线程只负责监听新连接,因此在并发量较大时新连接也能够被及时接受。线程池模式比较适合服务器端能预知最多有多少个客户端并发的情况,这时每个请求都能被业务线程池及时处理,性能也非常高。
TThreadPoolServer模式缺点:
线程池模式的处理能力受限于线程池的工作能力,当并发请求数大于线程池中的线程数时,新请求也只能排队等待
4 TNonblockingServer
TNonblockingServer该模式也是单线程工作,但是采用NIO的模式, 借助Channel/Selector机制, 采用IO事件模型来处理.
所有的socket都被注册到selector中,在一个线程中通过seletor循环监控所有的socket,每次selector结束时,处理所有的处于就绪状态的socket,对于有数据到来的socket进行数据读取操作,对于有数据发送的socket则进行数据发送,对于监听socket则产生一个新业务socket并将其注册到selector中。
工作原理图:
nio部分关键代码如下:
private void select() {
try {
// wait for io events.
selector.select();
// process the io events we received
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
while (!stopped_ && selectedKeys.hasNext()) {
SelectionKey key = selectedKeys.next();
selectedKeys.remove();
// skip if not valid
if (!key.isValid()) {
cleanupSelectionKey(key);
continue;
}
// if the key is marked Accept, then it has to be the server
// transport.
if (key.isAcceptable()) {
handleAccept();
} else if (key.isReadable()) {
// deal with reads
handleRead(key);
} else if (key.isWritable()) {
// deal with writes
handleWrite(key);
} else {
LOGGER.warn("Unexpected state in select! " + key.interestOps());
}
}
} catch (IOException e) {
LOGGER.warn("Got an IOException while selecting!", e);
}
}
TNonblockingServer模式优点:
相比于TSimpleServer效率提升主要体现在IO多路复用上,TNonblockingServer采用非阻塞IO,对accept/read/write等IO事件进行监控和处理,同时监控多个socket的状态变化;
TNonblockingServer模式缺点:
TNonblockingServer模式在业务处理上还是采用单线程顺序来完成,在业务处理比较复杂、耗时的时候,例如某些接口函数需要读取数据库执行时间较长,会导致整个服务被阻塞住,此时该模式效率也不高,因为多个调用请求任务依然是顺序一个接一个执行
5 THsHaServer
鉴于TNonblockingServer的缺点, THsHaServer继承TNonblockingServer,引入了线程池去处理, 其模型把读写任务放到线程池去处理.THsHaServer是Half-sync/Half-async的处理模式, Half-aysnc是在处理IO事件上(accept/read/write io), Half-sync用于handler对rpc的同步处理上.
工作模式图:
/**
* Helper to create an invoker pool
*/
protected static ExecutorService createInvokerPool(Args options) {
int minWorkerThreads = options.minWorkerThreads;
int maxWorkerThreads = options.maxWorkerThreads;
int stopTimeoutVal = options.stopTimeoutVal;
TimeUnit stopTimeoutUnit = options.stopTimeoutUnit;
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
ExecutorService invoker = new ThreadPoolExecutor(minWorkerThreads,
maxWorkerThreads, stopTimeoutVal, stopTimeoutUnit, queue);
return invoker;
}
THsHaServer的优点:
与TNonblockingServer模式相比,THsHaServer在完成数据读取之后,将业务处理过程交由一个线程池来完成,主线程直接返回进行下一次循环操作,效率大大提升;
THsHaServer的缺点:
主线程需要完成对所有socket的监听以及数据读写的工作,当并发请求数较大时,且发送数据量较多时,监听socket上新连接请求不能被及时接受。
6. TThreadedSelectorServer
TThreadedSelectorServer是对以上NonblockingServer的扩充, 其分离了Accept和Read/Write的Selector线程, 同时引入Worker工作线程池. 它也是种Half-sync/Half-async的服务模型
TThreadedSelectorServer模式是目前Thrift提供的最高级的模式,它内部有如果几个部分构成:
(1) 一个AcceptThread线程对象,专门用于处理监听socket上的新连接;
(2) 若干个SelectorThread对象专门用于处理业务socket的网络I/O操作,所有网络数据的读写均是有这些线程来完成;
(3) 一个负载均衡器SelectorThreadLoadBalancer对象,主要用于AcceptThread线程接收到一个新socket连接请求时,决定将这个新连接请求分配给哪个SelectorThread线程。
(4) 一个ExecutorService类型的工作线程池,在SelectorThread线程中,监听到有业务socket中有调用请求过来,则将请求读取之后,交个ExecutorService线程池中的线程完成此次调用的具体执行;主要用于处理每个rpc请求的handler回调处理(这部分是同步的).
工作模式图:
TThreadedSelectorServer模式中有一个专门的线程AcceptThread用于处理新连接请求,因此能够及时响应大量并发连接请求;另外它将网络I/O操作分散到多个SelectorThread线程中来完成,因此能够快速对网络I/O进行读写操作,能够很好地应对网络I/O较多的情况
从accpect线程到selectorThreads关键代码
protected boolean startThreads() {
try {
for (int i = 0; i < args.selectorThreads; ++i) {
selectorThreads.add(new SelectorThread(args.acceptQueueSizePerThread));//建立事件选择线程池
}
acceptThread = new AcceptThread((TNonblockingServerTransport) serverTransport_,
createSelectorThreadLoadBalancer(selectorThreads));//建立accept接受请求线程
for (SelectorThread thread : selectorThreads) {
thread.start();
}
acceptThread.start();
return true;
} catch (IOException e) {
LOGGER.error("Failed to start threads!", e);
return false;
}
}
负载均衡器SelectorThreadLoadBalancer对象部分关键代码:
protected SelectorThreadLoadBalancer createSelectorThreadLoadBalancer(Collection<? extends SelectorThread> threads) {
return new SelectorThreadLoadBalancer(threads);
}
/**
* A round robin load balancer for choosing selector threads for new
* connections.
*/
protected static class SelectorThreadLoadBalancer {
private final Collection<? extends SelectorThread> threads;
private Iterator<? extends SelectorThread> nextThreadIterator;
public <T extends SelectorThread> SelectorThreadLoadBalancer(Collection<T> threads) {
if (threads.isEmpty()) {
throw new IllegalArgumentException("At least one selector thread is required");
}
this.threads = Collections.unmodifiableList(new ArrayList<T>(threads));
nextThreadIterator = this.threads.iterator();
}
//根据循环负载均衡策略获取一个SelectorThread
public SelectorThread nextThread() {
// Choose a selector thread (round robin)
if (!nextThreadIterator.hasNext()) {
nextThreadIterator = threads.iterator();
}
return nextThreadIterator.next();
}
}
从SelectorThread线程中,监听到有业务socket中有调用请求,转到业务工作线程池关键代码
private void handleAccept() {
final TNonblockingTransport client = doAccept();//取得客户端的连接
if (client != null) {
// Pass this connection to a selector thread
final SelectorThread targetThread = threadChooser.nextThread();//获取目标SelectorThread
if (args.acceptPolicy == Args.AcceptPolicy.FAST_ACCEPT || invoker == null) {
doAddAccept(targetThread, client);
} else {
// FAIR_ACCEPT
try {
invoker.submit(new Runnable() {// 提交client的业务给到工作线程
public void run() {
doAddAccept(targetThread, client);
}
});
} catch (RejectedExecutionException rx) {
LOGGER.warn("ExecutorService rejected accept registration!", rx);
// close immediately
client.close();
}
}
}
}
demo地址:
码云:http://git.oschina.net/shunyang/thrift-all/tree/master/thrift-demo
github:https://github.com/shunyang/thrift-all/tree/master/thrift-demo
本文参考文章:
http://www.cnblogs.com/mumuxinfei/p/3875165.html
http://blog.csdn.net/sunmenggmail/article/details/46818147
Thrift RPC实战(二) Thrift 网络服务模型的更多相关文章
- Thrift RPC实战(三) thrift序列化揭秘
本文主要讲解Thrift的序列化机制, 看看thrift作为数据交换格式是如何工作的? 1.构造应用场景: 1). 首先我们先来定义下thrift的简单结构. 1 2 3 4 5 namespace ...
- Thrift RPC实战(一).初次体验Thrift
1.前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码,主要特点: 开发速度快: 通过编写RPC接口ID ...
- 开源RPC(gRPC/Thrift)框架性能评测
海量互联网业务系统只能依赖分布式架构来解决,而分布式开发的基石则是RPC:本文主要针对两个开源的RPC框架(gRPC. Apache Thrift),以及配合GoLang.C++两个开发语言进行性能对 ...
- 利用thrift rpc进行C++与Go的通信
一:什么是rpc rpc通俗来理解就是远程调用函数,相对于本地调用来说,只需要在主调函数中调用被掉函数即可,代码如下: void fun(int i) { cout << "fu ...
- rpc框架之 thrift 学习 1 - 安装 及 hello world
thrift是一个facebook开源的高效RPC框架,其主要特点是跨语言及二进制高效传输(当然,除了二进制,也支持json等常用序列化机制),官网地址:http://thrift.apache.or ...
- Apache Thrift学习之二(基础及原理)
Apache Thrift 是 Facebook 实现的一种高效的.支持多种编程语言的远程服务调用的框架.本文将从 Java 开发人员角度详细介绍 Apache Thrift 的架构.开发和部署,并且 ...
- 使用Thrift RPC编写程序(服务端和客户端)
1. Thrift类介绍 Thrift代码包(位于thrift-0.6.1/lib/cpp/src)有以下几个目录: concurrency:并发和时钟管理方面的库processor:Processo ...
- golang高性能RPC:Apache Thrift安装使用完全攻略
在企业应用中RPC的使用可以说是十分的广泛,使用该技术可以方便的与各种程序交互而不用考虑其编写使用的语言. 如果你对RPC的概念还不太清楚,可以点击这里. 现今市面上已经有许多应用广泛的RPC框架,比 ...
- Thrift RPC框架介绍
u 简介 Thrift是一种开源的跨语言的RPC服务框架.Thrift最初由facebook公司开发的,在2007年facebook将其提交apache基金会开源了.对于当时的facebook来说创造 ...
随机推荐
- Oracle性能分析11:系统统计信息
早期Oracle查询优化器的开销计算是基于运行SQL语句所须要的物理读,这种方法被叫做I/O开销模式(I/O cost model),这种方法的主要缺点是觉得单块读和多块读开销相当.在Oracle 8 ...
- oracle 电子商务解决方案讲义
1. 电商营销(CRM) - 高端客户体验 2. 当当网李国庆做 "千千面"购物体验 3. 所使用的唯一的产品oracle的CRM 4. 个人的事情.谁在世界上是用户体验. 5. ...
- ASP.NET MVC3中Model验证
原文:ASP.NET MVC3中Model验证 概述 上节我们学习了Model的数据在界面之间的传递,但是很多时候,我们在数据传递的时候为了确保数据的有效性,不得不给Model的相关属性做基本的数据验 ...
- Beginning Python From Novice to Professional (5) - 条件与循环
条件与循环 条件运行: name = raw_input('What is your name? ') if name.endswith('Gumby'): print 'Hello, Mr.Gumb ...
- Asp.Net MVC5入门学习系列⑦
原文:Asp.Net MVC5入门学习系列⑦ 接着上篇结尾所说,如果开发中刚才遇到Model需要添加或者减少字段/属性的话,但是刚好你也利用EF的Code frist通过Model生存的数据库,这时改 ...
- ExcelReport源码解析
ExcelReport第二篇:ExcelReport源码解析 导航 目 录:基于NPOI的报表引擎——ExcelReport 上一篇:使用ExcelReport导出Excel 下一篇:扩展元素 ...
- 实现一个简单的Unity3D三皮卡——3D Picking (1)
3D Picking 其原理是从摄像机位置到空间发射的射线.基于光线碰到物体回暖. 这里我们使用了触摸屏拿起触摸,鼠标选择相同的原理,仅仅是可选API不同. 从unity3D官网Manual里找到下面 ...
- 工作流设计参考(包括PHP实现)
工作流很少有让人满意的,即便是国内用的比较多的jbpm,用起来也会觉得很便扭.再加上PHP中没有什么好用的工作流,于是干脆自己设计一个,设计的原则如下: 1 根据80/20原则,只使用wfmc模型中最 ...
- Redis查看帮助文档
Redis查看帮助文档的方式,目前我用到的主要有两种: 1.访问官方文档: Redis文档 2.在redis-cli中通过命令查看,输入"?"或者"help"回 ...
- 安装SQL Server 2008 - 初学者系列 - 学习者系列文章
本文介绍SQL Server 2008数据库的安装 1.从下列地址获取SQL Server 2008的副本 thunder://QUFlZDJrOi8vfGZpbGV8Y25fc3FsX3NlcnZl ...