Netty 4.x
1. 高性能通讯框架,解决的是 "网络层" 的问题, 包括并发太多时,已经阻塞的IO(read/write) 导致新连接无法connect, accept的问题,
以及内存拷贝,上下文切换等问题。 主要的 目的是为了让你的 带宽能够完全发挥,不受 socket读写线程的影响。
2. 线程模型reactor:
2.1 单线程, 一个accept线程(NIO, 只处理到 connect/accpet,可以认为打开了一个新句柄,发送了ACK给client ) + 1个 read 线程 + 1个write线程
2.2 多线程,一个accpet线程 + n个read + n个write(1个write?)
2.3 主从线程池模型,
(bossPool负责 connect,accept, ssl头,ACK,新句柄等等)
(workPool 负责 read,write, encode, decode)
3. NioEventLoop 是个好东西, accept 也靠他, 读写也靠他。
ServerBootstrap bootstrap = new ServerBootstrap();
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
执行过程中不会被其他线程抢占, 设定的大小不应该过大。
workGroup中的NioEventLoop需要特别注意,不要在这种中间增加“业务”代码,
参照:
2.4. Netty线程开发最佳实践
2.4.1. 时间可控的简单业务直接在IO线程上处理
如果业务非常简单,执行时间非常短,不需要与外部网元交互、访问数据库和磁盘,不需要等待其它资源,则建议直接在业务ChannelHandler中执行,不需要再启业务的线程或者线程池。避免线程上下文切换,也不存在线程并发问题。
2.4.2. 复杂和时间不可控业务建议投递到后端业务线程池统一处理
对于此类业务,不建议直接在业务ChannelHandler中启动线程或者线程池处理,建议将不同的业务统一封装成Task,统一投递到后端的业务线程池中进行处理。
过多的业务ChannelHandler会带来开发效率和可维护性问题,不要把Netty当作业务容器,对于大多数复杂的业务产品,仍然需要集成或者开发自己的业务容器,做好和Netty的架构分层。
2.4.3. 业务线程避免直接操作ChannelHandler
对于ChannelHandler,IO线程和业务线程都可能会操作,因为业务通常是多线程模型,这样就会存在多线程操作ChannelHandler。为了尽量避免多线程并发问题,建议按照Netty自身的做法,通过将操作封装成独立的Task由NioEventLoop统一执行,而不是业务线程直接操作,相关代码如下所示:
图2-31 封装成Task防止多线程并发操作
如果你确认并发访问的数据或者并发操作是安全的,则无需多此一举,这个需要根据具体的业务场景进行判断,灵活处理。
目前看来,Netty的定位:
1. 网关
2. 消息推送