深入浅出gRPC
李林锋
《Netty 权威指南》、《分布式服务框架原理与实践》作者。
立即订阅
11236 人已学习
课程目录
已更新 6 讲 / 共 6 讲
01 | gRPC 入门及服务端创建和调用原理
02 | 客户端创建和调用原理
03 | gRPC 线程模型分析
04 | gRPC 服务调用原理
05 | gRPC 安全性设计
06 | gRPC 序列化机制
深入浅出gRPC
登录|注册

03 | gRPC 线程模型分析

李林锋 2018-03-14

RPC 线程模型

1.1 BIO 线程模型

在 JDK 1.4 推出 Java NIO 之前,基于 Java 的所有 Socket 通信都采用了同步阻塞模式(BIO),这种一请求一应答的通信模型简化了上层的应用开发,但是在性能和可靠性方面却存在着巨大的瓶颈。
因此,在很长一段时间里,大型的应用服务器都采用 C 或者 C++ 语言开发,因为它们可以直接使用操作系统提供的异步 I/O 或者 AIO 能力。
当并发访问量增大、响应时间延迟增大之后,采用 Java BIO 开发的服务端软件只有通过硬件的不断扩容来满足高并发和低时延。
它极大地增加了企业的成本,并且随着集群规模的不断膨胀,系统的可维护性也面临巨大的挑战,只能通过采购性能更高的硬件服务器来解决问题,这会导致恶性循环。
传统采用 BIO 的 Java Web 服务器如下所示(典型的如 Tomcat 的 BIO 模式):
采用该线程模型的服务器调度特点如下:
服务端监听线程 Acceptor 负责客户端连接的接入,每当有新的客户端接入,就会创建一个新的 I/O 线程负责处理 Socket;
客户端请求消息的读取和应答的发送,都有 I/O 线程负责;
除了 I/O 读写操作,默认情况下业务的逻辑处理,例如 DB 操作等,也都在 I/O 线程处理;
I/O 操作采用同步阻塞操作,读写没有完成,I/O 线程会同步阻塞。
BIO 线程模型主要存在如下三个问题:
性能问题:一连接一线程模型导致服务端的并发接入数和系统吞吐量受到极大限制;
可靠性问题:由于 I/O 操作采用同步阻塞模式,当网络拥塞或者通信对端处理缓慢会导致 I/O 线程被挂住,阻塞时间无法预测;
可维护性问题:I/O 线程数无法有效控制、资源无法有效共享(多线程并发问题),系统可维护性差。
为了解决同步阻塞 I/O 面临的一个链路需要一个线程处理的问题,通常会对它的线程模型进行优化,后端通过一个线程池来处理多个客户端的请求接入,形成客户端个数 "M" 与线程池最大线程数 "N" 的比例关系,其中 M 可以远远大于 N,通过线程池可以灵活的调配线程资源,设置线程的最大值,防止由于海量并发接入导致线程耗尽,它的工作原理如下所示:
优化之后的 BIO 模型采用了线程池实现,因此避免了为每个请求都创建一个独立线程造成的线程资源耗尽问题。但是由于它底层的通信依然采用同步阻塞模型,阻塞的时间取决于对方 I/O 线程的处理速度和网络 I/O 的传输速度。
本质上来讲,无法保证生产环境的网络状况和对端的应用程序能足够快,如果应用程序依赖对方的处理速度,它的可靠性就非常差,优化之后的 BIO 线程模型仍然无法从根本上解决性能线性扩展问题。

1.2 异步非阻塞线程模型

从 JDK1.0 到 JDK1.3,Java 的 I/O 类库都非常原始,很多 UNIX 网络编程中的概念或者接口在 I/O 类库中都没有体现,例如 Pipe、Channel、Buffer 和 Selector 等。2002 年发布 JDK1.4 时,NIO 以 JSR-51 的身份正式随 JDK 发布。它新增了个 java.nio 包,提供了很多进行异步 I/O 开发的 API 和类库,主要的类和接口如下:
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《深入浅出gRPC》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(8)

  • Ballontt
    什么时候写golang实现的版本。golang是趋势,写出来文章会有很久的生命期

    作者回复: 确实用go的越来越多,这块儿暂时还没有具体计划

    2018-06-02
    5
  • 云学
    我用C++11写了一个异步多线程网络库,思想类似,感兴趣的可以看下 https://github.com/lianzeng/multiThread

    作者回复: 不错,是RPC框架还是个网络通信框架?

    2018-03-30
    1
  • 蔡振盛
    老师老师“一个 NioEventLoop 聚合了一个多路复用器 Selector,因此可以处理成百上千的客户端连接,Netty 的处理策略是每当有一个新的客户端接入” 这一句是否有问题 ,是每个channel绑定一个多路复用器吧?
    2019-01-21
  • 秦东
    我想请问下,selector作用在哪里?在mainReactor还是 subReactor? 各个handler是不是处理每个具体的task? 那总得线程数量会制约task请求的效率?
    最后由selector 讲handler处理的结果返回到客户端,是这样理解吗?
    线程之间是如何做切换的呢
    2019-01-20
  • Yanyuyu
    你好,问一下我多线程向服务器入图片,图片都放在队列里了,让线程消费,40个线程,一个线程一次入5张,是通过GRPC链接的,发现最后总会有几张图片入不进去,结果发现是因为grpc在队列里面一直阻塞了,一直等runnable,结果有一个线程不是runnable,这样就阻塞了,请问有没有解决方案
    2018-09-19
  • landon30
    Hi 林锋 这篇文章有一个疑虑点
    1. 文章说grpc的逻辑线程池是jdk的cached thread pool 可以这个线程池没有队列啊(synchrousqueue)
    2. 到文章有两处
        - 有部分代码显示序列化那个线程池有一个concurrentqueue
        - 线程模型说目前的实现是netty的io线程池和序列化线程池同时对一个队列加锁

    所有些许有点困惑

    作者回复: 好的,我再确认下,你可以加我微信,确实之后给你回复

    2018-04-03
  • mark_II
    有疑惑,什么是写半包?

    作者回复: 就是一次write操作,没有把响应发送完,需要后续继续发,这个就是写半包

    2018-03-27
  • 总指挥
    netty初学者,我想问下老师在讲netty3和4线程模型差异那部分说到:netty4采用串行化设计,这里的串行化是指相同client发送的请求会被同一个nioEventLoop处理吗?

    作者回复: 是的,另外还有一个意思,就是消息的读与写都是在同一个nioevetloop处理,网络相关操作就不存在线程切换了

    2018-03-18
收起评论
8
返回
顶部