问题背景
短信服务数据库连接数告警,grafana查看数据库连接池被打满。
问题分析
在这段时间内,通过链路分析,发现最终调用第三方短信发送服务偶然耗时过长,分析了原有发送逻辑的代码,该实现在入口send处加了事务,导致后续的消息通道等逻辑判断,以及调用第三方发送逻辑全部在一个长事务里。
解决方案
由于环境因素,不同环境部署的中间件不一致,有些环境没有使用mq,所以mq不考虑在解决方案内
1、双缓存队列+动态线程池
2、初始化线程处理
Q1:为什么不用线程池?
为保证任务不丢失,线程池拒绝策略
AbortPolicy - 抛出异常,中止任务。抛出拒绝执行 RejectedExecutionException 异常信息。线程池默认的拒绝策略。必须处理好抛出的异常,否则会打断当前的执行流程,影响后续的任务执行
CallerRunsPolicy - 使用调用线程执行任务。当触发拒绝策略,只要线程池没有关闭的话,则使用调用线程直接运行任务。一般并发比较小,性能要求不高,不允许失败。但是,由于调用者自己运行任务,如果任务提交速度过快,可能导致程序阻塞,性能效率上必然的损失较大
DiscardPolicy - 直接丢弃,其他啥都没有
DiscardOldestPolicy - 丢弃队列最老任务,添加新任务。当触发拒绝策略,只要线程池没有关闭的话,丢弃阻塞队列 workQueue 中最老的一个任务,并将新任务加入
CallerRunsPolicy策略不会丢失任务,但是会使用提交线程执行任务,加入提交线程执行的任务超长耗时,将会导致整个线程池不可用
Q2:使用固定线程数不会丢数据吗?怎么解决?
在应用重启的时候,是会丢失极少部分数据,可以通过定时钟补偿。