• 业务线程池阻塞分析


    在一些业务处理ChannelHandler中执行一些耗时任务或者其他任务时,通常不会使用Nio线程来做,而是调度到其他的业务线程池去做。
    线程池ThreadPoolExecutor的 CallerRunsPolicy策略:如果当前线程池任务队列满了,新增的任务交由调度前的线程来执行
    如果后端业务逻辑处理慢,则会导致业务线程池阻塞队列积压,当积压达到容量上限时,JDK会抛出RejectedExecutionException异常,由于业务设置了CallerRunsPolicy策略,就会由调用方的线程NioEventLoop执行业务逻辑,最终导致 NioEventLoop线程被阻塞,无法读取请求消息
    造成的现象就是,如果NioEventLoop线程阻塞,则一段时间内客户端的网络消息IO会丢失,这段时间服务端接收不到消息。从阻塞状态恢复后就又可以接收到消息了。
    除了JDK线程池异常处理策略使用不当,有些业务人员喜欢自己写阻塞队列,当队列满时,向队列加入新的消息会阻塞当前线程,直到消息能够加入队列。案例中的车联网服务端真实业务代码就有此类问题:当转发到下游系统发生某些故障时,会导致业务定义的阻塞队列无法弹出消息进行处理,当队列积压满时,就会阻塞Netty的NIO线程,而且无法自动恢复。

    【防止NioEventLoop挂死】

    防止Nio线程挂死的方法就是,NioEventLoop只处理网络I/O,业务消息调度给业务线程池处理。

    【netty内多线程最佳实践】

    (1) 创建两个NioEventLoopGroup,用于逻辑隔离NIO Acceptor和 NIOI/O线程
    (2)尽量不要在ChannelHandler中启动用户线程(解码后用于将POJO消息派发到后端业务线程的除外)。
    (3) 解码要放在NIO线程调用的解码Handler中进行,不要切换到用户线程完成消息的解码,相对于切换线程的开销收益更大
    (4) 如果业务逻辑操作非常简单(纯内存操作),没有复杂的业务逻辑计算,也没有可能会导致线程被阻塞的磁盘操作、数据库操作、网络操作等,可以直接在NIO线程上完成业务逻辑编排,不需要切换到用户线程,相对于切换线程的开销收益更大
    (5)如果业务逻辑复杂,不要在 NIO线程上完成,建议将解码后的POJO消息封装成任务, 派发到业务线程池中由业务线程执行,以保证NIO线程尽快被释放,处理其他的1/O操作
    推荐的线程数量计算公式有以下两种。
    (1)公式1:线程数量=(线程总时间/瓶颈资源时间)x瓶颈资源的线程并行数。
    (2)公式2:QPS= 1000/线程总时间×线程数。
  • 相关阅读:
    acwing算法基础之基础算法--快速排序
    Vue04/ 计算属性computed 介绍和完整写法 get( ) set( )
    linux安装Android打包环境
    Spring学习中存在报错问题汇总
    九宫格 图片 自定义 路径
    【Vue项目】实现商品的升降序
    微服务分布式开源架构是什么?
    论文阅读 Fast Reinforcement Learning Via Slow Reinforcement Learning
    如何自定义代码生成器(下)
    autoware之轮式里程计计算
  • 原文地址:https://blog.csdn.net/qq_34448345/article/details/127439909