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 个线程?因为存在绑定多个端口。
NO 3: 心跳包一般都是怎么设计的?
所谓心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性.
注:心跳包还有另一个作用,经常被忽略,即:一个连接如果长时间不用,防火墙或者路由器就会断开该连接。
NO 4: Netty 的粘包拆包问题如何解决?
一般解决粘包拆包问题有 4 中办法
1.在数据的末尾添加特殊的符号标识数据包的边界,通常会加\n、\r、\t或者其他的符号
Netty内置LineBasedFrameDecoder
2.在数据的头部声明数据的长度,按长度获取数据
将消息分为 head 和 body,head 中包含 body 长度的字段,一般 head 的第一个字段使用 int 值来表示 body 长度; LengthFieldBasedFrameDecoder
3.规定报文的长度,不足则补空位。读取时按规定好的长度来读取。比如 100 字节,如果不够就补空格;
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 服务器怎么关闭?
• 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的处理顺序和注册顺序相反
• NO 9: Netty项目中经常出现 Timeout,可能是什么原因
请求/响应: 网络原因
• 执行:
• 执行本身问题:业务处理本身陷入僵局,例如死循环等待了
• 不执行:Netty 流量整形等原因
• 执行不过来:
• 系统瓶颈了 -> 全力以赴但是有心无力 -> 调优或者增加机器
• 没有充分利用系统资源 -> 线程模型不合适或者线程数太少(默认Cpu core * 2,对于业务是io密集型的应用程序太少)-> 增加线程数(推荐独立线程池)
• 排查思路:
网络原因 -> 业务本身原因 -> 流量整形等限制因素 -> 线程模型 -> 增加线程 -> 增加机器
• NO 10: 多次读事件或者连续读的时候,业务层收到的数据是保序的么?
对于单个连接而言:多次读,业务层收到的数据是保序的;
• 对于多个连接而言:不同的读,汇聚到业务层数据是不保序的
• 如何保序?
• DefaultEventExecutor (extends SingleThreadEventExecutor)