资讯专栏INFORMATION COLUMN

从网络IO到Thrift网络模型

马永翠 / 2232人阅读

摘要:基本原理函数监视的文件描述符分类,分别是和。具体模型如下与模式相比,在完成数据读取之后,将业务处理过程交由一个线程池来完成,主线程直接返回进行下一次循环操作,效率大大提升。是提供的最高效的网络模型。

I/O多路复用

IO多路复用就是通过一种机制,一个进程可以监听多个文件描述符,一个某个描述符就绪(一般是读就绪或写就绪),就能够通知程序进行相应的读写操作。select、poll、epoll本质上都是同步IO,因为他们需要在读写事件就绪后自己负责读写,即这个读写过程是阻塞的,而异步IO则无需自己负责读写,异步IO的实现会把数据从内核拷贝到用户空间。

select 基本原理

select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述符就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以通过遍历fdset,来找到就绪的描述符。

select的缺点

单进程所能打开的文件描述符有一定限制,32位机默认1028,64位机默认2048。

对socket进程扫描时是线性扫描,效率很低。

用来存放文件描述符的数据结构,在用户空间和内核空间的复制开销极大。

poll

poll与select类似,略过。

epoll

epoll是在linux 2.6内核中提出的,是select和poll的增强版本。

基本原理

epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd。

epoll的优点

没有最大连接数的限制,1G内存约能监听10W个端口。

不采用轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会回调。

内存拷贝,epoll使用mmap减少复制开销。(注:mmap本质就是绕过从网卡、磁盘拷贝数据到内核再拷贝到用户空间的方式,直接从网卡拷贝数据到用户空间,性能爆炸。)

Thrift网络服务模型

thrift提供的网络服务模型有阻塞服务模型、非阻塞服务模型:

阻塞服务模型:TSimpleServer、TThreadPoolServer

非阻塞服务模型:TNonblockingServer、THsHaServer和TThreadedSelectorServer

TSimpleServer

该模式采用最简单的阻塞IO,一次只能接收并处理一个socket,处理流程如下:

此种模式效率低下,生产不会使用,略过。

TThreadPoolServer

TThreadPoolServer模式采用阻塞socket方式工作,主线程负责阻塞式(划重点,不是select的方式)监听是否有新socket到来,具体的业务处理交由一个线程池来处理。

accept部分的代码如下:

  protected TSocket acceptImpl() throws TTransportException {
    if (serverSocket_ == null) {
      throw new TTransportException(TTransportException.NOT_OPEN, "No underlying server socket.");
    }
    try {
      // 阻塞式监听新的连接
      Socket result = serverSocket_.accept();
      TSocket result2 = new TSocket(result);
      result2.setTimeout(clientTimeout_);
      return result2;
    } catch (IOException iox) {
      throw new TTransportException(iox);
    }
  }

具体模型如下:

TThreadPoolServer本质是One Thread Per Connection模型。模型受限于线程池的最大线程数,在连接数很大话,请求只能排队,对于高并发的场景,此模型并不合适。

TNonblockingServer

TNonblockingServer模式也是单线程工作,但是采用NIO的模式,借助Channel/Selector机制, 采用IO事件模型来处理。本质是一种event-loop模型。

具体模型如下:

event-loop的核心代码如下:

    private void select() {
      try {
        // 等待事件,jdk7之前的版本存在问题,会存在会将CPU打满的情况,没有事件,select却返回,从而将CPU打满;Netty中通过threshold,解决了该问题
        selector.select();

        // 获取IO事件
        Iterator 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);
      }
    }

这个模型一般由一个event dispatcher等待各类事件,待事件发生后原地调用对应的event handler,全部调用完后等待更多事件,故为"loop"。这个模型的实质是把多段逻辑按事件触发顺序交织在一个系统线程中。一个event-loop只能使用一个核,故此类程序要么是IO-bound,要么是每个handler有确定的较短的运行时间(比如http server),否则一个耗时漫长的回调就会卡住整个程序,产生高延时。在实践中这类程序不适合多开发者参与,一个人写了阻塞代码可能就会拖慢其他代码的响应。由于event handler不会同时运行,不太会产生复杂的race condition,一些代码不需要锁。此类程序主要靠部署更多进程增加扩展性。

