Thrift 提供了如图五种模式:TSimpleServer、TNonblockingServer、THsHaServer、TThreadPoolServer、TThreadSelectorServer

​​

TSimpleServer、TThreadPoolServer 属于阻塞模型

TNonblockingServer、THsHaServer、TThreadedSelectorServer 属于非阻塞模型

TServer

TServer 为抽象类

  1. public static class Args extends AbstractServerArgs<Args> {
  2. public Args(TServerTransport transport) {
  3. super(transport);
  4. }
  5. }
  6.  
  7. public static abstract class AbstractServerArgs<T extends AbstractServerArgs<T>> {
  8. final TServerTransport serverTransport;
  9. // 处理层工厂
  10. TProcessorFactory processorFactory;
  11. // 传输层工厂
  12. TTransportFactory inputTransportFactory = new TTransportFactory();
  13. TTransportFactory outputTransportFactory = new TTransportFactory();
  14. // 协议层工厂
  15. TProtocolFactory inputProtocolFactory = new TBinaryProtocol.Factory();
  16. TProtocolFactory outputProtocolFactory = new TBinaryProtocol.Factory();
  17. }

TServer 定义的对外方法

  1. /**
  2. * The run method fires up the server and gets things going.
  3. */
  4. public abstract void serve();
  5. /**
  6. * Stop the server. This is optional on a per-implementation basis. Not
  7. * all servers are required to be cleanly stoppable.
  8. */
  9. public void stop() {}

stop 并不是每个服务都需要优雅的退出,所以没有定义为抽象方法

抽象方法 serve() 由具体的 TServer 实例实现

TSimpleServer

TSimpleServer 实现比较简单,是单线程阻塞模型,只适合测试开发使用

serve 方法源码分析

  1. public void serve() {
  2. // 启动监听 socket
  3. serverTransport.listen();
  4. // 设置服务状态
  5. setServing(true);
  6. // 不断的等待与处理 socket 请求
  7. while(!stopped) {
  8. // accept 一个业务 socket 请求
  9. client = serverTransport_.accept();
  10. if (client != null) {
  11. // 通过工厂获取 server 定义的处理层、传输层和协议层
  12. processor = processorFactory_.getProcessor(client);
  13. inputTransport = inputTransportFactory_.getTransport(client);
  14. outputTransport = outputTransportFactory_.getTransport(client);
  15. inputProtocol = inputProtocolFactory_.getProtocol(inputTransport);
  16. outputProtocol = outputProtocolFactory_.getProtocol(outputTransport);
  17. if (eventHandler_ != null) {
  18. connectionContext = eventHandler_.createContext(inputProtocol, outputProtocol);
  19. }
  20. // 阻塞式处理
  21. while (true) {
  22. // 处理请求事件
  23. if (eventHandler_ != null) {
  24. eventHandler_.processContext(connectionContext, inputTransport, outputTransport);
  25. }
  26. // 如果处理层为异步,则退出
  27. if(!processor.process(inputProtocol, outputProtocol)) {
  28. break;
  29. }
  30. }
  31. }
  32. // 关闭
  33. eventHandler_.deleteContext(connectionContext, inputProtocol, outputProtocol);
  34. inputTransport.close();
  35. outputTransport.close();
  36. setServing(false);
  37. }
  38. }

TSimpleServer 工作图

TThreadPoolServer

ThreadPoolServer 解决了 TSimple 不支持并发和多连接的问题,引入了线程池

与 TSimple 相同,主线程负责阻塞式监听 socket,而剩下的业务处理则全部交由线程池去处理

  1. public void serve() {
  2. // 主线程启动监听 socket
  3. serverTransport_.listen();
  4. // 设置服务状态
  5. stopped_ = false;
  6. setServing(true);
  7. // 等待并处理 socket 请求
  8. while (!stopped_) {
  9. TTransport client = serverTransport_.accept();
  10. // Runnable run 逻辑与 TSimpleServer 类似
  11. WorkerProcess wp = new WorkerProcess(client);
  12. int retryCount = 0;
  13. long remainTimeInMillis = requestTimeoutUnit.toMillis(requestTimeout);
  14. while(true) {
  15. // 交由线程池来处理
  16. executorService_.execute(wp);
  17. break;
  18. }
  19. }
  20. executorService_.shutdown();
  21. setServing(false);
  22. }

TThreadPoolServer 的缺点:

处理能力的好坏受限于线程池的设置

TNoblockingServer

TNoblockingServer 是单线程工作,但该模式采用了 NIO,所有的 socket 被注册到 selector 中,通过一个线程循环 selector 来监控所有 socket,当有就绪的 socket 时,根据不同的请求做不同的动作(读取、写入数据或 accept 新连接)

