dubbo

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

  dubbo 相关ISSUE

  

  项目中增加以下代码处理即可:

  

/**
 * 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+"
"+META_REPORT_ADDRESS+"="+reportAddress;
        }

        log.info("
== 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显示元数据成功的结果

  完整demo代码

四、后记

   本人尚未查找到2.7.5版本解决方法资料,所以查看部分源码暂时解决。 dubbo源码十分复杂(十几万行代码), 和 spring 有的一拼。 本人尚未理解所有流程,可能会有所纰漏。如果官方或者其他出了更优雅的解决方案, 还请留言告知。

  bug多数是由代码变更造成的, 愿程序猿一生无bug !T_T  (在十几万行完全陌生的复杂代码找bug, 实在太痛苦了 T_T)

参考资料:

  dubbo-admin使用新版本和dubbo的2.7版本发现没有元数据的原因

人生没有彩排,每一天都是现场直播
原文地址:https://www.cnblogs.com/timfruit/p/12433931.html