💖1.问题现象
dubbo
服务启动时抛出异常Unsupported generic type false
,但不影响服务正常发布。
Caused by: java.lang.IllegalArgumentException: Unsupported generic type false
📖2. 版本信息
SpringBoot 2.1.3
+ Dubbo 2.7.2
👉3. 问题根因
项目中使用了Spring Boot Actuator
模块,Spring Boot Actuator
配置独立的管理端口时会创建特殊子上下文(项目中会存在两个上下文实例),导致dubbo
对spring
上下文刷新事件监控触发两次;
同时开启了dubbo
延迟发布(dubbo.provider.delay=3000
),由于dubbo 2.7.2
中的BUG,两次均会进行了export
动作;
同时由于dubbo 2.7.2
版本中对generic
的判断问题而在第二次export
时抛出异常。
⭐4.分析过程
由
message
中可以看出:Unsupported generic type false
是dubbo
泛化调用配置错误告警
🌟4.1. 检查配置是否有误
检查全局中dubbo
配置是否存在generic="false"
<dubbo:service generic="false" />
经过检查,项目中无该问题配置
🌟4.2. 堆栈分析
通过检查未发现人为错误配置,进一步分析异常堆栈
具体堆栈如下:
level: ERRORthreadName: mainclassName: o.a.d.c.AbstractConfig - [DUBBO] Failed to override , dubbo version: 2.7.2, current host: xxx.xxx.xxx.xxx
java.lang.reflect.InvocationTargetException: nullat sun.reflect.GeneratedMethodAccessor864.invoke(Unknown Source) ~[?:?]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_412]at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_412]at org.apache.dubbo.config.AbstractConfig.refresh(AbstractConfig.java:567) ~[dubbo-2.7.2.jar!/:2.7.2]at org.apache.dubbo.config.ServiceConfig.checkAndUpdateSubConfigs(ServiceConfig.java:304) ~[dubbo-2.7.2.jar!/:2.7.2]at org.apache.dubbo.config.ServiceConfig.export(ServiceConfig.java:369) ~[dubbo-2.7.2.jar!/:2.7.2]at org.apache.dubbo.config.spring.ServiceBean.export(ServiceBean.java:336) ~[dubbo-2.7.2.jar!/:2.7.2]at org.apache.dubbo.config.spring.ServiceBean.onApplicationEvent(ServiceBean.java:114) ~[dubbo-2.7.2.jar!/:2.7.2]at org.apache.dubbo.config.spring.ServiceBean.onApplicationEvent(ServiceBean.java:60) ~[dubbo-2.7.2.jar!/:2.7.2]at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:402) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:359) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:896) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:163) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:552) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]at com.xxx.xxx.xxx.xxx.xxxApiStartUp.main(xxxApiStartUp.java:26) ~[classes!/:?]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_412]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_412]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_412]at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_412]at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) ~[xxx.jar:?]at org.springframework.boot.loader.Launcher.launch(Launcher.java:87) ~[xxx.jar:?]at org.springframework.boot.loader.Launcher.launch(Launcher.java:50) ~[xxx.jar:?]at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51) ~[xxx.jar:?]
Caused by: java.lang.IllegalArgumentException: Unsupported generic type falseat org.apache.dubbo.config.ServiceConfig.setGeneric(ServiceConfig.java:1015) ~[dubbo-2.7.2.jar!/:2.7.2]
从堆栈上可以看出在ServiceBean
这里有对spring
事件的监控
查看源码如下:
可以看出dubbo
是在spring ApplicationContext
上下文刷新时进行了发布动作export
这里引起思考:为什么实际dubbo服务没有发布失败了?
🌟4.3. 官方issues
前往github
检索官方可发现有相同的issues
https://github.com/apache/dubbo/issues/3629
🌟4.4. 源码分析
为什么export两次ServiceConfig会有问题了?
再回到dubbo
源码,可以发现在export
中有对generic
赋默认值false
的动作;
在第一次refresh
后进行赋值,在第二次refresh
时由于generic="false"
报错
然而这里并没有结束
在前面duubo serviceBean
对spring
上下文刷新事件的监控源码中可以看到,dubbo
有对重复发布的屏蔽处理动作
那为什么还能发布两次了?
继续看该发布属性的更新可知,是在serviceConfig doExport
之后更新
往上寻找调用栈可以发现:在dubbo export
时有两种发布模式:直接发布、延迟发布
看过dubbo源码的同学可以看出,直接发布模式下,应该会被发布属性拦截
可以看出异步发布模式下,第一次异步发布后,第二次上下文刷新事件监控进入时发布属性会无法拦截
前往配置中心也可以看到有相关延迟发布的配置
dubbo.provider.delay=3000
到这里我们取消了延迟发布后,重启后不再报错。
虽然表象问题解决,但根本原因是为什么会触发两次发布动作了?
看源码这里是对spring ApplicationContext
上下文刷新事件的监控,触发两次说明有两个个上下文实例。
可以写个工具检测输出下:
@Component
public class ContextRefreshCounter implements ApplicationListener<ContextRefreshedEvent> {private static int count = 0;@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {System.out.println("Context refreshed, total times: " + ++count);System.out.println("ApplicationContext instance: " + System.identityHashCode(event.getApplicationContext()));System.out.println("ApplicationContext id: " + event.getApplicationContext().getId());}
}
看到有两个上下文:application
、application:management
application
是根上下文
application:management
是Spring Boot Actuator
管理端点的子上下文
查看配置中心,发现有配置独立的管理端口
management.server.port=8081
由此可知由于引入了Spring Boot Actuator
且配置了独立的管理端口,导致生成多个上下文实例,从而触发了多次spring
上下文刷新事件的监控。
⭐5. 官方修复
后续版本官方已针对generic
值的判断以及dubbo
服务发布逻辑进行了优化,具体可前往github
官网查看。
https://github.com/apache/dubbo
结束