聊天软件开发之Netty3.x VS Netty4.x的线程模型总结

本文详细对比了Netty 3.x和4.x的线程模型,Netty 4.x采用串行化设计理念,统一由I/O线程处理Inbound和Outbound操作,降低开发难度并提升性能。Netty 3.x的Inbound和Outbound线程模型不统一,可能导致线程安全问题。虽然在特定场景下Netty 3.x可能有性能优势,但Netty 4.x的整体性能更优且更易用。

Netty 3.X的I/O操作线程模型比较复杂,它的处理模型包括两部分:

    Inbound:主要包括链路建立事件、链路激活事件、读事件、I/O异常事件、链路关闭事件等;
    Outbound:主要包括写事件、连接事件、监听绑定事件、刷新事件等。

Inbound操作的主要处理流程如下:

    I/O线程(Work线程)将消息从TCP缓冲区读取到SocketChannel的接收缓冲区中;
    由I/O线程负责生成相应的事件,触发事件向上执行,调度到ChannelPipeline中;
    I/O线程调度执行ChannelPipeline中Handler链的对应方法,直到业务实现的Last Handler;
    Last Handler将消息封装成Runnable,放入到业务线程池中执行,I/O线程返回,继续读/写等I/O操作;
    业务线程池从任务队列中弹出消息,并发执行业务逻辑。


通过对Netty 3的Inbound操作进行分析我们可以看出,Inbound的Handler都是由Netty的I/O Work线程负责执行。

Outbound操作的主要处理流程如下:

    业务线程发起Channel Write操作,发送消息;
    Netty将写操作封装成写事件,触发事件向下传播;
    写事件被调度到ChannelPipeline中,由业务线程按照Handler Chain串行调用支持Downstream事件的Channel Handler;
    执行到系统最后一个ChannelHandler,将编码后的消息Push到发送队列中,业务线程返回;
    Netty的I/O线程从发送消息队列中取出消息,调用SocketChannel的write方法进行消息发送。

Netty 4.x 线程模型

相比于Netty 3.X系列版本,Netty 4.X的I/O操作线程模型比较简答。

Outbound操作的主要处理流程如下:

    I/O线程NioEventLoop从SocketChannel中读取数据报,将ByteBuf投递到ChannelPipeline,触发ChannelRead事件;
    I/O线程NioEventLoop调用ChannelHandler链,直到将消息投递到业务线程,然后I/O线程返回,继续后续的读写操作;
    业务线程调用ChannelHandlerContext.write(Object msg)方法进行消息发送;
    如果是由业务线程发起的写操作,ChannelHandlerInvoker将发送消息封装成Task,放入到I/O线程NioEventLoop的任务队列中,由NioEventLoop在循环中统一调度和执行。放入任务队列之后,业务线程返回;
    I/O线程NioEventLoop调用ChannelHandler链,进行消息发送,处理Outbound事件,直到将消息放入发送队列,然后唤醒Selector,进而执行写操作。


通过流程分析,我们发现Netty 4修改了线程模型,无论是Inbound还是Outbound操作,统一由I/O线程NioEventLoop调度执行。即时通讯聊天软件app开发可以加蔚可云的v:weikeyun24咨询

线程模型对比

在进行新老版本线程模型PK之前,首先还是要熟悉下串行化设计的理念:

我们知道当系统在运行过程中,如果频繁的进行线程上下文切换,会带来额外的性能损耗。多线程并发执行某个业务流程,业务开发者还需要时刻对线程安全保持警惕,哪些数据可能会被并发修改,如何保护?这不仅降低了开发效率,也会带来额外的性能损耗。

为了解决上述问题,Netty 4采用了串行化设计理念,从消息的读取、编码以及后续Handler的执行,始终都由I/O线程NioEventLoop负责,这就意外着整个流程不会进行线程上下文的切换,数据也不会面临被并发修改的风险,对于用户而言,甚至不需要了解Netty的线程细节,这确实是个非常好的设计理念。

一个NioEventLoop聚合了一个多路复用器Selector,因此可以处理成百上千的客户端连接,Netty的处理策略是每当有一个新的客户端接入,则从NioEventLoop线程组中顺序获取一个可用的NioEventLoop,当到达数组上限之后,重新返回到0,通过这种方式,可以基本保证各个NioEventLoop的负载均衡。一个客户端连接只注册到一个NioEventLoop上,这样就避免了多个I/O线程去并发操作它。

