• 业务线程池阻塞分析


    在一些业务处理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/线程总时间×线程数。
  • 相关阅读:
    深入讲解C++基础知识(一)
    Python语言:经典案例分析讲解2
    【已解决】部署Django项目,uwsgi出现错误:unavailable modifier requested: 0 【解决方法集合】
    魔众短链接系统 v3.9.0
    ctfshow萌新计划web1-5(正则匹配绕过)
    算法竞赛进阶指南 捉迷藏
    2023年10月工作经验及问题整理总结
    flink-cdc同步mysql数据到hbase
    【在Java实际开发项目中,有几个关键要点需要注意】在Java开发过程中,可能会遇到一些常见的问题和挑战。以下是一些c常见问题
    Python入门-变量定义与切片&Python引入包和引入模块
  • 原文地址:https://blog.csdn.net/qq_34448345/article/details/127439909