springboot dubbo配置不识别yml端口配置
问题:springboot dubbo 自定义了端口,但还是默认启用zookeeper的默认端口 20880,在启动一个的时候就发现20880 端口被占用
先贴上解决方案:springboot dubbo版本的问题,参数值必须使用字符串,而不是数字
或者使用 application.properties
以 properties
的方式进行配置
问题:在springboot yml中配置了dubbo端口,但未生效,但其它参数生效
yml配置
dubbo:
application:
name: test-dubbo
registry:
protocol: zookeeper
address: 127.0.0.1:2181
protocol:
name: dubbo
port: 12345
使用的版本
spring-boot 1.5.21 版本
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.21.RELEASE</version>
<!-- <version>2.5.2</version>-->
<relativePath/>
</parent>
dubbo-spring-boot-starter 2.0版本
<dependency>
<groupId>com.alibaba.spring.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
zookeeper 3.4.11
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.11</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
问题比较怪异,如果我把 dubbo.application.name
去掉,那启动会报错,没有设置application
的name
名称,那说明有加载这个配置
我在把dubbo.registry.address
删掉,也会报错,说明也加载了该配置
但是 dubbo.protocol.port
去掉、增加都没影响,就很奇怪
后来初步尝试,在springboot的resources 中增加 dubbo.properties
配置,里面就一个配置
#配置dubbo启动端口
dubbo.protocol.port=12345
发现可以按12345
的端口启动 ,唯独不能再yml中设置成功
初步排除
方式一:源代码调试装配情况
后来看源码查看,难道是以前版本prot 属性不对?不是dubbo.protocol.prot 的方式?那为什么dubbo.properties
又可以,难道不应该是数值 应该是字符串? 找到源代码验证下
找装配的时候怎么赋值的 找到 DubboConfigBindingBeanPostProcessor
的 postProcessBeforeInitialization
其中这里也有个port 不要误解了,首先得了解 dubbo最开始的方式 xml
<!-- 注册中心配置 -->
<!-- 这个端口不是必须的,只有当你的 ZooKeeper 服务器配置在非默认端口上时,你才需要指定 port-->
<dubbo:registry protocol="zookeeper" address="zookeeper://127.0.0.1" port="2888"/>
<!--通常这样配置-->
<dubbo:registry protocol="zookeeper" address="zookeeper://127.0.0.1:2888"/>
<!-- 协议配置 port 也不是必须的,-1 表示自动递增,避免影响端口用情况 ,20880为默认端口-->
<dubbo:protocol name="dubbo" port="20880"/>
所以 RegistryConfig
并不是我们要找的对象,这个是注册中心的相关配置,端口是注册中心的端口
在继续找,我们要找的是 ProtocolConfig
中的 port,ProtocolConfig
中 port 也是Integer 类型的
继续,这里只读取到 protocol 的 name属性,没有port ,从这一步就没有读到了
方式二:日志查看装配情况
上面这个方法太笨了,通过上面源代码方式查看,如果开启了日志会打印日子
我们查看日志分析下也可以得出结论,没有装配上
没有装配上使用了默认的端口 20880,在继续往上找,看那个步骤读取了配置
排查可疑点:查询装配情况(结论:未在此,但不会调试的可以看看)
为了更好的看日志情况,把logging 改为DEBUG 模式,打印尽可能详尽的日志
logging:
level:
org.springframework: DEBUG
先启动,然后查看日志 筛选刚才我们找到的 DubboConfigBindingBeanPostProcessor
类 在看上面打印的日子,有没有可疑的地方
看到MutablePropertySources
添加了 application.yml 配置文件
那我们找下 idea按两下shift,查找 MutablePropertySources,然后在搜索 Adding PropertySource
可以看到配置文件中,读取的话 是读取到了这个20897的端口的,这个地方没问题
那继续往下看日志,看到附近代码,创建
Creating shared instance of singleton bean 'com.alibaba.dubbo.config.spring.ServiceBean#0'
然后在调用了ServiceBean 里面的 afterPropertiesSet 方法
Invoking afterPropertiesSet() on bean with name 'com.alibaba.dubbo.config.spring.ServiceBean#0'
我们看下这个afterPropertiesSe方法做了什么
同样搜索ServiceBean
因为我们是dubbo上有问题,我们先排查dubbo的配置问题,所以打开第一个com.alibab:dubbo的 ServiceBean
然后发现,就是这里有问题,鸡冻啊
关键代码找到了
Map<String, ProtocolConfig> protocolConfigMap = this.applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(this.applicationContext, ProtocolConfig.class, false, false);
进去BeanFactoryUtils.beansOfTypeIncludingAncestors
看下
public static <T> Map<String, T> beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
Map<String, T> result = new LinkedHashMap(4);
result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit));
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory)lbf;
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
Map<String, T> parentResult = beansOfTypeIncludingAncestors((ListableBeanFactory)hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
Iterator var7 = parentResult.entrySet().iterator();
while(var7.hasNext()) {
Map.Entry<String, T> entry = (Map.Entry)var7.next();
String beanName = (String)entry.getKey();
if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) {
result.put(beanName, entry.getValue());
}
}
}
}
return result;
}
重启后打断点在这,会有很多断点进来,只认准这个,其他的都释放跳过(其他断点放开,只留上面ServiceBean 断点进去后改逐步调试)进入 BeanFactoryUtils
的 beansOfTypeIncludingAncestors
找到 进入ServiceBean 断点后
在这打个断点result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit));
再进入断点 getBeansOfType
怎么获取result的
Step Over :我暂且叫逐行调试吧
Step into :我暂且叫步调试吧
到这步的时候用逐步调试,会是这样的美景如getBeansOfType
在往下逐行跳过第一个,然后逐步跳过getBeanFactory
会进入getBeansOfType
在getBeansOfType中,先获取对应className名称,然后在给该对象赋值,在result.put方法中打个断点,我们看下这个getBean做了什么
进入doGetBean 方法
在找到关键代码
Object sharedInstance = this.getSingleton(beanName);
在逐步调试,看 getSingleton
做了什么
很遗憾,这里只是装配了下bean,并没有赋值的地方
这个地方只负责获取转载好的bean,这里只是根据className 获取对应属性内容
结论其实很早就给出了,最开始:我们看下这个afterPropertiesSe方法做了什么?
这一步骤 是设置属性后的意思,这给我蠢的
再次找问题所在
根据上2次经验,获取yml配置没问题,但设置属性后就没这个端口了,那出现意外就在这中间了,在找下日志吧
我们在debug日志中直接搜索,ProtocolConfig ,因为按上次拍错来看,是有创建这个对象的,先看下
尴尬:由于日志太多,被刷掉了,看日志文件吧
先配置下日志文件记录吧
logging:
level:
org.springframework: DEBUG
file:
#重点代码,再这里记录日志
name: logs/test.log
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd} [%thread] %-5level %logger{35} - %msg%n"
encoder:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd} [%thread] %-5level %logger{35} - %msg%n"
rollingpolicy:
file:
# max-history: 7
total-size-cap: 1GB
clean-history-on-start: true
然后再启动,项目所在文件夹下有个logs/test.log文件,再搜索ProtocolConfig
好家伙 找到了,是这个打的日志,DubboConfigBindingRegistrar,盘它
有2个,选择一样的就好了,选第一个,第二个明显多了个s
根据日志提示:The dubbo config bean definition,那就搜索这个
这个和日志打印结果一致
但是吧,根据前车之鉴,我们定位下这个属于哪个包下面的
发现是com.alibab 2.6 是我们在用的版本,类名 DubboConfigBindingRegistrar
大概意思也是 Dubbo配置绑定注册器
所以这个时候千万别客气,给他掐断,再重启
这个我们就过,我们要的是 com.alibaba.dubbo.config.ProtocolConfig
逮着了
然后在这打断点,registerBeanDefinition
注册Bean定义 ,然后逐步断点
前面是一些断言,我们看下面的代码比较符合
逐行调试,最后返回了上面
这个逐行结束后,日志打印了,但跳到了上面的代码中
原来是上面代码中调用了registerDubboConfigBean
方法,这个方法结束后,要继续运行接下来的代码,所以返回到了上面,那也就是上面那个方法 registerDubboConfigBeans
是先执行的,那我们在打断点在此处,且看到了熟悉的 PropertySources
这里是获取系统参数配置的地方,看来接近真相了
同样的,我们只要 com.alibaba.dubbo.config.ProtocolConfig 类,其他的都放行,看下有哪些配置
applicationConfigurationProperties ,这个熟悉吧,应用程序配置属性
接下来就是怎么去获取参数了,下面这个关键代码才是,通过属性名称 去匹配 prefix
传入进来的属性名称,获取对应的参数值
逐步进入 PropertySourcesUtils.getSubProperties
这里大概就是从上面PropertySources
所有属性中 匹配 dubbo.protocol 配置
获取参数过程
获取dubbo.protocol.name 的值
过分了,这里判断是否是字符串,是字符串才加入进去,不是的话就不配值!!!!
按道理,里面还会又个端口配置,下一个遍历,可以获取到我们配置的自定义端口 20897端口
但是,它是integer 数值类型的,所以被无情的抛弃了
是的,非字符串不配!!!
原因找到了,那我们就改为字符串吧,看来所有关于dubbo的数字类的都要是字符串,不然没办法加入到配置中,
#目前版本 数字参数一定要用字符串格式,不能直接用数字,否则装配补上
dubbo:
application:
name: test-dubbo
registry:
protocol: zookeeper
address: 127.0.0.1:2181
protocol:
name: dubbo
#圈重点:该属性值要为字符串,后面的也要,后面贴图
port: "20897"
provider:
#group: "biao"
timeout: "10000"
threadpool: fixed
threads: "100"
accepts: "1000"
consumer:
timeout: "10000" # 消费者超时时间,单位:毫秒
retries: "0" # 默认重试都为0次
check: false # 不检查消费服务有没有启用
看来是com.alibab.dubbo 2.6.0的版本问题
看下,是dubbo-spring-boot-starter 2.0.0 版本里自带的