前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CompletableFuture调用OpenFegin,谁用谁傻

CompletableFuture调用OpenFegin,谁用谁傻

作者头像
疯狂的KK
修改2023-03-15 15:46:22
8030
修改2023-03-15 15:46:22
举报
文章被收录于专栏:Java项目实战Java项目实战

业务场景

在业务看板中,需明确各种单据状态及其数量,以便能跳转相应系统,由于看板节点较多,涉及多个外部业务系统,且都为读操作,则设计为CompletableFuture的runAsync()方法实现异步,然后将统计操作返回,原代码结构如下:

代码语言:javascript
复制

 CompletableFuture<Void> t1 = CompletableFuture.runAsync(() -> {
    response.xxx;
}, taskExecutor);
CompletableFuture<Void> t2 = CompletableFuture.runAsync(() -> {
    response.xxx;
}, taskExecutor);
CompletableFuture<Void> t3 = CompletableFuture.runAsync(() -> {
    response.xxx;
}, taskExecutor);
CompletableFuture.allOf(t1, t2, t3 ....).join();

开发环境

SpringCloud + jdk11

报错如下

代码语言:javascript
复制

java.util.concurrent.CompletionException: xxxjava.lang.reflect.InvocationTargetException
    at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314) ~[na:na]
    at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:319) ~[na:na]
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1739) ~[na:na]
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177) ~[na:na]
Caused by: xxx
    at xxx
    at xxx
    at xxx
    at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute(LoadBalancerFeignClient.java) ~[na:na]
    at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient$$FastClassBySpringCGLIB$$fb8167ef.invoke(<generated>) ~[na:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749) ~[spring-aop-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88) ~[spring-aop-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at xxx
    at jdk.internal.reflect.GeneratedMethodAccessor384.invoke(Unknown Source) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644) ~[spring-aop-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633) ~[spring-aop-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70) ~[spring-aop-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) ~[spring-aop-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient$$EnhancerBySpringCGLIB$$d0eeb310.execute(<generated>) ~[na:na]
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:108) ~[feign-core-10.1.0.jar!/:na]
    at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:78) ~[feign-core-10.1.0.jar!/:na]
    at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103) ~[feign-core-10.1.0.jar!/:na]
    at com.sun.proxy.$Proxy217.findShippedButNotReceived(Unknown Source) ~[na:na]
    at xxx
    at xxx
    at xxx
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736) ~[na:na]
    ... 6 common frames omitted
Caused by: java.lang.reflect.InvocationTargetException: null
    at jdk.internal.reflect.GeneratedMethodAccessor385.invoke(Unknown Source) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at xxx
    ... 34 common frames omitted
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration]; nested exception is java.io.FileNotFoundException: class path resource [org/springframework/boot/context/properties/EnableConfigurationPropertiesImportSelector$ConfigurationPropertiesBeanRegistrar.class] cannot be opened because it does not exist
    at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:599) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:302) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:242) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:199) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:167) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:315) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:232) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:275) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:95) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:691) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:528) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.cloud.context.named.NamedContextFactory.createContext(NamedContextFactory.java:120) ~[spring-cloud-context-2.1.0.RELEASE.jar!/:2.1.0.RELEASE]
    at org.springframework.cloud.context.named.NamedContextFactory.getContext(NamedContextFactory.java:88) ~[spring-cloud-context-2.1.0.RELEASE.jar!/:2.1.0.RELEASE]
    at org.springframework.cloud.netflix.ribbon.SpringClientFactory.getContext(SpringClientFactory.java:118) ~[spring-cloud-netflix-ribbon-2.1.0.RELEASE.jar!/:2.1.0.RELEASE]
    at org.springframework.cloud.context.named.NamedContextFactory.getInstance(NamedContextFactory.java:129) ~[spring-cloud-context-2.1.0.RELEASE.jar!/:2.1.0.RELEASE]
    at org.springframework.cloud.netflix.ribbon.SpringClientFactory.getInstance(SpringClientFactory.java:108) ~[spring-cloud-netflix-ribbon-2.1.0.RELEASE.jar!/:2.1.0.RELEASE]
    at org.springframework.cloud.netflix.ribbon.SpringClientFactory.getClientConfig(SpringClientFactory.java:65) ~[spring-cloud-netflix-ribbon-2.1.0.RELEASE.jar!/:2.1.0.RELEASE]
    at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.getClientConfig(LoadBalancerFeignClient.java:80) ~[na:na]
    at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.executeOld(LoadBalancerFeignClient.java:64) ~[na:na]
    ... 38 common frames omitted
