背景
最近工作中,使用IBMMQ,重启服务时有偶发性的报文丢失情况,应用从队列中获取到了消息,但是线程停止没有处理。
分析
消息处理线程流程:
- 判断线程状态是否可用,如果不可用直接返回。
- 使用MQQueue.get方法获取消息。
- 如果消息为null继续循环;否则判断线程是否可用,可用则进行处理,不可用则产生消息丢失。
服务关闭流程:
1.设置线程状态为不可用
2.睡眠2秒等待线程处理当前内容。
3.线程关闭;tp.shutdown
4. 线程等待结束。tp.awaitTermination(50,SENCONDS)
分析以上过程,发生消息丢弃必为
1.当前线程状态可用
2.获取消息
3.关闭服务:线程不可用
4.关闭服务:睡眠2秒
5.关闭线程
6.处理消息
获取消息与处理消息之间必须大于2s,但是消息获取成功后立即处理,一般情况不会超过2秒。所以将原因定位到获取消息方法中。
分析MQQueue.get方法,如果队列中没有消息,会发生等待,等待时间由参数Wait Interval (-WI)
决定。
该参数解析详见IBM官网说明.
WI参数用来设置适配器等待消息接收的时间。
参数0:不进行等待
参数S: 一直等待
wait_interval_in_milliseconds:等待时间
默认为每次等待1s
在等待时间内,线程无法停止。
分析代码,发现该参数我们代码默认设置了10s,所以在执行服务关闭流程4时,需要等待获取消息结束。
如果在1s时获取消息,在2s时关闭服务,则在4s时线程关闭,如果在6s时获取到消息,此时线程关闭,则发生消息丢失。
改进
修改IW参数值,设置为1,保证在服务关闭睡眠2s时间段内可以获取消息完成,不影响服务关闭流程。
备注
awaitTermination(s,unit)方法说明:
在单位s时间段内,如果线程全部处理完成,则结束返回true;在单位s时间段内,如果线程未全部处理完成,则继续等待;超过单位s时间后,则返回false。