云计算、AI、云原生、大数据等一站式技术学习平台

网站首页 > 教程文章 正文

docker容器中使用java8并行流遇到的坑(线上)

jxf315 2024-12-11 14:13:10 教程文章 38 ℃

1.问题

最近接到用户报障,线上某个接口频频超时,故拎出代码,大致如下

list.parallelStream().map(c -> {c.httpGet()}

上面的代码大致意思是并行流循环list,然后去调用一个第三方接口,用这种做法来达到多线程的方式调用第三方接口。

首先承认的是这种写法的人只是想实现某个功能,而没有真实的高并发编程经验和toC经验。

博主回想以前在某互联网公司对于此类接口,统称为“重服务”。如何优化这种场景本篇不提,只是想看看为什么接口会超时。

2.分析过程

分析:

1.查看调用链

查看调用链发现调用链显示串行循环调用三方接口100多次,每次大概500ms所以统计下大概50几秒,而ng侧会在调用超过30ms切掉本次调用

当时心里一想这不是java8的并行流吗,为什么串行循环调用。而不是并行去调用。一度怀疑调用链出现问题,当然万万不可怀疑是jdk问题。

2.分析源码

java8的流是如上面图结构,最后走的公共线程池是ForkJoinPool,而这个ForkJoinPool的初始化线程池数量为:

再深入到这个方法 ForkJoinPool makeCommonPool() {}

return new ForkJoinPool(parallelism, (ForkJoinPool.ForkJoinWorkerThreadFactory)factory, handler, 0, "ForkJoinPool.commonPool-worker-");

通过上图可以看到:

Runtime.getRuntime().availableProcessors() - 1 如果大于0那么就是这个数,否则 parallelism为1

3.查看日志

截图当时未保存确实显示的是如下一个线程,验证猜想

ForkJoinPool.commonPool-worker-0

4.进一步验证

线上打印Runtime.getRuntime().availableProcessors()


4.解决

1.临时解决方案

增加jvm参数-Djava.util.concurrent.ForkJoinPool.common.parallelism=10,为parallelStream增加自定义线程池线程数量,这个是全局的慎用

2. 定义自定义的线程池,来代替java8默认线程池

5.为什么Runtime.getRuntime().availableProcessors() =1

需要指明的是本项目是在docker容器里部署

jdk <1.8.0_131 在docker内 获取的是宿主机上的内核数

jdk >=1.8.0_131 在docker内 获取的是docker被限制的内核数

以下2篇文章可以很好的解答这个问题

https://blog.csdn.net/weixin_30905133/article/details/98007814

https://blogs.oracle.com/java/post/java-se-support-for-docker-cpu-and-memory-limits

Tags:

最近发表
标签列表