1、分析当前存在的问题
当数据抵达provider之后,首先经过一个自定义解码器,然后进入到ServerHandle中进行处理
在ServerHandle中,根据第2节中的代码可以看出,ChannelHandler中的业务逻辑,正常是由NioEventLoop线程串行执行的。这时候就会出现问题了:
首先,通过解码器将字节序列转换为消息对象,再将消息对象传给后续业务的handler进行处理,如果后续业务需要进行IO查询,或者调用第三方插件,有可能会消耗大量的时间,占用额外的线程资源。
- 在ChannelHandler中编写了可能会阻塞NIO线程的代码,但是用户没有意识到,如数据库查询、第三方服务的远程调用、消息中间件等
- 用户知道需要耗时,但是处理过程中出错了,比如业务线程池阻塞、任务队列已满等
所以,不能使用默认的串行编程,否则一旦出现阻塞,就将影响其他服务的远程调用
服务端改善
查看java.nio.channels.SelectionKey的源码 ,可以知道只有四种NIO事件:read、write、connect、accept。
Netty自身设计了不同的线程池管理不同的事件,服务端的workerGroup负责读写事件,bossGroup负责accept事件。
但是业务线程最好不要交给NioEventLoopGroup线程池处理。否则可能导致IO处理任务与业务执行任务之间发生资源竞争,造成阻塞。所以,面向具体业务,较给独立的业务线程池处理。
在Netty的内部初始化channelHandler的时候,我们可以通过手动指定线程池的方式来保证handler所使用的线程池和NioEventLoopGroup线程池进行隔离。
使用阻塞队列提高吞吐性能
设置一条单独的阻塞队列用来接收请求,然后在队尾设置一个业务线程池进行消息的消费。当访问量巨大的时候,还可以通过横向扩展,非常简单地实现扩容。我们可以专注于提高单点地性能。
初始化一个容量为512的阻塞队列,当收到链接通知的时候,就将它放入到阻塞队列里面,然后在读取出来,放到具体的业务池中进行处理。
优化之后的ServerHandler就可以变得很简单了
客户端改善:
在Rpc包装类中写上异步相关的属性:
此时,只需要注意两点:
- 客户端启动时,将异步属性设为true;
- 在代理模块中,加入一个if判断,如果为异步请求,就不必要在RESP_MAP中判断是否有响应结果了。