THsHaServer

THsHaServer继承于TNonblockingServer,引入了线程池提高了任务处理的并发能力。THsHaServer是半同步半异步(Half-Sync/Half-Async)的处理模式,Half-Aysnc用于IO事件处理(Accept/Read/Write),Half-Sync用于业务handler对rpc的同步处理上。

具体模型如下:

THsHaServer与TNonblockingServer模式相比,THsHaServer在完成数据读取之后,将业务处理过程交由一个线程池来完成,主线程直接返回进行下一次循环操作,效率大大提升。

但是,主线程仍然需要处理accpet、read、write时间,当并发量非常大,读取或者发送的数据量比较大时,会将主线程阻塞住,新的连接无法被及时处理。

TThreadedSelectorServer

TThreadedSelectorServer是对THsHaServer的一种改进,它将selector中的read/write事件从主线程中剥离出来。

TThreadedSelectorServer是thrift提供的最高效的网络模型。具体模型如下:

构成如下:

一个Accpet线程,专门用来处理新的socket

n个SelectorThread,用来处理read/write事件,读取、返回数据都是有这些线程完成的,每个SelectorThread会与若干个socket绑定,每个SelectorThread会处理与它绑定socket的read/write事件。

一个负载均衡器SelectorThreadLoadBalancer对象,在accpet线程接收到新的socket以后,由SelectorThreadLoadBalancer决定将socket与哪个SelectorThread绑定(其实就是一个next函数,每分配一个socket,就调用next)。

一个ExecutorService类型的worker线程池,在SelectorThread读取数据之后,将其包装成一个task,分配给worker线程池,处理业务逻辑。

总结:TThreadedSelectorServer模式,其实就是标准的Reactor模式,Tomcat7以后的版本、Cobar、MyCat(分库分表proxy)基本都是这个套路,具体实现略有差异。

原文链接

https://segmentfault.com/a/11...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76986.html

相关文章

  • Thrift 简易入门与实战

    摘要:简介是一个软件框架用来进行可扩展且跨语言的服务的开发它结合了功能强大的软件堆栈和代码生成引擎以构建在这些编程语言间无缝结合的高效的服务官网地址安装的安装比较简单在下可以直接使用快速安装或可以通过官网下载这里就不再多说了当下载安装完毕后我们就 简介 thrift是一个软件框架, 用来进行可扩展且跨语言的服务的开发. 它结合了功能强大的软件堆栈和代码生成引擎, 以构建在 C++, Java...

    iliyaku 评论0 收藏0
  • Thrift架构

    摘要:服务器端使用它来做顶层接口,编写实现类。会自动生成同步调用和异步调用的两个接口。方法参数的封装类,以方法名命名方法返回值的封装类,以方法名命名参考个人博客 基本概念 轻量级、跨语言的RPC框架 功能特点: 基于IDL(接口描述语言)生成跨语言的RPC clients and servers,支持超过20种语言 支持二进制的高性能的编解码框架 支持NIO的底层通信 相对简单的服务调用模...

    wall2flower 评论0 收藏0
  • thrift调用流程分析

    摘要:由于工作需要使用,并且需要根据需求修改源码,因此必须熟悉执行的流程。目前支持的模型包括使用阻塞的单线程服务器,主要用于调试。 由于工作需要使用thrift,并且需要根据需求修改thrift源码,因此必须熟悉thrift执行的流程。以下是根据thrift源码阅读而得出流程分析。 thrift协议栈概述 thrift是一个rpc框架,开发者可以通过thrift自带的接口定义语言(IDL)来...

    nidaye 评论0 收藏0
  • 使用swoole运行thrift服务

    摘要:是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务。它被当作一个远程过程调用框架来使用,是由为大规模跨语言服务开发而开发的。笔者使用的异步服务端提供的接口实现一个了异步协程化的应用。 Swoole扩展简介 Swoole:面向生产环境的 PHP 异步网络通信引擎使 PHP 开发人员可以编写高性能的异步并发 TCP、UDP、Unix Socket、HTTP,WebSock...

    Hancock_Xu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<