三宗罪:
I/O多路复用技术
可以灵活切换Reactor线程模型
采用串行无锁化设计,I/O线程内部使用串行操作,避免多线程竞争和同步锁导致性能下降。局部无锁化
影响性能的关键因素:
Netty的零拷贝主要体现在三个方面:
为了重用缓冲区,Netty提供了基于内存池的缓冲区重用机制。通过RECYCLE的get方法循环使用ByteBuf对象
几个对性能影响较大的参数:
Netty的应用场景:
传统的同步阻塞编程模式在连接时会直接阻塞直到连接成功或者连接超时,NIO的异步连接超时无法再API层面直接设置,需要通过定时器来主动监测
Netty实现:
在创建客户端时,可以配置连接超时参数
Netty在发起连接的时候,会根据超时时间创建ScheduledFuture挂在Reactor上,用于定时检测是否发生连接超时,具体定时任务由NioEventLoop执行。
NIO编程出现的可靠性问题:
Netty通过SocketChannel的read操作报出异常,由NioByteUnsafe同一处理
同样,Netty通过SocketChannel的read操作返回值处理异常
Netty的处理策略为发生I/O异常时,释放底层资源,再将异常信息发送给用户
心跳检测机制
Reactor除了会发生IO异常,也可能会发生非io异常,也要捕捉Throwable
捕获 Throwable之后,即便发生了意外未知对异常,线程也不会跑飞,它休眠1S,防止死循环导致的异常绕接,然后继续恢复执行。这样处理的核心理念就是:
著名的epoll bug
Netty的解决策略:
NIO的内存保护主要集中在以下几点:
Netty提供了内存池和对象池,这种机制下JVM不会自动释放对象,需要显式释放,防止用户遗漏导致内存泄露,Netty在PipeLine的尾Handler中自动堆内存进行释放
在内存分配时指定缓冲区长度上限
在对缓冲区进行写入操作时,如果容量不足需要扩展首先对最大容量进行判断,如果容量超过上限,则拒绝扩展。
在消息解码时,对消息长度进行判断,如果超过最大容量上限,就抛出解码异常,拒绝分配内存。
流量整形(Traffic Shaping)是一种主动调整流量输出速率的措施,一个典型应用是基于下游网络节点的TP指标来控制本地流量的输出。
Netty的流量整形有两个作用:
作用范围是进程级的,也就是说对所有的Channel都有效。
可以通过参数设置:报文接收速率、发送速率、整形周期。
Netty流量整形原理:
对每次读取到的ByteBuf可写字节数进行计算,获取当前的报文流量,然后与流量整形阈值对比。如果已经达到或者超过了值。则计算等待时间delay,将当前的ByteBuf放到定时任务Task中缓存,由定时任务线程池在延迟 delay之后继续处理该ByteBuf。
与全局流量整形的区别就是作用域不同。
Java的优雅停机通常通过注册JDK的ShutdownHook来实现,当系统接收到退出指令后,首先标记系统处于退出状态,不再接收新的消息,然后将积压的消息处理完,最后调用资源回收接口将资源销毁,最后各线程退出执行。
通常优雅退出有个时间限制,例如30S,如果到达执行时间仍然没有完成退出前的操作,则由监控脚本直接 kill-9pid,强制退出。
发送队列并没有容量上限,如果对方网络处理速度较慢,或一次发送消息量过大,都会导致ChannelOutboundBuffer的内存膨胀,可能导致系统内存溢出
优化方式:通过启动项的ChannelOption设置发送队列的长度
当网络发生故障的时候,Netty会关闭链路,然后循环释放待未发送的消息,最后通知监听 listener。
大多数场景下,业务用户会使用RPC框架,他们通常不需要直接针对Netty编程,如果Netty提供了发送失败消息的回推功能,RPC框架就可以进行封装,提供不同的策略给业务用户使用,例如:、
1.缓存重发策略:当链路发生异常之后,尚未发送成功的消息自动缓存,待链路恢复正常之后重发失败的消息;
2.失败删除策略:当链路发生异常之后,尚未发送成功的消息自动销毁,它可能是非重要消息,例如日志消息,也可能是由业务直接监听异常并做特殊处理:
3.其他策略…