前言:
  Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 不过Thrift的实现, 简单使用离实际生产环境还是有一定距离, 本系列将对Thrift作代码解读和框架扩充, 使得它更加贴近生产环境. 本文主要讲解Thrift的高性能网络框架模型, 讲解各种网络模型的特点和区别.

Thrift 高性能网络服务模型
1). TServer类层次体系

TSimpleServer/TThreadPoolServer是阻塞服务模型
TNonblockingServer/THsHaServer/TThreadedSelectotServer是非阻塞服务模型(NIO)

2). TServer抽象类的定义
内部静态类Args的定义, 用于TServer类用于串联软件栈(传输层, 协议层, 处理层)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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类定义的抽象类

1
2
3
4
5
6
7
public abstract class TServer {
  public abstract void serve();
  public void stop();
 
  public boolean isServing();
  public void setServerEventHandler(TServerEventHandler eventHandler);
}

评注:

  抽象函数serve由具体的TServer实例来实现, 而并非所有的服务都需要优雅的退出, 因此stop没有被定义为抽象

3). TSimpleServer
TSimpleServer实现, 正如其名Simple, 其实现非常的简单, 是个单线程阻塞模型, 只适合测试开发使用
抽象的代码可简单描述如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// *) server socket进行监听
serverSocket.listen();
while ( isServing() ) {
  // *) 接受socket链接
  client = serverSocket.accept();
  // *) 封装处理器
  processor = factory.getProcess(client);
  while ( true ) {
    // *) 阻塞处理rpc的输入/输出
    if ( !processor.process(input, output) ) {
      break;   
    }  
  }
}

4). ThreadPoolServer
ThreadPoolServer解决了TSimple不支持并发和多连接的问题, 引入了线程池. 实现的模型是One Thread Per Connection

线程池代码片段:

1
2
3
4
5
6
7
8
9
private static ExecutorService createDefaultExecutorService(Args args) {
  SynchronousQueue<Runnable> executorQueue =
    new SynchronousQueue<Runnable>();
  return new ThreadPoolExecutor(args.minWorkerThreads,
                                args.maxWorkerThreads,
                                60,
                                TimeUnit.SECONDS,
                                executorQueue);
}

评注:
  采用同步队列(SynchronousQueue), 线程池采用能线程数可伸缩的模式.
主线程循环

1
2
3
4
5
6
7
8
9
setServing(true);
while (!stopped_) {
  try {
    TTransport client = serverTransport_.accept();
    WorkerProcess wp = new WorkerProcess(client);
    executorService_.execute(wp);
  } catch (TTransportException ttx) {
  }
}

评注: 
  拆分了监听线程(accept)和处理客户端连接的工作线程(worker), 监听线程每接到一个客户端, 就投给线程池去处理. 这种模型能提高并发度, 但并发数取决于线程数, IO依旧阻塞, 从而限制该服务的服务能力.

5). TNonblockingServer
TNonblockingServer采用NIO的模式, 借助Channel/Selector机制, 采用IO事件模型来处理.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void select() {
  try {
    selector.select();  // wait for io events.
    // process the io events we received
    Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
    while (!stopped_ && selectedKeys.hasNext()) {
      SelectionKey key = selectedKeys.next();
      selectedKeys.remove();
      if (key.isAcceptable()) {
        handleAccept(); // deal with accept
      } else if (key.isReadable()) {
        handleRead(key);    // deal with reads
      } else if (key.isWritable()) {
        handleWrite(key); // deal with writes
      }
    }
  } catch (IOException e) {
  }
}

评注:

  select代码里对accept/read/write等IO事件进行监控和处理, 唯一可惜的这个单线程处理. 当遇到handler里有阻塞的操作时, 会导致整个服务被阻塞住.

6). THsHaServer
鉴于TNonblockingServer的缺点, THsHa引入了线程池去处理, 其模型把读写任务放到线程池去处理.
HsHa是: Half-sync/Half-async的处理模式, Half-aysnc是在处理IO事件上(accept/read/write io), Half-sync用于handler对rpc的同步处理上.

7). TThreadedSelectorServer
TThreadedSelectorServer是最成熟,也是被业界所推崇的RPC服务模型
TThreadedSelectorServer是对以上NonblockingServer的扩充, 其分离了Accept和Read/Write的Selector线程, 同时引入Worker工作线程池. 它也是种Half-sync/Half-async的服务模型.

总结:

  MainReactor就是Accept线程, 用于监听客户端连接, SubReactor采用IO事件线程(多个), 主要负责对所有客户端的IO读写事件进行处理. 而Worker工作线程主要用于处理每个rpc请求的handler回调处理(这部分是同步的).