Caused by: java.io.FileNotFoundException: class path resource [org/springframework/boot/context/properties/EnableConfigurationPropertiesImportSelector$ConfigurationPropertiesBeanRegistrar.class] cannot be opened because it does not exist
    at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:180) ~[spring-core-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:51) ~[spring-core-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:103) ~[spring-core-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.core.type.classreading.CachingMetadataReaderFactory.getMetadataReader(CachingMetadataReaderFactory.java:123) ~[spring-core-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:81) ~[spring-core-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassParser.asSourceClass(ConfigurationClassParser.java:685) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassParser.asSourceClasses(ConfigurationClassParser.java:664) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:570) ~[spring-context-5.1.4.RELEASE.jar!/:5.1.4.RELEASE]
    ... 56 common frames omitted

问题排查过程

首先这个接口并不是新开接口,原业务增加逻辑而已,但是个人测试业务很简单,所以早已上线,近期版本也由于业务更替较大,升级了版本,而且这个接口已经在测试环境测试过了,怎么会换个环境就报错了呢?第一反应是不是老版本冲突,但自己调用rpc后正常响应,而且如果是我这边的问题,报错不是这样的应该是诸如feign.FeignException: status 500 readingxxxxx才对,但还是重启了对应服务,但重启后仍旧报错

排查代码层面,确认无误后,排查上游,第一时间并未怀疑是Forkjoin的问题,双方重新打包,构建后仍旧失败,随着问题越来越焦灼,测试下普通调用是否正常,遂把异步调用改为同步,重启后成功。

问题原因

在搜索后github上的issues看到了此问题

本地环境可以,但是Linux环境不行

但使用线程池后即可解决此问题,两个方法区别如下,如果不指定线程池则使用系统级别的

代码语言:javascript
复制


    /**
     * Returns a new CompletableFuture that is asynchronously completed
     * by a task running in the {@link ForkJoinPool#commonPool()} after
     * it runs the given action.
     *
     * @param runnable the action to run before completing the
     * returned CompletableFuture
     * @return the new CompletableFuture
     */
    public static CompletableFuture<Void> runAsync(Runnable runnable) {
        return asyncRunStage(ASYNC_POOL, runnable);
    }

    /**
     * Returns a new CompletableFuture that is asynchronously completed
     * by a task running in the given executor after it runs the given
     * action.
     *
     * @param runnable the action to run before completing the
     * returned CompletableFuture
     * @param executor the executor to use for asynchronous execution
     * @return the new CompletableFuture
     */
    public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                   Executor executor) {
        return asyncRunStage(screenExecutor(executor), runnable);
    }

当调用FeignBlockdingloadBalancerClient使用并行流(多线程)会有问题。

问题结论

当使用boot build 打包的镜像在docker环境运行时,使用CompletableFuture的runAsync()调用会出现此问题,或者当标记@Async异步时也会出现,但是jdk8中是好的,jdk11会出现此问题,原因是Fegin接口是懒加载的,只有在我们第一次使用该Fegin接口的时候才会对Fegin接口进行初始化,但是如果在ForkJoinWorkerThread中使用Fegin接口的话,就会出现ClassNotFoundException。,既然是ClassNotfound,必然是ClassLoader相关引起的,随后在修复方案中也验证了,此时的classLoader在默认线程中TCCL是找不到Spring管理的加载器的,为null时应在ApplicationInitializer中设置为默认加载器,使用其他普通的JVM线程池是不会出现问题的。这个解释其实并不是很形象,或者很明确,因为恰好是符合这个场景,并且不是直接解决,而是排查解决的,虽然但是,觉得并不是很好的回答,细节太少,等在往期补充。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 赵KK日常技术记录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云 BI
腾讯云 BI(Business Intelligence,BI)提供从数据源接入、数据建模到数据可视化分析全流程的BI能力,帮助经营者快速获取决策数据依据。系统采用敏捷自助式设计,使用者仅需通过简单拖拽即可完成原本复杂的报表开发过程,并支持报表的分享、推送等企业协作场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档