Netty通过串行化设计理念降低了用户的开发难度,提升了处理性能。利用线程组实现了多个串行化线程水平并行执行,线程之间并没有交集,这样既可以充分利用多核提升并行处理能力,同时避免了线程上下文的切换和并发保护带来的额外性能损耗。

了解完了Netty 4的串行化设计理念之后,我们继续看Netty 3线程模型存在的问题,总结起来,它的主要问题如下:

    Inbound和Outbound实质都是I/O相关的操作,它们的线程模型竟然不统一,这给用户带来了更多的学习和使用成本;
    Outbound操作由业务线程执行,通常业务会使用线程池并行处理业务消息,这就意味着在某一个时刻会有多个业务线程同时操作ChannelHandler,我们需要对ChannelHandler进行并发保护,通常需要加锁。如果同步块的范围不当,可能会导致严重的性能瓶颈,这对开发者的技能要求非常高,降低了开发效率;
    Outbound操作过程中,例如消息编码异常,会产生Exception,它会被转换成Inbound的Exception并通知到ChannelPipeline,这就意味着业务线程发起了Inbound操作!它打破了Inbound操作由I/O线程操作的模型,如果开发者按照Inbound操作只会由一个I/O线程执行的约束进行设计,则会发生线程并发访问安全问题。由于该场景只在特定异常时发生,因此错误非常隐蔽!一旦在生产环境中发生此类线程并发问题,定位难度和成本都非常大。


讲了这么多,似乎Netty 4 完胜 Netty 3的线程模型,其实并不尽然。在特定的场景下,Netty 3的性能可能更高,就如本文第4章节所讲,如果编码和其它Outbound操作非常耗时,由多个业务线程并发执行,性能肯定高于单个NioEventLoop线程。

但是,这种性能优势不是不可逆转的,如果我们修改业务代码,将耗时的Handler操作前置,Outbound操作不做复杂业务逻辑处理,性能同样不输于Netty 3,但是考虑内存池优化、不会反复创建Event、不需要对Handler加锁等Netty 4的优化,整体性能Netty 4版本肯定会更高。

总而言之,如果用户真正熟悉并掌握了Netty 4的线程模型和功能类库,相信不仅仅开发会更加简单,性能也会更优!

就Netty 而言,掌握线程模型的重要性不亚于熟悉它的API和功能。很多时候我遇到的功能、性能等问题,都是由于缺乏对它线程模型和原理的理解导致的,结果我们就以讹传讹,认为Netty 4版本不如3好用等。不能说所有开源软件的版本升级一定都胜过老版本,就Netty而言,我认为Netty 4版本相比于老的Netty 3,确实是历史的一大进步。

   本课程由《Netty 4核心原理作者》亲授。    在互联网分布式系统的推动下,Netty 作为一个能够支撑高性能、高并发的底层网络通信框架而存在。Netty 底层是基于 Java NIO 实现的,对 NIO 进行了非常多的优化,因此深受广大开发者尤其是一线大厂开发者的青睐。    作为一个 Java 开发者,如果没有研究过 Netty,那么你对 Java 语言的使用和理解可能仅仅停留在表面,会点 SSH,写几个 MVC,访问数据库和缓存,这些只是初级 Java 开发者做的事。如果你要进阶,想了解 Java 服务器的高阶知识,Netty 是一个必须要跨越的门槛。学会了 Netty,你可以实现自己的 HTTP 服务器、FTP 服务器、UDP 服务器、RPC 服务器、WebSocket 服务器、Redis 的 Proxy 服务器、MySQL 的 Proxy 服务器等。    如果你想知道 Nginx 是怎么写出来的,    如果你想知道 Tomcat 和 Jetty 是如何实现的,    如果你也想实现一个简单的 Redis 服务器,    ……    那么你应该好好研究一下 Netty,它们高性能的原理都是类似的。    因为 Netty 5.x 已被官方弃用,本课程内容基于 Netty 4 分析其核心原理,培养高级开发者自己“造轮子”的能力。本课程不仅讲述理论知识,还围绕能够落地的实战场景,开创手写源码的学习方式,使学习源码更加高效。本书的主要特色是首次提供了基于 Netty 手写 RPC 框架、基于 Netty 手写消息推送系统等实战案例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值