dubbo - dubbo2.7.5 dubbo admin "无元数据信息,请升级至Dubbo2.7及以上版本"问题解决
dubbo - dubbo2.7.5 dubbo admin "无元数据信息,请升级至Dubbo2.7及以上版本"问题解决
一、问题
demo使用dubbo 2.7.5版本, dubbo admin 使用develop分支最新版本(引用dubbo 2.7.3),出现以下问题:
二、原因
1. dubbo 2.7以上版本, 增加了元数据, dubbo admin 从2.6升级到2.7会出现上述问题,需要进行以下配置
意思是, 在配置中心中配置元数据地址和内容, 如zookeeper, 在节点/dubbo/config/dubbo/dubbo.properties添加数据
dubbo.registry.address=zookeeper://127.0.0.1:2181 dubbo.metadata-report.address=zookeeper://127.0.0.1:2181
项目中增加以下代码处理即可:
/** * 2.7.0版本及以上 在dubbo-admin显示元数据的配置, * * 需要注意, dubbo-admin和服务提供者引入的dubbo为同一版本才行 * * @author TimFruit * @date 20-3-3 下午11:49 */ @EnableDubbo @Configuration @Slf4j public class DubboConfig implements EnvironmentAware { private Environment env; @Override public void setEnvironment(Environment environment) { this.env=environment; } // 2.7.0 版本以上 // https://blog.csdn.net/wangxq0224/article/details/99304253 //用于fix dubbo admin : "无元数据信息,请升级至Dubbo2.7及以上版本,或者查看application.properties中关于config center的配置,详见 这里" // https://github.com/apache/dubbo-admin/wiki/Dubbo-Admin%E9%85%8D%E7%BD%AE%E8%AF%B4%E6%98%8E @PostConstruct public void postInitAdminMeta(){ final String REGISTRY_ADDRESS="dubbo.registry.address"; String registryAddress=env.getProperty(REGISTRY_ADDRESS); if(!StringUtils.hasText(registryAddress)){ log.warn(REGISTRY_ADDRESS+"属性没有配置值"); return; } if(!registryAddress.startsWith("zookeeper")){ log.info("注册中心不是zookeeper"); return; } //注册中心为zookeeper, 修复元数据问题 String data=REGISTRY_ADDRESS+"="+registryAddress; final String META_REPORT_ADDRESS="dubbo.metadata-report.address"; String reportAddress=env.getProperty(META_REPORT_ADDRESS); if(StringUtils.hasText(reportAddress)){ data=data+"\n"+META_REPORT_ADDRESS+"="+reportAddress; } log.info("\n== data: {}", data); //warn: 多个注册中心未测试 String connectString=registryAddress.replace("zookeeper://", ""); RetryPolicy retryPolicy=new RetryNTimes(2, 1000); CuratorFramework zkClient=CuratorFrameworkFactory.newClient(connectString,retryPolicy); zkClient.start(); try { String nodePath="/dubbo/config/dubbo/dubbo.properties"; if(zkClient.checkExists().forPath(nodePath)==null){ zkClient.create() .creatingParentsIfNeeded() .forPath(nodePath, data.getBytes()); }else { zkClient.setData().forPath(nodePath, data.getBytes()); } } catch (Exception e) { throw new RuntimeException(e); }finally { zkClient.close(); } }
2. 做了以上配置,仍出现这样的问题,可以检查以下dubbo-admin和 服务项目引用的dubbo版本是否相同
dubbo 2.7.0, dubbo 2.7.3, 以及dubbo 2.7.5 均对元数据相关代码做了修改,需要使用相同的版本, 才可以查询相同的元数据路径
以下是dubbo 2.7.3 版本, 在zookeeper中的元数据路径demo, dubbo 2.7.0版本的元数据路径后面还有一个节点/service.data
3. dubbo admin develop分支, 目前支持的是2.7.3版本, 如果项目使用了dubbo 2.7.5版本, 仍旧会出现该问题, 因为2.7.5版本对元数据处理做了修改
文末给出本人的解决办法
4. 在其他配置无误的情况下, 出现该问题的根本原因是, dubbo admin 和项目dubbo指定的元数据路径不一致造成的
三、dubbo 2.7.5 在dubbo admin 2.7.3 显示元数据的解决办法
1. 增加 "二、原因"中第一点的代码
2. 其他关键代码
2.1) 配置:
# Spring boot application spring.application.name=dubbo-auto-configuration-provider-demo # Base packages to scan Dubbo Component: @org.apache.dubbo.config.annotation.Service dubbo.scan.base-packages=com.ttx.dubbo.simple.provider.service # Dubbo Application ## The default value of dubbo.application.name is ${spring.application.name} dubbo.application.name=${spring.application.name} # Dubbo Protocol dubbo.protocol.name=dubbo dubbo.protocol.port=12345 ## Dubbo Registry #dubbo.registry.address=N/A embedded.zookeeper.port = 2181 ## Dubbo Registry dubbo.registry.address=zookeeper://127.0.0.1:${embedded.zookeeper.port} dubbo.registry.file = ${user.home}/dubbo-cache/${spring.application.name}/dubbo.cache ## dubbo-admin 元数据 ## 使得dubbo2.7.5可在dubbo-admin2.7.3版本的显示"元数据"的配置, 2.7.3及以下版本无须配置 dubbo.provider.parameters.metadata=myremote dubbo.metadata-report.sync-report=true dubbo.metadata-report.address=${dubbo.registry.address} dubbo.config-center.address=${dubbo.registry.address} ## 使用dubbo2.7.0版本才需要的配置, 可以去掉 spring.main.allow-bean-definition-overriding=true ## 监控 dubbo.monitor.protocol=registry ## DemoService version dubbo.provider.DemoService.version=1.0.0
2.2)
/** * dubbo 2.7.5 在dubbo-admin中显示元数据的配置 * @author TimFruit * @date 20-3-6 下午10:36 */ @Configuration @ConfigurationProperties @Data public class DubboConfigProperties { /* org.apache.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) 方法中调用WritableMetadataService metadataService = WritableMetadataService.getExtension(url.getParameter("metadata", "local")); 默认使用的是local meta服务, 即是存放在内存中 本属性自定义meta属性值, 使用自定义的meta 服务 */ // public static final String META_REPORT_META="dubbo.provider.parameters.metadata"; // public static final String META_REPORT_META="dubbo.application.parameters.metadata"; @Value("${"+META_REPORT_META+":}") private String metadata; }
2.3 ) 增加以下代码, 使得2.7.5版本可以发送元数据到配置中心
/** * dubbo 2.7.5版本存储meta元数据信息, dubbo-admin 需要使用https://github.com/apache/dubbo-admin develop开发分支的代码 * * * dubbo 2.7.0版本无须使用该配置, dubbo admin 使用https://github.com/apache/dubbo-admin/archive/0.2.0.tar.gz * https://github.com/apache/dubbo-admin/releases * * dubbo 2.7.3版本无须使用该配置, dubbo-admin 需要使用https://github.com/apache/dubbo-admin develop开发分支的代码, 其引入的dubbo版本为2.7.3 * * @see DubboConfig#postInitAdminMeta() 为显示2.7.5版本的元数据信息而配置 * * @author TimFruit * @date 20-3-6 下午8:04 */ @Configuration @Slf4j public class RemoteWritableMetadataServiceDelegateConfig { @Autowired DubboConfigProperties dubboConfigProperties; @PostConstruct public void postMetadataService(){ String meta=dubboConfigProperties.getMetadata(); if(!StringUtils.hasText(meta)){ log.info("没有使用自定义meta服务..."); return; } ExtensionLoader<WritableMetadataService> extensionLoader=ExtensionLoader.getExtensionLoader(WritableMetadataService.class); extensionLoader.addExtension(meta, MyRemoteWriteableMetadataService.class); } }
出现以下日志,表示发送元数据到zookeeper
2.4) 自定义的meta服务
/** * @author TimFruit * @date 20-3-6 下午10:32 */ public class MyRemoteWriteableMetadataService extends RemoteWritableMetadataService { public MyRemoteWriteableMetadataService() { super( (InMemoryWritableMetadataService)(ExtensionLoader.getExtensionLoader(WritableMetadataService.class).getExtension("local")) ); } @Override public void publishServiceDefinition(URL providerUrl) { // this.getMetadataReport().storeProviderMetadata(new MetadataIdentifier(providerUrl.getServiceInterface(), providerUrl.getParameter("version"), providerUrl.getParameter("group"), Constants.PROVIDER_SIDE, (String)providerUrl.getParameter("application")), serviceDefinition); try { String interfaceName = providerUrl.getParameter("interface"); if (StringUtils.isNotEmpty(interfaceName) && !ProtocolUtils.isGeneric(providerUrl.getParameter("generic"))) { Class interfaceClass = Class.forName(interfaceName); ServiceDefinition serviceDefinition = ServiceDefinitionBuilder.build(interfaceClass); //修改指定路径参数, 使用2.7.3版本的元数据路径 //this.getMetadataReport().storeProviderMetadata(new MetadataIdentifier(providerUrl.getServiceInterface(), providerUrl.getParameter("version"), providerUrl.getParameter("group"), (String)null, (String)null), serviceDefinition); this.getMetadataReport().storeProviderMetadata( new MetadataIdentifier(providerUrl.getServiceInterface(), providerUrl.getParameter("version"), providerUrl.getParameter("group"), //以下两个参数 对应与dubbo admin 项目 org.apache.dubbo.admin.controller.ServiceController#serviceDetail(@PathVariable String service, @PathVariable String env) //方法中的构造参数, 使得提供者和admin都查询zookeeper相同的元数据路径 Constants.PROVIDER_SIDE, (String)providerUrl.getParameter("application") ), serviceDefinition); return; } this.logger.error("publishProvider interfaceName is empty . providerUrl: " + providerUrl.toFullString()); } catch (ClassNotFoundException var5) { this.logger.error("publishProvider getServiceDescriptor error. providerUrl: " + providerUrl.toFullString(), var5); } this.publishProvider(providerUrl);
admin显示元数据成功的结果
四、后记
本人尚未查找到2.7.5版本解决方法资料,所以查看部分源码暂时解决。 dubbo源码十分复杂(十几万行代码), 和 spring 有的一拼。 本人尚未理解所有流程,可能会有所纰漏。如果官方或者其他出了更优雅的解决方案, 还请留言告知。
bug多数是由代码变更造成的, 愿程序猿一生无bug !T_T (在十几万行完全陌生的复杂代码找bug, 实在太痛苦了 T_T)
参考资料:
dubbo-admin使用新版本和dubbo的2.7版本发现没有元数据的原因