【发布时间】:2021-02-04 02:38:07
【问题描述】:
在我的 Spring Boot 服务中,我正在根据订单详细信息和客户详细信息验证收到的订单。
在客户详细信息中,我有不同的对象列表,例如服务、属性、产品等,对于每个列表,我都在执行以下操作:
products.stream()
.filter(Objects::nonNull)
.map(Product::getResource)
.filter(Objects::nonNull)
.filter(<SimplePredicate>)
.collect(Collectors.toList());
我多次对产品、服务和属性使用这样的流。我们观察到,在性能方面,它提供了非常高的 TPS,并且内存使用率也非常理想。但这非常消耗CPU。我们在 Kubernetes pod 中运行该服务,它占用了所提供 CPU 的 90%。
一个更有趣的观察是,我们提供的 CPU 越多,达到的 TPS 越高,CPU 使用率也达到 90%。
是因为 Streams 消耗更多的 CPU 吗?还是因为每次 Streams 迭代后内部内存都可能被垃圾回收,所以垃圾回收率很高?
EDIT-1:
在使用负载测试进行进一步调查后,发现:
- 每当我们增加并发线程时,由于 CPU 使用率高,服务开始没有响应,随后 CPU 突然减少,从而导致 TPS 低。
- 每当我们减少并发线程时,CPU 使用率仍然很高,但服务以最佳方式执行,即高 TPS。
以下是不同CPU/线程配置下TPS vs. CPU的统计数据。
CPU:1500m,线程:70
| TPS | 176 | 140 | 125 | 79 | 63 |
|----------------------------------|
| CPU | 1052 | 405 | 201 | 84 | 13 |
CPU:1500m,线程:35
| TPS | 500 | 510 | 500 | 530 |
|-----------------------------|
| CPU | 1172| 1349| 1310| 1214|
CPU:2500m,线程:70
| TPS | 20 | 20 | 25 | 28 | 26 |
|----------------------------------|
| CPU | 2063| 2429| 2303| 879 | 35 |
CPU:2500m,线程:35
| TPS | 1193 | 1200 | 1200 | 1230 |
|---------------------------------|
| CPU | 600 | 1908 | 2044 | 1949 |
使用的 Tomcat 配置:
server.tomcat.max-connections=100
server.tomcat.max-threads=100
server.tomcat.min-spare-threads=5
EDIT-2:
线程转储分析表明:80% 的http-nio 线程处于Waiting on condition 状态。这意味着所有线程都在等待某事,没有人消耗任何 CPU 来解释低 CPU 使用率。 但是什么可能导致线程等待?我也没有在服务中使用任何异步调用。即使我没有使用任何并行流,只使用上面提到的顺序流。
以下是 CPU 和 TPS 下降时的线程转储:
"http-nio-8090-exec-72" #125 daemon prio=5 os_prio=0 tid=0x00007f014001e800 nid=0x8f waiting on condition [0x00007f0158ae1000]
java.lang.Thread.State: **TIMED_WAITING** (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000d7470b10> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:89)
at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:33)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None
【问题讨论】:
-
您的 “更高的 cpu 使用率” 与什么比较?一个 for 循环?
-
@ernest_k 我说的是其他没有流的服务。该服务与其他服务没有什么不同,我唯一使用的是流和可选项而不是 for-loop 和 if 条件。
-
你怎么知道这种行为是由流引起的?数据库和查询、连接池、消耗的外部服务等呢?
-
假设 TPS 的意思是“每秒事务数”,那么如果您看到高 TPS 速率,那么 CPU 使用率也很高。关于内存使用,它真的很大程度上取决于你的服务做什么。如果在增加线程数时 TPS 急剧下降,这可能表明这些线程正在“争夺资源”,例如锁、池化数据库连接、文件 I/O 资源或类似资源。也许您正在遇到(临时)死锁、活锁或其他类型的资源匮乏。没有MCVE,我不能说更具体的了。
-
好吧,除非 OP 澄清后来 cmets 中提出的各种观点,否则他不太可能得到除笼统之外的任何答案。好像他在浪费他的赏金点:-)
标签: java tomcat websocket threadpool deadlock