千万短信1小时发完,怎么设计线程池

生产环境绝对不能用Executors快速创建线程池,不管是用new fixedThreadPool还是newCachedThreadPool,底层都是无界队列,瞬间就能撑爆jvm堆内存,生产上必须手动地去new ThreadPoolExecutor,配上有界队列,这个才是铁律,接下来,线程数到底给多少,要先了解这个任务是CPU密集型还是IO密集型,短线涉及网络调用,典型的是IO密集型,根据实战经验,IO密集型初始线程数可以按N乘以1加W比C来做基础,N就是CPU核数,W就是等待时间,C是计算时间,实际落地的时候,先设计一个初始值,通过压测不断去调优,用数据去说话,这才是架构师严谨的逻辑,拒绝策略怎么选,线程池满了,默认用abortpolicy,直接抛异常,数据丢了,绝对不能忍,生产环境下,推荐CallerRunsPolicy,它是让提交任务主线程自己去执行,这其实是一种天然的背压机制,主线程忙着发短信,就没空再去拉新任务,减缓了任务的生产速度,给线程池留出了喘息的空间,系统才不会雪崩。还有一点,线程池参数写死了,线上流量波动该怎么办,接入动态线程池,核心线程数、队列大小全部放在配置中心,实时可调,再配合一个监控报警,队列积压超过80%的时候,就自动报警,必要时候可以动态扩容,hip for g 或者 Daniel TP让人知道你有实战经验。最后千万级短信,光靠线程池绝对不够,还需要兜底,每条短信对应数据库的一条记录,设置一个状态位,发送成功直接改状态,万一服务挂了,重启后,定时任务扫描未发送的记录,补偿重发。

AI总结:
生产环境线程池的设计核心是可控性、可观测、可兜底,绝对不能直接用 Executors 快速创建线程池,必须基于 ThreadPoolExecutor 做定制化构建,配合参数科学调优、动态管控和兜底补偿机制,才能保障高并发场景下的系统稳定性。


一、为什么禁用 Executors 快速创建线程池

Executors 提供的快捷方法本质都是「无界设计」,生产环境没有流量边界管控,极易引发 OOM 和系统雪崩,核心问题分两类:

  1. newFixedThreadPool:核心线程数与最大线程数固定,底层使用无界的 LinkedBlockingQueue(默认容量 Integer.MAX_VALUE)。高并发下任务会无限堆积在队列中,很快撑满 JVM 堆内存,最终触发 OOM。
  2. newCachedThreadPool:核心线程数为 0,最大线程数无界(Integer.MAX_VALUE),任务到来就创建新线程。高并发下会瞬间创建大量线程,耗尽系统 CPU 和内存资源。

生产铁律:必须手动 new ThreadPoolExecutor,显式指定所有核心参数,强制使用有界队列,从根源控制任务堆积的上限。


二、核心线程数的科学设定方法

线程数不是拍脑袋定的,要先按任务类型做估算,再通过压测最终确认。

  1. 先区分任务类型
    • CPU 密集型:以计算逻辑为主,线程数推荐约等于 CPU 核心数,避免过多上下文切换损耗性能。
    • IO 密集型:涉及网络调用、数据库读写等(比如短信发送、远程接口调用),线程大部分时间在等待 IO,CPU 利用率低,可以设置更多线程。绝大多数业务场景都属于 IO 密集型。
  2. IO 密集型经验公式
    初始估算公式:线程数 = CPU核心数 × (1 + 等待时间 / 计算时间)
    • W:任务的 IO 等待时间;C:任务的 CPU 计算时间
    • 例:一次短信发送,网络 IO 耗时 200ms,本地计算耗时 10ms,8 核 CPU 下,初始线程数 ≈ 8 × (1 + 200/10) = 168
  3. 落地原则
    公式只用来估算初始值,最终参数必须通过压测验证,结合 CPU 利用率、吞吐量、响应时间逐步调优,用实际数据定最终值,这才是架构师的严谨逻辑。

三、拒绝策略的生产级选型

线程池满载(核心线程占满、队列排满、达到最大线程数)时,拒绝策略直接决定系统的稳定性。

  1. 默认 AbortPolicy:直接抛出拒绝异常,任务直接丢弃。生产环境绝大多数业务不能接受数据丢失,不推荐使用。
  2. 生产推荐:CallerRunsPolicy(调用者运行策略)
    • 机制:任务提交失败时,由提交任务的主线程自己执行该任务。
    • 本质是天然的背压机制:主线程忙着执行任务,就无法继续拉取、提交新任务,自动降低任务流入速度,给线程池留出喘息空间,避免任务雪崩。

四、生产级增强:动态调优与可观测性

解决参数写死、无法适配流量波动的问题,体现线上运维实战能力。

  1. 参数动态热更新:把核心线程数、最大线程数、队列容量等核心参数接入配置中心(如 Nacos、Apollo),支持实时调整,无需重启服务,快速应对流量波动。
  2. 监控告警:监控线程池的活跃线程数、队列积压率、任务拒绝次数等核心指标;队列使用率超过 80% 时自动告警,提前感知风险。
  3. 动态线程池:可以基于开源动态线程池组件(如 DynamicTp)实现,支持根据流量自动弹性扩缩容,适配流量波峰波谷,提升资源利用率。

五、海量任务场景的兜底补偿

千万级短信这类海量异步任务,单靠线程池无法 100% 保障可靠性,必须增加持久化兜底机制:
每条短信先落库,设置独立的状态位(待发送、发送成功、发送失败);发送成功后异步更新状态。
一旦服务异常重启,通过定时任务扫描所有「待发送 / 发送失败」的记录,执行补偿重发,保证业务最终一致性。


总结

生产环境线程池的完整设计逻辑是:先做边界管控(有界队列防 OOM)→ 科学估算参数 → 合理选择拒绝策略(背压防雪崩)→ 动态可观测适配流量 → 持久化兜底保最终一致,从基础规范到极端保障,形成完整的稳定性闭环。

posted @ 2026-09-16 10:03  堭鍙銤  阅读(8)  评论(0)    收藏  举报