TNoblockingServer 的 serve 方法在其父类 AbstractNonblockingServer 中定义

  1. /**
  2. * Begin accepting connections and processing invocations.
  3. * 开始接受并处理调用
  4. */
  5. public void serve() {
  6. // start any IO threads
  7. // 注册一些监听 socket 的线程到 selector 中
  8. if (!startThreads()) {
  9. return;
  10. }
  11. // start listening, or exit
  12. // 开始监听
  13. if (!startListening()) {
  14. return;
  15. }
  16. // 设置服务状态
  17. setServing(true);
  18. // this will block while we serve
  19. // TNonblocking 中实现为 selectAcceptThread_.join();
  20. // 主线程等待 selectAcceptThread 执行完毕
  21. // SelectAcceptThread 的 run 方法为 select();
  22. // 取出一个就绪的 socket
  23. waitForShutdown();
  24.  
  25. setServing(false);
  26.  
  27. // do a little cleanup
  28. stopListening();
  29. }
  30.  
  31. // SelectAcceptThread run 方法
  32. public void run() {
  33. while (!stopped_) {
  34. select();
  35. processInterestChanges();
  36. }
  37. for (SelectionKey selectionKey : selector.keys()) {
  38. cleanupSelectionKey(selectionKey);
  39. }
  40. }
  41.  
  42. // SelectAcceptThread Select 过程
  43. private void select() {
  44. try {
  45. // wait for io events.
  46. // NIO 取出一个
  47. selector.select();
  48. // process the io events we received
  49. Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
  50. // 遍历就绪的 socket
  51. while (!stopped_ && selectedKeys.hasNext()) {
  52. SelectionKey key = selectedKeys.next();
  53. selectedKeys.remove();
  54. // if the key is marked Accept, then it has to be the server
  55. // transport.
  56. // accept 新 socket 并将其注册到 selector 中
  57. if (key.isAcceptable()) {
  58. handleAccept();
  59. } else if (key.isReadable()) {
  60. // deal with reads
  61. // 处理读数据的 socket 请求
  62. handleRead(key);
  63. } else if (key.isWritable()) {
  64. // deal with writes
  65. // 处理写数据的 socket 请求
  66. handleWrite(key);
  67. } else {
  68. LOGGER.warn("Unexpected state in select! " + key.interestOps());
  69. }
  70. }
  71. } catch (IOException e) {
  72. LOGGER.warn("Got an IOException while selecting!", e);
  73. }
  74. }
  75.  
  76. // 接收新的连接
  77. private void handleAccept() throws IOException {
  78. SelectionKey clientKey = null;
  79. TNonblockingTransport client = null;
  80. // accept the connection
  81. client = (TNonblockingTransport)serverTransport.accept();
  82. // 注册到 selector 中
  83. clientKey = client.registerSelector(selector, SelectionKey.OP_READ);
  84. // add this key to the map
  85. FrameBuffer frameBuffer = createFrameBuffer(client, clientKey, SelectAcceptThread.this);
  86. clientKey.attach(frameBuffer);
  87. }

TNonblockingServer 模式的缺点:

其还是采用单线程顺序来完成,当业务处理比较复杂耗时,该模式的效率将会下降

TNonblockingServer 工作图:

THsHaServer

THsHaServer 是 TNoblockingServer 的子类,处理逻辑基本相同,不同的是,在处理读取请求时,THsHaServer 将处理过程交由线程池来完成,主线程直接返回进行下一次循环,提高了效率

THsHaServer 模式的缺点:

其主线程需要完成对所有 socket 的监听一级数据的写操作,当大请求量时,效率较低

TThreadedSelectorServer

TThreadedSelectorServer 是 Thrift 目前提供的最高级模式,生产环境的首选,其对 TNonblockingServer 进行了扩展

TThreadedSelectorServer 源码中一些关键的属性

  1. public static class Args extends AbstractNonblockingServerArgs<Args> {
  2. // 在已接收的连接中选择线程的个数
  3. public int selectorThreads = 2;
  4. // 执行线程池 ExecutorService 的线程个数
  5. private int workerThreads = 5;
  6. // 执行请求具体任务的线程池
  7. private ExecutorService executorService = null;
  8. }
  9. // The thread handling all accepts
  10. private AcceptThread acceptThread;
  11. // Threads handling events on client transports
  12. private final Set<SelectorThread> selectorThreads = new HashSet<SelectorThread>();
  13. // This wraps all the functionality of queueing and thread pool management
  14. // for the passing of Invocations from the selector thread(s) to the workers
  15. // (if any).
  16. private final ExecutorService invoker;
  17. /**
  18. * 循环模式的负载均衡器,用于为新的连接选择 SelectorThread
  19. */
  20. protected static class SelectorThreadLoadBalancer {}
  1. AcceptThread 线程对象,用于监听 socket 的新连接

  2. 多个 SelectorThread 线程对象,用于处理 socket 的读写操作

  3. 一个负载均衡对象 SelectorThreadLoadBalancer,用于决定将 AcceptThread 接收到的 socket 请求分配给哪个 SelectorThread 线程

  4. SelectorThread 线程执行过读写操作后,通过 ExecutorService 线程池来完成此次调用的具体执行

