Kubernetes中Spring Boot微服务CPU占用过高问题排查求助
微服务CPU持续100%问题排查需求
环境与问题现象
我们在Kubernetes中部署了约60个微服务,其中两个运行一段时间后出现CPU占用率跳升至100%并持续维持的问题(Pod分配1核新一代AMD EPYC CPU)。问题随机出现在应用启动到人工重启/删除Pod的任意阶段。
这些微服务基于以下技术栈构建:
- Spring Boot 2.7.8
- Undertow(替代Tomcat)
- Retrofit2 2.9.0 + OkHttp3 3.14.9
- Project Reactor
异常线程转储
捕获到的线程转储显示存在长期存活的异常线程,每次转储时该线程均处于类似状态,存活时长几乎与微服务运行时长一致,栈迹偶尔变化:
"getProductVariants-260" #354 prio=5 os_prio=0 cpu=678915664.69ms elapsed=687202.16s tid=0x00007f63d800a800 nid=0x170 runnable [0x00007f643ccec000] java.lang.Thread.State: RUNNABLE at java.lang.Throwable.fillInStackTrace(java.base@11.0.17/Native Method) at java.lang.Throwable.fillInStackTrace(java.base@11.0.17/Throwable.java:787) - locked <0x00000000f4132990> (a okhttp3.internal.connection.RouteException) at java.lang.Throwable.<init>(java.base@11.0.17/Throwable.java:315) at java.lang.Exception.<init>(java.base@11.0.17/Exception.java:102) at java.lang.RuntimeException.<init>(java.base@11.0.17/RuntimeException.java:96) at okhttp3.internal.connection.RouteException.<init>(RouteException.java:31) at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.java:96) at okhttp3.internal.connection.Transmitter.newExchange(Transmitter.java:169) at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:41) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:94) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:88) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at de.md.services.microframework.http.RetryInterceptor.intercept(RetryInterceptor.java:22) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at de.md.services.microframework.http.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:98) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at de.md.services.microframework.http.HeaderInterceptor.intercept(HeaderInterceptor.java:32) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at de.md.services.microframework.http.RetrofitClient.lambda$buildOkHttpClient$0(RetrofitClient.java:166) at de.md.services.microframework.http.RetrofitClient$$Lambda$1044/0x0000000100959c40.intercept(Unknown Source) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:229) at okhttp3.RealCall.execute(RealCall.java:81) at retrofit2.OkHttpCall.execute(OkHttpCall.java:204) at com.jakewharton.retrofit2.adapter.reactor.ExecuteSinkConsumer.accept(ExecuteSinkConsumer.java:39) at com.jakewharton.retrofit2.adapter.reactor.ExecuteSinkConsumer.accept(ExecuteSinkConsumer.java:24) at reactor.core.publisher.FluxCreate.subscribe(FluxCreate.java:95) at reactor.core.publisher.FluxSubscribeOn$SubscribeOnSubscriber.run(FluxSubscribeOn.java:194) at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:84) at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:37) at java.util.concurrent.FutureTask.run(java.base@11.0.17/FutureTask.java:264) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(java.base@11.0.17/ScheduledThreadPoolExecutor.java:304) at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@11.0.17/ThreadPoolExecutor.java:1128) at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@11.0.17/ThreadPoolExecutor.java:628) at java.lang.Thread.run(java.base@11.0.17/Thread.java:829)
相关业务代码
涉及的getProductVariants方法实现如下:
public Mono<ProductVariantList> getProductVariants(String productVariantIds, HttpServletRequest httpServletRequest) { ProductConnector productConnector = retrofitClient.buildRetrofitClientReactorDefault( connectorConfig.getProductService(), connectorConfig.getDefaultTimeout(), getProductVariantsScheduler, httpServletRequest).create(ProductConnector.class); return productConnector.getProductVariants(productVariantIds, connectorConfig.getSlapToken()) // .doOnNext(res -> ThirdPartyUtils.checkRetrofitResponse(res, LOGGER, "ProductService")) .flatMap(MonoHelper::toResponseContentMono) // .doOnError(error -> LOGGER.error("getProductVariants ist auf einen Fehler gelaufen.", error)); }
补充配置信息:
- 默认请求超时10秒
getProductVariantsScheduler为Project Reactor调度器,线程上限40,队列上限100000- 该请求仅从第三方微服务获取小于150KB的数据
- 服务请求量约10-20次/分钟,部署两个实例
- 内存消耗及内存转储均无异常
排查思路
- OkHttp版本BUG验证:OkHttp 3.14.9存在连接池相关的高CPU/死循环已知问题,结合线程栈卡在
RouteException创建环节,优先升级到3.14.x最新补丁版(如3.14.12),确认是否解决问题。 - 自定义RetryInterceptor逻辑检查:检查自定义重试拦截器是否存在无限循环——比如未设置重试次数上限,或错误码判断逻辑导致持续重试,进而触发OkHttp内部异常处理的循环消耗。
- Reactor调度器配置校验:验证
getProductVariantsScheduler的任务超时、拒绝策略是否生效,是否存在任务堆积导致线程长期占用CPU;确认subscribeOn的使用是否正确,避免线程泄漏。 - OkHttp连接池配置优化:检查连接池的
maxIdleConnections、keepAliveDuration配置,是否存在闲置连接未回收导致的线程泄漏;尝试调整连接池参数,观察CPU占用变化。 - 异常栈轨迹填充优化:线程栈显示卡在
Throwable.fillInStackTrace(Native方法),若频繁触发会导致高CPU。排查RouteException高频抛出的根源(如网络波动、DNS异常),或通过升级OkHttp减少不必要的栈轨迹填充。 - 超时逻辑有效性验证:确认OkHttp的超时配置是否正确传递到请求,检查自定义拦截器是否覆盖了超时设置;验证Reactor适配器的超时与OkHttp超时是否存在冲突,确保超时能正常触发终止请求。
- AMD EPYC架构适配检查:JDK 11.0.17在AMD EPYC架构下可能存在性能异常,尝试升级到11.0.x最新补丁版,或调整JVM参数(如GC策略、线程数)优化CPU使用。
内容的提问来源于stack exchange,提问作者Marvin
相关产品推荐
相关产品推荐

