记一次job不跑的问题

现象:同步数据的脚本突然不执行了,但从日志上来看job的确跑了(日志打印了xxx开始执行...)。

原因:多个job使用一个线程池,其中一个job因为第三方数据和代码的问题导致资源未能释放掉,从而引发了同步数据脚本执行不了。

如何发现的:

  • 首先日志有打印说明job肯定执行了,所以肯定是job内部的代码导致业务逻辑未执行。
  • 沿着这个方向去看了下具体的业务实现,发现它使用了ThreadPoolExecutor和CountDownLatch来处理;而ThreadPoolExecutor又定义为成员变量(多个job使用一个ThreadPoolExecutor),而非局部变量。
  • 根据这些特性便猜测应该是有哪个job在使用线程资源时未能及时的释放掉,接着我挨个排查,终于在一个job中看到了奇葩的while(true);它的逻辑是直到拿到想要的数据才会break掉,当然正常情况下是没有问题的,但当第三方数据异常便会出现数据积压,直到堆满线程池,让其它job都无法正常执行下去。

解决方案:

  • 当时比较紧急,先采用了最简单,且改动最小的办法:让那个while(true)的job使用私有的线程池,这样就不会影响到其它的job了。
  • 之后再询问当事人后,了解到有问题的那个job对数据的实时性要求没那么高,所以我们去除了while(true)的逻辑,让处于中间态的数据直接结束,到下一次执行job的时候再同步数据。

当时的代码(伪代码):

 1 public class Job {
 2 
 3     /**
 4      * 线程池(我为了方便才使用Executors,实际开发不建议使用Executors来勾线线程池)
 5      */
 6     private static final ExecutorService THREAD_POOL = Executors.newFixedThreadPool(15);
 7 
 8     /**
 9      * 影响业务的job,也就是上面所说突然不跑的job
10      */
11     public void importantJob() {
12         try {
13             final CountDownLatch latch = new CountDownLatch(size);
14             for (final Orders order : orderList) {
15                 THREAD_POOL.execute(new Runnable() {
16                     @Override
17                     public void run() {
18                         try {
19                             log.info("{} importantJob start", order.getId());
20                             // 这里是业务处理
21                             log.info("{} importantJob end", order.getId());
22                         }
23                         catch (Exception e) {
24                             log.error("importantJob handler error", e);
25                         }
26                         finally {
27                             latch.countDown();
28                         }
29                     }
30                 });
31             }
32 
33             latch.await();
34         }
35         catch (Exception e) {
36             log.error("importantJob error", e);
37         }
38     }
39 
40     /**
41      * 影响业务的job,也就是上面所说突然不跑的job
42      */
43     public void minorJob() {
44         try {
45             final CountDownLatch latch = new CountDownLatch(size);
46             for (final Data data : dataList) {
47                 THREAD_POOL.execute(new Runnable() {
48                     @Override
49                     public void run() {
50                         try {
51                             log.info("{} minorJob start", data.getId());
52                             minorJobHandler();
53                             log.info("{} minorJob end", data.getId());
54                         }
55                         catch (Exception e) {
56                             log.error("minorJob handler error", e);
57                         }
58                         finally {
59                             latch.countDown();
60                         }
61                     }
62                 });
63             }
64 
65             latch.await();
66         }
67         catch (Exception e) {
68             log.error("minorJob error", e);
69         }
70     }
71 
72     private void minorJobHandler() {
73         // 一直拿第三方数据,直到拿到想要的结果
74         while (true) {
75             // 获取第三方数据
76             Data data = getData();
77             if (data.status == 1) {
78                 // 拿到想要的数据,结束循环
79                 break;
80             }
81             // 否则一直去获取
82         }
83     }
84 
85 }

这是不是很有意思(✪ω✪)

 

posted @ 2019-11-27 10:23  被猪附身的人  阅读(467)  评论(0编辑  收藏  举报