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。

  • 相关阅读:
    1 . CentOS 7的yum更换为国内的阿里云yum源
    0. vagrant+vbox创建centos7虚拟机
    git上传到码云和下载到本地
    spring boot udp或者tcp接收数据
    你好,博客园
    使用firdder抓取APP的包
    初见loadrunner
    sublime快捷键大全
    html中行内元素与块级元素的区别。
    html.css溢出
  • 原文地址:https://www.cnblogs.com/baronzhao/p/netty_2.html
Copyright © 2011-2022 走看看