zoukankan      html  css  js  c++  java
  • Netty并发优化之ExecutionHandler

    上文《Netty框架入门》说到:如果业务处理handler耗时长,将严重影响可支持的并发数。
    针对这一问题,经过学习,发现了可以使用ExecutionHandler来优化。
    先来回顾一下没有使用ExecutionHandler优化的流程:
        1)Boss线程(接收到客户端连接)->生成Channel->交给Worker线程池处理。
        2)某个被分配到任务的Worker线程->读完已接收的数据到ChannelBuffer->触发ChannelPipeline中的ChannelHandler链来处理业务逻辑。
        注意:执行ChannelHandler链的整个过程是同步的,如果业务逻辑的耗时较长,会将导致Work线程长时间被占用得不到释放,从而影响了整个服务器的并发处理能力。
     
    一、引入ExecutionHandler优化
    //HttpServerPipelineFactory.java
    private final ExecutionHandler executionHandler = new ExecutionHandler(
                 new OrderedMemoryAwareThreadPoolExecutor(16, 1048576, 1048576));
    public class HttpServerPipelineFactory implements ChannelPipelineFactory {
        @Override
        public ChannelPipeline getPipeline() throws Exception {
            ChannelPipeline pipeline = Channels.pipeline();
            pipeline.addLast("decoder", new HttpRequestDecoder());
            pipeline.addLast("encoder", new HttpResponseEncoder());
            pipeline.addLast("execution", executionHandler);
            pipeline.addLast("handler", new HttpServerHandler());
            return pipeline;
        }
    }
        当我们引入ExecutionHandler后,原本同步的ChannelHandler链在经过 ExecutionHandler后就结束了,它会被ChannelFactory的worker线程池所回收,而剩下的ChannelHandler链将由ExecutionHandler的线程池接手处理。
        对于ExecutionHandler需要的线程池模型,Netty提供了两种可选:
            1) MemoryAwareThreadPoolExecutor 通过对线程池内存的使用控制,可控制Executor中待处理任务的上限(超过上限时,后续进来的任务将被阻塞),并可控制单个Channel待处理任务的上限,防止内存溢出错误。但是它不维持同一Channel的ChannelEvents秩序,当经过ExecutionHandler后的ChannelHandler链中有不止一个Handler时,这些事件驱动存在混乱的可能。例如:
     ----------------------------------------> Timeline ------------------------------------->
     Thread X: --- Channel A (Event 2) --- Channel A (Event 1) ----------------------------->
     Thread Y: --- Channel A (Event 3) --- Channel B (Event 2) --- Channel B (Event 3) --->
     Thread Z: --- Channel B (Event 1) --- Channel B (Event 4) --- Channel A (Event 4) --->
            2) OrderedMemoryAwareThreadPoolExecutor 是 MemoryAwareThreadPoolExecutor 的子类。除了MemoryAwareThreadPoolExecutor 的功能之外,它还可以保证同一Channel中处理的事件流的顺序性(不同Channel使用不同的key保持事件顺序),这主要是控制事件在异步处理模式下可能出现的错误事件顺序,但它并不保证同一 Channel中的事件都在一个线程中执行(通常也没必要)。例如:
               
    ----------------------------------------> Timeline ---------------------------------------->
     Thread X: --- Channel A (Event 1) --.   .-- Channel B (Event 2) --- Channel B (Event 3) --->
                                           /
                                           X
                                          / 
     Thread Y: --- Channel B (Event 1) --'   '-- Channel A (Event 2) --- Channel A (Event 3) --->
    二、具有可伸缩性的OrderedMemoryAwareThreadPoolExecutor使用策略
        在大多数情况下,我们会使用OrderedMemoryAwareThreadPoolExecutor,它的构造函数要求我们提供线程池的大小,在上面的代码中,我们使用了16这个具体的值,是一种很不好的写法,通常情况下,我们会使用配置文件使之可变,但是在实际部署时,并不能保证实施人员能很好的去调整,故提供如下的一种写法:
    double coefficient = 0.8;  //系数
    int numberOfCores = Runtime.getRuntime().availableProcessors();
    int poolSize = (int)(numberOfCores / (1 - coefficient));

    我们可以使用poolSize取代OrderedMemoryAwareThreadPoolExecutor(16, 1048576, 1048576)中的那个16,因为当一个系统被开发出来后,它是CPU密集型还是IO密集型是可评估的,通过评估其密集型,调整系数即可:CPU密集型接近0,IO密集型接近1。

  • 相关阅读:
    redis 持久化RDB、AOF
    进程、线程、协程
    nginx 常用配置
    Redis详解(一)
    Nginx(一)
    docker compose
    练习1——排序
    8万行的insert数据,Ctrl+c、Ctrl+v后心态崩了(如何在Linux下对MySQL数据库执行sql文件)
    Ubuntu使用+Thinkphp5学习——20(html文件中代码注释掉,但是runtime/temp中的php文件仍然存在)
    Ubuntu使用+Thinkphp5学习——19(新增用户报错strip_tags() expects parameter 1 to be string, array given)
  • 原文地址:https://www.cnblogs.com/baronzhao/p/netty_2.html
Copyright © 2011-2022 走看看