开发流程就不写了!基本上网上很多用例 基本上可以拿来就使用了。但是想运用到实际项目中还需要做很多调整
参数 :
Netty设置option警告Unknown channel option问题?
1. option主要是设置的ServerChannel的一些选项,而childOption主要是设置的ServerChannel的子Channel的选项。 如果是Bootstrap的话,只会有option而没有childOption,所以设置的是客户端Channel的选项。
2. option主要是针对boss线程组,child主要是针对worker线程组
客户端常见使用配置 :
服务端常见使用配置:
很关键的参数:
ALLOW_HALF_CLOSURE
Netty参数,一个连接的远端关闭时本地端是否关闭,默认值为False。值为False时,连接自动关闭;为True时,触发ChannelInboundHandler的userEventTriggered()方法,事件为ChannelInputShutdownEvent。
核心监听事件:
IdleStateEvent
IdleStateHandler心跳检测主要是通过向线程任务队列中添加定时任务,判断channelRead()方法或write()方法是否调用空闲超时,如果超时则触发超时事件执行自定义userEventTrigger()方法;
Netty通过IdleStateHandler实现最常见的心跳机制不是一种双向心跳的PING-PONG模式,而是客户端发送心跳数据包,服务端接收心跳但不回复,因为如果服务端同时有上千个连接,心跳的回复需要消耗大量网络资源;如果服务端一段时间内内有收到客户端的心跳数据包则认为客户端已经下线,将通道关闭避免资源的浪费;在这种心跳模式下服务端可以感知客户端的存活情况,无论是宕机的正常下线还是网络问题的非正常下线,服务端都能感知到,而客户端不能感知到服务端的非正常下线;
要想实现客户端感知服务端的存活情况,需要进行双向的心跳;Netty中的channelInactive()方法是通过Socket连接关闭时挥手数据包触发的,因此可以通过channelInactive()方法感知正常的下线情况,但是因为网络异常等非正常下线则无法感知;
合理设置接收和发送缓冲区容量
对于长链接,每个链路都需要维护自己的消息接收和发送缓冲区,JDK 原生的 NIO 类库使用的是 java.nio.ByteBuffer, 它实际是一个长度固定的 Byte 数组,我们都知道数组无法动态扩容,ByteBuffer 也有这个限制,相关代码如下:
容量无法动态扩展会给用户带来一些麻烦,例如由于无法预测每条消息报文的长度,可能需要预分配一个比较大的 ByteBuffer,这通常也没有问题。但是在海量推送服务系统中,这会给服务端带来沉重的内存负担。假设单条推送消息最大上限为 10K,消息平均大小为 5K,为了满足 10K 消息的处理,ByteBuffer 的容量被设置为 10K,这样每条链路实际上多消耗了 5K 内存,如果长链接链路数为 100 万,每个链路都独立持有 ByteBuffer 接收缓冲区,则额外损耗的总内存 Total(M) = 1000000 * 5K = 4882M。内存消耗过大,不仅仅增加了硬件成本,而且大内存容易导致长时间的 Full GC,对系统稳定性会造成比较大的冲击
实际上,最灵活的处理方式就是能够动态调整内存,即接收缓冲区可以根据以往接收的消息进行计算,动态调整内存,利用 CPU 资源来换内存资源,具体的策略如下:
- ByteBuffer 支持容量的扩展和收缩,可以按需灵活调整,以节约内存;
- 接收消息的时候,可以按照指定的算法对之前接收的消息大小进行分析,并预测未来的消息大小,按照预测值灵活调整缓冲区容量,以做到最小的资源损耗满足程序正常功能。
幸运的是,Netty 提供的 ByteBuf 支持容量动态调整,对于接收缓冲区的内存分配器,Netty 提供了两种:
FixedRecvByteBufAllocator:固定长度的接收缓冲区分配器,由它分配的 ByteBuf 长度都是固定大小的,并不会根据实际数据报的大小动态收缩。但是,如果容量不足,支持动态扩展。动态扩展是 Netty ByteBuf 的一项基本功能,与 ByteBuf 分配器的实现没有关系;
AdaptiveRecvByteBufAllocator:容量动态调整的接收缓冲区分配器,它会根据之前 Channel 接收到的数据报大小进行计算,如果连续填充满接收缓冲区的可写空间,则动态扩展容量。如果连续 2 次接收到的数据报都小于指定值,则收缩当前的容量,以节约内存。