当我从第三方拉取百万数据到本地的线程莫名不工作的排查思路

186 阅读3分钟

1.背景

开发任务:

本地mysql存储了100w+ 的酒店行记录数据, 现在遍历每个酒店id,从elong开发者平台拉取elong的百万酒店的图片到本地,存储到ES当中

方案设计:

  1. 采取生产消费的代码模型,一个线程不断地循环读取mysql 的酒店id add 进阻塞队列,然后在启动spring容器的同时,初始化三个线程去消费阻塞队列
  2. 阻塞队列是线程安全的,设计没有问题
  3. 为什么是三个线程去消费,是因为受限于elong开发者平台 获取酒店图片的接口QPS的限制
  4. 每个消费的线程http去访问elong开发者平台获取酒店图片的接口,采用的是kevinsawicki框架
  5. 每个线程在访问elong开发者平台获取酒店图片的接口失败的时候,会打印日志并且重新加入阻塞队列进行重试
  6. spring bean的初始化消费线程刚开始获取不到阻塞队列的元素就睡一会,保护机器线程
  7. 这个接口写了是去拉取数据的,待拉取数据完成后,需要调小blockingQueue的大小,然后注释掉@PostConstruct, 避免在jvm进程中有一些冗余的time_waited 线程,接口是一次性的。拉完数据就释放资源

2.代码demo演示

bean中的成员变量blockingQueue

image.png

主线程中的生产者

image.png

spring 的bean的三个消费者

image.png

3. 遇到的问题

消费线程莫名不工作了,不清楚是否阻塞队列还有待消费的元素,反正es的数据保持不变,没有增加,说明消费线程不工作了,或者是机器重启了

4 解决步骤

  1. 怀疑跑任务机器被重启,去devops平台确认没人重启

  2. 去看日志,少打了,补充日志,会打日志真的很重要,会打日志真的很重要,会打日志真的很重要,重要的事情说三遍,等会说一下我补充了哪些日志

  3. 补充了日志进一步上生产继续跑数据,5个小时以后发生了同样的问题,怀疑机器oom了,去查看机器的oom日志和gc日志、cpu、内存等信息

image.png

不巧的很,没配置heap oom 日志 -XX:HeapDumpPath=null

image.png

cpu, 内存正常

image.png

image.png

GC 日志正常

image.png

  1. 排查到这,常规手段都用上了,都没啥问题,现在我的疑惑就是我的消费线程干嘛去了,现在在干嘛,我要去追踪我的线程,那只能上jstack了

image.png

接下来分析stack文件,一下子就看到了

image.png

我在http访问第三方接口的时候被阻塞了,一直在那running,很明显我没设置降级措施,没设置超时时间

image.png

image.png

总结

外部接口的不稳定最终会导致这样的问题,所以一定要http接口降级,设置超时时间

我补充了线程名称和阻塞队列的任务数目的日志,后来我去jstack才能准确的捕获到出问题的线程,大家也看到了,并不是只有block的线程有危害,running的线程也是有可能有问题的

可以借助chatgpt分析jstack文件