SelectorThread 对象源码解析

  1. /**
  2. * 多个 SelectorThread 负责处理 socket 的 I/O 操作
  3. */
  4. protected class SelectorThread extends AbstractSelectThread {
  5. /**
  6. * The work loop. Handles selecting (read/write IO), dispatching, and
  7. * managing the selection preferences of all existing connections.
  8. * 选择(处理 socket 的网络读写 IO),分配和管理现有连接
  9. */
  10. public void run() {
  11. while (!stopped_) {
  12. select();
  13. }
  14. }
  15. private void select() {
  16. // process the io events we received
  17. Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
  18. while (!stopped_ && selectedKeys.hasNext()) {
  19. SelectionKey key = selectedKeys.next();
  20. selectedKeys.remove();
  21. // skip if not valid
  22. if (!key.isValid()) {
  23. cleanupSelectionKey(key);
  24. continue;
  25. }
  26. if (key.isReadable()) {
  27. // deal with reads
  28. handleRead(key);
  29. } else if (key.isWritable()) {
  30. // deal with writes
  31. handleWrite(key);
  32. } else {
  33. LOGGER.warn("Unexpected state in select! " + key.interestOps());
  34. }
  35. }
  36. }
  37. }

AcceptThread 对象源码解析

  1. /**
  2. * 在服务器传输中选择线程(监听 socket 请求)并向 IO 选择器(SelectorThread)提供新连接
  3. */
  4. protected class AcceptThread extends Thread {
  5. // The listen socket to accept on
  6. private final TNonblockingServerTransport serverTransport;
  7. private final Selector acceptSelector;
  8. // 负载均衡器,决定将连接分配给哪个 SelectorThread
  9. private final SelectorThreadLoadBalancer threadChooser;
  10. public void run() {
  11. while (!stopped_) {
  12. select();
  13. }
  14. }
  15. private void select() {
  16. // process the io events we received
  17. Iterator<SelectionKey> selectedKeys = acceptSelector.selectedKeys().iterator();
  18. while (!stopped_ && selectedKeys.hasNext()) {
  19. SelectionKey key = selectedKeys.next();
  20. selectedKeys.remove();
  21. // 处理接收的新情求
  22. if (key.isAcceptable()) {
  23. handleAccept();
  24. } else {
  25. LOGGER.warn("Unexpected state in select! " + key.interestOps());
  26. }
  27. }
  28. }
  29. /**
  30. * Accept a new connection.
  31. */
  32. private void handleAccept() {
  33. final TNonblockingTransport client = doAccept();
  34. if (client != null) {
  35. // 从负载均衡器中,获取 SelectorThread 线程
  36. final SelectorThread targetThread = threadChooser.nextThread();
  37. if (args.acceptPolicy == Args.AcceptPolicy.FAST_ACCEPT || invoker == null) {
  38. doAddAccept(targetThread, client);
  39. } else {
  40. // FAIR_ACCEPT
  41. invoker.submit(new Runnable() {
  42. public void run() {
  43. // 将选择到的线程和连接放入 线程池 处理
  44. // 用 targetThread 线程取处理一个给接受的链接 client,如果新连接的队列处于满的状态,则将处于阻塞状态
  45. doAddAccept(targetThread, client);
  46. }
  47. });
  48. }
  49. }
  50. }
  51. private TNonblockingTransport doAccept() {
  52. return (TNonblockingTransport) serverTransport.accept();
  53. }
  54. // 用 targetThread 线程取处理一个给接受的链接 client,如果新连接的队列处于满的状态,则将处于阻塞状态
  55. private void doAddAccept(SelectorThread thread, TNonblockingTransport client) {
  56. if (!thread.addAcceptedConnection(client)) {
  57. client.close();
  58. }
  59. }
  60. }

TThreadedSelectorServer 工作图

参考资料

