vlambda博客
学习文章列表

Netty十大热点问题解惑

今天来聊一聊Netty的常见热点问题,网上学习Netty的资料也很多,真正想要掌握Netty框架的底层原理还是得系统的看下书。带着问题去学习会更有效,当然最重要的还是要动手实践,把代码跑起来debug,这样才能更好的理解Netty设计的精妙之处。

线程模型

  • 线程模型:BIO、伪异步I/O、NIO、AIO

  • reactor线程模型:Reactor单线程、Reactor多线程、主从Reactor多线程模型、Netty自定义线程模型

服务端一般流程

启动-创建连接-接收请求-处理业务-发送响应-断开连接-关闭服务

处理业务包含了编解码、过滤、拦截等操作

客户端一般流程

启动-连接服务端-发送请求-接收响应-处理业务-关闭服务

参数优化

  • Linux参数:/proc/sys/net/ipv4/tcp_keepalive_time,最大文件打开句柄数

    • ulimit -n xxxx

  • SO_BACKLOG, 1024

  • TCP_NODELAY true

  • AUTO_CLOSE

  • SO_REUSEADDR

易于调试的一些设置

  • 完善线程名

  • 完善handler名称

  • 日志设置

  • 数据可视化:当前连接数、线程数

需要注意的点

  • ctx.channel.write/ctx write 的区别

    • ctx.channel.write:会从tailContext开始,一般在客户端使用

    • ctx.write:从当前context,一般在服务端使用

  • SimpleChannelInboundHandler/SimpleChannelOutboundHandler:一般最后的handler可以直接继承这个,会自动释放buf


下面就一起来看下Netty常见的十大问题:


NO 1: 作为一个学习netty的纯新手,有哪些值得推荐的、系统学习的书?


主要分为三大类相关的书籍推荐:

1. 网络知识:《TCP/IP详解》、《图解TCP/IP》、《Wireshark网络分析就这么简单》

2. 网络编程:《Java 网络编程》、《Java TCP/IP Socket编程》

3. Netty 相关: 《Netty权威指南》 、《Netty实战》(译自《Netty in action》: Norman Maurer)、 《Netty进阶之路:跟着案例学Netty》


NO 2:为什么说boss group大多项目只有一个线程在用?



根本上还是一个端口对应一个boss线程池的一条处理线程,即一条线程持有一个端口对应的selector。当然如果我们启动不仅仅是一个端口的话,仍然需要写对应的端口个数。以上是Netty实现的原理,那为什么Netty不把 boss group 改成默认就 1 个线程?因为存在绑定多个端口。

Netty十大热点问题解惑


NO 3: 心跳包一般都是怎么设计的?


所谓心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性.


注:心跳包还有另一个作用,经常被忽略,即:一个连接如果长时间不用,防火墙或者路由器就会断开该连接


Netty十大热点问题解惑



    

Netty十大热点问题解惑


NO 4: Netty 的粘包拆包问题如何解决?

一般解决粘包拆包问题有 4 中办法

  1. 1.在数据的末尾添加特殊的符号标识数据包的边界,通常会加\n、\r、\t或者其他的符号

Netty内置LineBasedFrameDecoder
  1. 2.在数据的头部声明数据的长度,按长度获取数据

将消息分为 head 和 body,head 中包含 body 长度的字段,一般 head 的第一个字段使用 int 值来表示 body 长度; LengthFieldBasedFrameDecoder
 
  1. 3.规定报文的长度,不足则补空位。读取时按规定好的长度来读取。比如 100 字节,如果不够就补空格;


  2. 4.使用更复杂的应用层协议。


NO 5: Netty 能支持多少连接?


直接先说结论:

• 对于单个客户端而言:连接到一个服务器最多连接数?约64K

• 对于单个服务器而言:最多支持多少连接,取决于内存资源!(1 socket 约占 3-4K)


客户端:

一个连接是由:客户端 IP + 客户端PORT + 服务器 IP + 服务器 PORT(固定的) 四个元素决定的

• 对于单个客户端而言:连接到一个固定服务器最多连接数?

• 理论值:取决于本地可用端口数(因为其他3个元素固定了):约64K【65535(报文中端口占用字节数是16)-

1024(保留端口,不给用)】


服务端:

64K),万亿以上。

• 实际值:受限于两个方面:

• 系统限制:同上,Linux 文件句柄最大21亿(2147483584)

• 资源限制:内存资源限制,100万连接就要占用3G以上了,1000 万 30G 以上


所以如果有目标,先定义一个小目标:100 万

(ulimit 超过 1048576 时需要更改 fs.nr_open)


• NO 6:产线环境,Netty 服务器怎么关闭?


Netty十大热点问题解惑



• NO 7: 判断 ChannelHandler 是否可以共享有没有一般的原则?


• Handler 共享使用原则:

• 必须共享或者必须不共享的,无从选择

• 可共享的,可不共享的;就共享!

io.netty.handler.logging.LoggingHandler


一个 handler 如果是共享的,往往如此:

• 功能是全局性的,例如统计整个系统的收发字节数;

• 标记 @Sharable;

• Javadoc 标识的用法指明应该共享;

• 没有任何成员变量,或者只有static的成员变量

• 类名带有关键字 Global


• NO 8: ChannelHandler 的顺序应该怎么排?


Netty中的所有handler都实现自ChannelHandler接口。按照输出输出来分,分为ChannelInboundHandler、ChannelOutboundHandler两大类。ChannelInboundHandler对从客户端发往服务器的报文进行处理,一般用来执行解码、读取客户端数据、进行业务处理等;ChannelOutboundHandler对从服务器发往客户端的报文进行处理,一般用来进行编码、发送报文到客户端。


- 对于Inbound事件,InboundHandler的处理顺序是和注册顺序一致
- 对于Outbound事件,OutboundHandler的处理顺序和注册顺序相反


Netty十大热点问题解惑

      


• NO 9: Netty项目中经常出现 Timeout,可能是什么原因


请求/响应: 网络原因

• 执行:

• 执行本身问题:业务处理本身陷入僵局,例如死循环等待了

• 不执行:Netty 流量整形等原因

• 执行不过来:

• 系统瓶颈了 -> 全力以赴但是有心无力 -> 调优或者增加机器

• 没有充分利用系统资源 -> 线程模型不合适或者线程数太少(默认Cpu core * 2,对于业务是io密集型的应用程序太少)-> 增加线程数(推荐独立线程池)

• 排查思路:

网络原因 -> 业务本身原因 -> 流量整形等限制因素 -> 线程模型 -> 增加线程 -> 增加机器


• NO 10: 多次读事件或者连续读的时候,业务层收到的数据是保序的么?


对于单个连接而言:多次读,业务层收到的数据是保序的;

• 对于多个连接而言:不同的读,汇聚到业务层数据是不保序的

• 如何保序?

• DefaultEventExecutor (extends SingleThreadEventExecutor)