问题:

  这边提几个小小的问题, 考考读者?
  1). Java NIO中 Selector采用什么方式实现? c++中的select/poll/epool? 如果是epool的话, 采用的是水平触发,还是边缘触发?
  2). 这边非阻塞模型是HsHa, 有没有全异步的模式? 为何通用的模型是采用TThreadedSelectorServer这种模式呢?
  期待你的回答, 也敬请关注后续的文章.

Thrift 个人实战--Thrift 网络服务模型(转)的更多相关文章

  1. Thrift 个人实战--Thrift 网络服务模型

    前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 不过Thrift的实现, 简单使用离实际生产环境还 ...

  2. Thrift 个人实战--Thrift 的序列化机制

    前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 不过Thrift的实现, 简单使用离实际生产环境还 ...

  3. Thrift 个人实战--Thrift 服务化 Client的改造

    前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 不过Thrift的实现, 简单使用离实际生产环境还 ...

  4. Thrift 个人实战--Thrift RPC服务框架日志的优化

    前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 不过Thrift的实现, 简单使用离实际生产环境还 ...

  5. Thrift RPC实战(三) thrift序列化揭秘

    本文主要讲解Thrift的序列化机制, 看看thrift作为数据交换格式是如何工作的? 1.构造应用场景: 1). 首先我们先来定义下thrift的简单结构. 1 2 3 4 5 namespace ...

  6. Thrift RPC实战(一).初次体验Thrift

    1.前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码,主要特点: 开发速度快: 通过编写RPC接口ID ...

  7. Thrift入门初探--thrift安装及java入门实例

    什么是thrift? 简单来说,是Facebook公布的一款开源跨语言的RPC框架. 那么问题来了. 什么是RPC框架? RPC全称为Remote Procedure Call,意为远程过程调用. 假 ...

  8. 【thrift】thrift入门初探--thrift安装及java入门实例

    转载:https://www.cnblogs.com/fingerboy/p/6424248.html 公司的一些平台服务框架底层封装了thrift提供服务,最近项目不是很紧,于是研究了一下,刚刚入门 ...

  9. Thrift架构~动态Thrift插件的注入

    先说AOP 说到注入,大家就会想起来IoC和AOP,确实如些,这一讲中,我们通过unity来实现对thrift插件的动态注入,事实上,这个功能在以后的项目中经常要用到,比如,你将一些功能分发到指定服务 ...

随机推荐

  1. win10 WiFi 密码查询 命令

    如果你笔记本有WiFi,正好你系统又是Win10 那么... netsh wlan show profile * key=clear

  2. Spring Boot 启动 Struts 报冲突

    错误信息如下: Caused by: com.opensymphony.xwork2.config.ConfigurationException: Bean type class com.opensy ...

  3. Prime Gift CodeForces - 912E (中途相遇)

    链接 大意:求素因子只含给定素数的第k大数 先二分答案转为判定x是第几大, 然后分两块合并即可, 按奇偶分块可以优化一下常数 #include <iostream> #include &l ...

  4. git 添加tag

    前言 什么是tag?tag是节点的意思,一般在上线的时候使用.比如说:你在本地做了好几个功能,然后把这些功能提交到了上线的分支上,某个时刻,你想上线你的新功能,这个时候你需要你个tag来标记一下,告诉 ...

  5. 关于vue Unexpected identifier 问题

    vue对于es6虽然自带babel转换 但是在index.html文件中并不会发生转换 因此在index.html中使用新的语法会导致低版本浏览器不识别代码因此报出Unexpected identif ...

  6. curl常用功能

    <?php //创建一个新cURL资源 $ch = curl_init(); //******************************************************** ...

  7. css中 font常用的样式属性

    今天我总结一下文本常用的字体样式 1.font常用样式 1)字体类型 语法:font-family: +字体类型:    如: font-family:宋体; 2)字体大小 语法:font-size: ...

  8. storage路径问题

    1 概念总述 android开发中,关于存储路径,我们经常听到以下几个概念:内存.内部存储和外部存储,现在我们就来详细说说这三者区别与联系. 内存:英文中记为memory,即RAM 内部存储:英文记为 ...

  9. weblogic下更改jsp不生效的解决办法

    1.删除user_projects\domains\base_domain(我自己的域名)\servers\AdminServer目录下的所有文件夹 一下为转载from:http://blog.itp ...

  10. 如何获取选定部分的HTML

    其实这个程序很简单,首先是如何获取选定部分的HTML,还记得这段吗: HTMLDoc := WebBrowser1.Document as IHTMLDocument2;  SelectRange : ...