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版本發現沒有元數據的原因