Thrift源码学习二——Server层的更多相关文章

  1. [阿里DIN] 从论文源码学习 之 embedding层如何自动更新

    [阿里DIN] 从论文源码学习 之 embedding层如何自动更新 目录 [阿里DIN] 从论文源码学习 之 embedding层如何自动更新 0x00 摘要 0x01 DIN源码 1.1 问题 1 ...

  2. Dubbo源码学习(二)

    @Adaptive注解 在上一篇ExtensionLoader的博客中记录了,有两种扩展点,一种是普通的扩展实现,另一种就是自适应的扩展点,即@Adaptive注解的实现类. @Documented ...

  3. python 协程库gevent学习--gevent源码学习(二)

    在进行gevent源码学习一分析之后,我还对两个比较核心的问题抱有疑问: 1. gevent.Greenlet.join()以及他的list版本joinall()的原理和使用. 2. 关于在使用mon ...

  4. Vue源码学习二 ———— Vue原型对象包装

    Vue原型对象的包装 在Vue官网直接通过 script 标签导入的 Vue包是 umd模块的形式.在使用前都通过 new Vue({}).记录一下 Vue构造函数的包装. 在 src/core/in ...

  5. 以太坊 layer2: optimism 源码学习(二) 提现原理

    作者:林冠宏 / 指尖下的幽灵.转载者,请: 务必标明出处. 掘金:https://juejin.im/user/1785262612681997 博客:http://www.cnblogs.com/ ...

  6. Thrift 源码学习一——源码结构

    Thrift 客户端与服务端的交互图 源码结构 传输层 TTransport: TTransport:客户端传输层抽象基础类,read.write.flush.close 等方法 TSocket 与 ...

  7. [spring源码学习]二、IOC源码——配置文件读取

    一.环境准备 对于学习源码来讲,拿到一大堆的代码,脑袋里肯定是嗡嗡的,所以从代码实例进行跟踪调试未尝不是一种好的办法,此处,我们准备了一个小例子: package com.zjl; public cl ...

  8. SocketServer源码学习(二)

    SocketServer 中非常重要的两个基类就是:BaseServer 和 BaseRequestHandler在SocketServer 中也提供了对TCP以及UDP的高级封装,这次我们主要通过分 ...

  9. mybatis源码学习(二)--mybatis+spring源码学习

    这篇笔记主要来就,mybatis是如何利用spring的扩展点来实现和spring的整合 1.mybatis和spring整合之后,我们就不需要使用sqlSession.selectOne()这种方式 ...

随机推荐

  1. 在vue项目中使用canvas-nest.js,报parameter 1 is not of type 'Element'

    canvas-nest.js是一款轻量的网页特效,如图: github地址:https://github.com/hustcc/canvas-nest.js 在普通的html项目中,只要将<sc ...

  2. 第01章 准备工作.md

    第1章 准备工作 1.1 本书的内容 本书讲的是利用Python进行数据控制.处理.整理.分析等方面的具体细节和基本要点.我的目标是介绍Python编程和用于数据处理的库和工具环境,掌握这些,可以让你 ...

  3. android monitor 汉化 ddms

    作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@qq.com E-mail: 313134555 @qq.com android.jar\com\androi ...

  4. bzoj 4767: 两双手 组合 容斥

    题目链接 bzoj4767: 两双手 题解 不共线向量构成一组基底 对于每个点\((X,Y)\)构成的向量拆分 也就是对于方程组 $Ax * x + Bx * y = X $ \(Ay * x + B ...

  5. Python解释数学系列——分位数Quantile

    跳转到我的博客 1. 分位数计算案例与Python代码 案例1 Ex1: Given a data = [6, 47, 49, 15, 42, 41, 7, 39, 43, 40, 36],求Q1, ...

  6. Problem C: 找气球

    Description zstu集训队经常举办月赛,但是气球经常不够.现有多个桶,每个桶有一种颜色,每个桶可能对应多个题,给定每个题对应的桶,打比赛的时候,经常某道题被发现是水题,但是该颜色的气球没有 ...

  7. JS 显示周 几和 月 日

    function getMyDay(date){ var week; ; var day = date.getDate(); ) week="周日" ) week="周一 ...

  8. 在eclipse总是会运行之前的错误项目

    我改了context root没有用,删了work下面的项目文件也没有用,最后必须要改server的配置,就是在windows下有个server runtimeEnvironment http://b ...

  9. [TenserFlow学习笔记]——安装

    最近人工智能.深度学习.机器学习等词汇很是热闹,所以想进一步学习一下.不一定吃这口饭,但多了解一下没有坏处.接下来将学习到的一些知识点做一下记录. 1.安装环境 在VMWare虚拟机中安装最新版本的U ...

  10. cocos3.x - lua vs2013环境搭建及项目创建示例

    第一步:装vs2013vs 2013下载(没试过) 安装颜色设深色(不伤眼睛),只装c++够用了 第二步:装cocos环境 (如果不打包只为了解引擎之类的话,只装cocos就可以了(就可以了,jdk, ...