为什么建议 Netty 的 I/O 线程与业务线程分离

问题背景:

某互联网同学咨询一个Netty使用问题:最近在研究公司内部的RPC框架,发现底层通信框架使用的是Netty,而且Netty的I/O线程与处理业务的线程分离。具体如下:

1、负责服务端监听的是Accept NioEventLoopGroup线程组

2、负责链路读写操作的是Work NioEventLoopGroup线程组

3、消息解码完成之后,投递到后端的一个业务线程池中处理,线程池使用的是JDK自带的线程池

该同学的疑问:为什么业务的处理不能放到Work NioEventLoopGroup中?

1、如果业务线程处理比较慢,即便I/O线程处理再快,业务端到端响应还是不会缩短

2、I/O线程到业务线程存在线程上下文切换,增加了额外的开销

想法:

构造一个线程数较大(例如1024)的NioEventLoopGroup,同时处理链路的读写和业务处理。即业务处理和消息读写统一使用Netty的I/O线程池(实质自定义的线程组)。


问题答复

Netty I/O线程和业务处理线程分离原因:

1、充分利用多核的并行处理能力:I/O线程和业务线程分离,双方可以并行的处理网络I/O和业务逻辑,充分利用多核的并行计算能力,提升性能。

2、故障隔离:后端的业务线程池处理各种类型的业务消息,有些是I/O密集型、有些是CPU密集型、有些是纯内存计算型,不同的业务处理时延,以及发生故障的概率都是不同的。如果把业务线程和I/O线程合并,就会存在如下问题:

1)某类业务处理较慢,阻塞I/O线程,导致其它处理较快的业务消息的响应无法及时发送出去。

2)即便是同类业务,如果使用同一个I/O线程同时处理业务逻辑和I/O读写,如果请求消息的业务逻辑处理较慢,同样会导致响应消息无法及时发送出去。

3、可维护性:I/O线程和业务线程分离之后,双方职责单一,有利于代码维护和问题定位。如果合设在一起,当RPC调用时延增大之后,到底是网络问题、还是I/O线程问题、还是业务逻辑问题导致的时延大,纠缠在一起,问题定位难度非常大。例如业务线程中访问缓存或者数据库偶尔时延增大,就会导致I/O线程被阻塞,时延出现毛刺,这些时延毛刺的定位,难度非常大。

4、资源代价:NioEventLoopGroup的创建并不是廉价的,它会聚合Selector,Selector本身就会消耗句柄资源。

Netty的NioEventLoop设计理念就是通过有限的I/O线程,通过多路复用和非阻塞的方式,一个线程同时处理成百上千个链路,来解决传统一连接一线程的同步阻塞模型。

因此,它的创建成本也较高,一个进程中不宜创建过多NioEventLoop。

相关代码如下所示:

5、线程切换的代价:如果不是追求极致的性能,线程切换只要不过于频繁,它的代价还是可以接受的。在一个复杂的系统中,当你集成第三方SDK时,例如Redis Client,通常都包含着隐式的线程切换。一些场景下,为了实现系统的高可用性,例如 基于Hystrix做故障隔离,同样会存在线程切换:Hystrix基于线程做故障隔离


该问题引申的其它几个问题

1、Netty的I/O线程 NioEventLoop是否可以处理非I/O任务?

答案是肯定的,通过它提供的接口就可以看出这点:

它的execute方法参数是Runnable,与JDK的线程池execute方法是等价的(异常处理策略存在差异)。

开了这个口子之后,就会存在风险,例如用户为了简化线程处理模型,把所有的业务任务封装成Task,丢到Nett用的I/O线程NioEventLoop中执行。为了防止过多的业务任务阻塞I/O线程的网络读写操作,NioEventLoop提供了设置I/O任务和非I/O任务的处理比例,通过合理的调整处理比例,来保证更合理的资源调度。

Netty并不反对在I/O线程中处理非I/O任务,而是需要用户必须要避免意外的I/O线程阻塞,以及过多的占用I/O任务调度,导致网络I/O处理性能下降。

2、一个超大的JDK业务线程池是不合适的,原因有两个:

1)性能问题:JDK线程池默认采用一个阻塞队列,N个work线程的模式,随着work线程数的增加、队列的争用会非常激烈,进而导致性能下降。

建议采用N组线程池,每个线程池线程数尽量少的方式增加并行处理能力,

减少锁争用。

2)故障隔离问题:如果后端只有一个线程池,某个服务故障将会导致整个进程不可用。采用分组处理业务服务的方式,可以降低故障的影响范围,示例如下所示:



原文发布于微信公众号 - 芋道源码(YunaiV)

原文发表时间:2018-10-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java架构沉思录

基于Netty的百万级推送服务设计要点

最近很多从事移动互联网和物联网开发的同学给我发邮件或者微博私信我,咨询推送服务相关的问题。问题五花八门,在帮助大家答疑解惑的过程中,我也对问题进行了总结,大概可...

2272
来自专栏Dawnzhang的开发者手册

Maven 那点事儿(转)

毋庸置疑,Jason 也是一个秃顶。James Gosling、Rod Johnson、Gavin King,你们可以告诉我为什么吗?

1752
来自专栏应用案例

性能测试之gatling详解

大家接触过形形色色的压力测试工具,例如lr,jmeter各有各的优点,那么最近在做接口测试中涉及到压力测试,小弟就看到一个好用的工具俗称“加特林”英文Gatli...

3446
来自专栏高性能服务器开发

(八)高性能服务器架构设计总结1——以flamigo服务器代码为例

这篇文章算是对这个系列的一个系统性地总结。我们将介绍服务器的开发,并从多个方面探究如何开发一款高性能高并发的服务器程序。 所谓高性能就是服务器能流畅地处理各个客...

4306
来自专栏chenssy

好 RESTful API 的设计原则

做出一个好的API设计很难。API表达的是你的数据和你的数据使用者之间的契约。打破这个契约将会招致很多愤怒的邮件,和一大堆伤心的用户-因为他们手机上的App不工...

1072
来自专栏大闲人柴毛毛

缓存世界中的三大问题及解决方案

目前的IO设备远不能满足互联网应用海量的读写请求。于是便出现了缓存,利用内存的高速读写性能来应付海量的查询请求。然而内存资源非常宝贵,将全量数据存储在内存中显...

3035
来自专栏jouypub

十分钟检查Linux服务器性能

你是否遇到过:服务器负载飙升;服务被已经挂起,接口长时间没响应;服务刚重启,过一会又无法访问等等。这时下面这几条命令就可以尽快的帮你快速定位问题,找出问题的根源

2461
来自专栏后端技术探索

解决nginx负载均衡的session共享问题

查了一些资料,看了一些别人写的文档,总结如下,实现nginx session的共享

964
来自专栏数据和云

数据库高可用和分区解决方案-MongoDB 篇

许春植(Luocs) (阿里巴巴高级数据库管理员,7年以上数据库运维管理经验,擅长MySQL、Oracle及MongoDB数据库,目前主要研究并建设Mongo...

7276
来自专栏华章科技

玩大数据一定用得到的18款Java开源Web爬虫

网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用...

2253

扫码关注云+社区