dubbo3.x集成dubbo-api-docs文档

背景,2022.4.13,当前dubbo-spring-boot-starter版本是3.0.7, dubbo-api-docs-core版本在2.7.8.3
2023.4.8 由于springboot2.7 ,springboot3调整了自动配置规则 ,dubbo-spring-boot-starter需要升级到3.2.0+版本

由于dubbo默认采用dubbo协议非http方式调用,项目中不利于把接口元数据信息暴露并呈现在类swagger的文档管理界面上,当前springboot项目中有效的方式是在服务端maven上引用dubbo-api-docs-core和dubbo-api-docs-annotations,将现在dubbo服务API元数据暴露给大在dubbo-admin中进行查看.

dubbo-admin下载地址
在这里插入图片描述
基于目前dubbo3.x与dubbo-api-docs-core2.7.8.3版本有冲突,dubbo-api-docs-core内部依赖dubbo2.7.8首先要解决依赖冲突.

       <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>3.0.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-api-docs-annotations</artifactId>
            <version>2.7.8.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-api-docs-core</artifactId>
            <version>2.7.8.3</version>
            <!-- 排除冲突 -->
            <exclusions>
                <exclusion>
                    <artifactId>dubbo</artifactId>
                    <groupId>org.apache.dubbo</groupId>
                </exclusion>
            </exclusions>
        </dependency>

同样如果注册中心采用nacos,也要解决依赖冲突

	   <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>2.0.4</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.alibaba.nacos</groupId>
                    <artifactId>nacos-client</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.alibaba.nacos</groupId>
                    <artifactId>nacos-client</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

服务端添加注解


@ApiModule(value = "测试接口服务", apiInterface = UserService.class) //必要 api-doc会查找所有带有@ApiModule的bean对象
@DubboService(version = "1.0")  //需要@DubboService或@Service 注解有效
public class UserServiceImpl implements UserService {
    
    @ApiDoc(value = "测试查询接口",  description = "通过id查用户", responseClassDescription = "A quick star response bean") // 必要 扫描@ApiDoc注解的方法作为API
    @Override
    public SysUserDto getUserById(String userId) {

        SysUserEntity entity = sysUserDataService.getById(userId);
        SysUserDto result = BeanHelper.copyProperties(entity, SysUserDto::new);
        return result;
    }
 }

服务端application.yml或bootstrap.yml中添加配置

dubbo:
  registry:
    address: nacos://localhost:8848
  protocol:
    name: dubbo
    port: 20881
  consumer:
    check: false
    retries: 1 #超时后不管前面是否完成,重试一次,可能会造成重复调用
    timeout: 10000 #10秒内超时
  metadata-report: #添加汇报元数据信息
    address: nacos://localhost:8848

在启动时报错提示,如下

Description:

Field application in org.apache.dubbo.apidocs.core.DubboApiDocsAnnotationScanner required a bean of type 'org.apache.dubbo.config.ApplicationConfig' that could not be found.

The injection point has the following annotations:
	- @org.springframework.beans.factory.annotation.Autowired(required=true)

Action:

Consider defining a bean of type 'org.apache.dubbo.config.ApplicationConfig' in your configuration.

分析原因是DubboApiDocsAnnotationScanner这个类需要依赖ApplicationConfig ,RegistryConfig,ProtocolConfig 注入的bean,但是dubbo3在前面未在beanFactory中创建这3个bean对象。

@Import({DubboDocProviderImpl.class})
public class DubboApiDocsAnnotationScanner implements ApplicationListener<ApplicationReadyEvent> {
	...
    @Autowired
    private ApplicationConfig application;

    @Autowired
    private RegistryConfig registry;

    @Autowired
    private ProtocolConfig protocol;

private ParamBean processHtmlType(Class<?> classType, RequestParam annotation, ParamBean param) {
....
    /**
     * export dubbo service for dubbo doc
     */
    private <I, T> void exportDubboService(Class<I> serviceClass, T serviceImplInstance, boolean async) {
        ServiceConfig<T> service = new ServiceConfig<>();
        service.setApplication(application); #这里有用
        service.setRegistry(registry);  #这里有用
        service.setProtocol(protocol); #这里有用
        service.setInterface(serviceClass);
        service.setRef(serviceImplInstance);
        service.setAsync(async);
//        service.setVersion("1.0.0");
        service.export(); #内部
    }

 ...省略

//以下为扫描具体实现方法
    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        // Register dubbo doc provider
        IDubboDocProvider dubboDocProvider = applicationContext.getBean(IDubboDocProvider.class);
        exportDubboService(IDubboDocProvider.class, dubboDocProvider, false);

        LOG.info("================= Dubbo API Docs--Start scanning and processing doc annotations ================");

        Map<String, Object> apiModules = applicationContext.getBeansWithAnnotation(ApiModule.class); //从带@ApiModule的BEAN中查找 
        apiModules.forEach((key, apiModuleTemp) -> {
            Class<?> apiModuleClass;
            if (AopUtils.isAopProxy(apiModuleTemp)) {
                apiModuleClass = AopUtils.getTargetClass(apiModuleTemp);
            } else {
                apiModuleClass = apiModuleTemp.getClass();
            }
            ApiModule moduleAnn = apiModuleClass.getAnnotation(ApiModule.class);
            //必须是@Service或@DubboService
            if (!apiModuleClass.isAnnotationPresent(Service.class) && !apiModuleClass.isAnnotationPresent(DubboService.class)) {
                LOG.warn("【Warning】" + apiModuleClass.getName() + " @ApiModule annotation is used, " +
                        "but it is not a dubbo provider (without " + Service.class.getName() + " or " +
                        DubboService.class.getName() + " annotation)");
                return;
            }
            boolean async;
            String apiVersion;
            String apiGroup;
            if (apiModuleClass.isAnnotationPresent(Service.class)) {
                Service dubboService = apiModuleClass.getAnnotation(Service.class);
                async = dubboService.async();
                apiVersion = dubboService.version();
                apiGroup = dubboService.group();
            } else {
                DubboService dubboService = apiModuleClass.getAnnotation(DubboService.class);
                async = dubboService.async();
                apiVersion = dubboService.version();
                apiGroup = dubboService.group();
            }

            // API version&group safe guard!
            String version = getSupplierValueIfAbsent(apiVersion, () -> ofNullable(providerConfig).map(ProviderConfig::getVersion).orElse(""));
            String group = getSupplierValueIfAbsent(apiGroup, () -> ofNullable(providerConfig).map(ProviderConfig::getGroup).orElse(""));

            apiVersion = applicationContext.getEnvironment().resolvePlaceholders(version);
            apiGroup = applicationContext.getEnvironment().resolvePlaceholders(group);

            ModuleCacheItem moduleCacheItem = new ModuleCacheItem();
            DubboApiDocsCache.addApiModule(moduleAnn.apiInterface().getCanonicalName(), moduleCacheItem);
            //module name
            moduleCacheItem.setModuleDocName(moduleAnn.value());
            //interface name containing package path
            moduleCacheItem.setModuleClassName(moduleAnn.apiInterface().getCanonicalName());
            //module version
            moduleCacheItem.setModuleVersion(apiVersion);
            //module group
            moduleCacheItem.setModuleGroup(apiGroup);

            Method[] apiModuleMethods = apiModuleClass.getMethods();
            // API basic information list in module cache
            List<ApiCacheItem> moduleApiList = new ArrayList<>(apiModuleMethods.length);
            moduleCacheItem.setModuleApiList(moduleApiList);
            for (Method method : apiModuleMethods) {
            //从带@ApiDoc的方法分析API
                if (method.isAnnotationPresent(ApiDoc.class)) {
                    processApiDocAnnotation(method, moduleApiList, moduleAnn, async, moduleCacheItem, apiVersion, apiGroup);
                }
            }
        });
        LOG.info("================= Dubbo API Docs-- doc annotations scanning and processing completed ================");
    }

因此只需要添加配置类启用文档并注入bean. 如果引入 dubbo-spring-boot-autoconfigure,会从配置文件中读取配置创建DubboConfigurationProperties对象。这里就可以利用。 可以新建一个配置类如下


@Profile("dev")
@EnableDubboApiDocs
@Configuration
public class DubboDocConfig {

    @Autowired
    DubboConfigurationProperties properties;

    @ConditionalOnMissingBean
    @Bean
    ApplicationConfig application() {

        if (properties != null && properties.getApplication() != null) {
            return properties.getApplication();
        }
        return new ApplicationConfig(); //当前应用配置信息 ,如服务名
    }

    @Bean
    @ConditionalOnMissingBean
    RegistryConfig registry() {
        if (properties != null && properties.getRegistry() != null) {
            return properties.getRegistry();
        }
        RegistryConfig registryConfig = new RegistryConfig();//注册中心配置
//        registryConfig.setAddress("nacos://localhost:8848");
//        registryConfig.setUsername("nacos");
//        registryConfig.setPassword("nacos");
        return registryConfig;
    }

    @Bean
    @ConditionalOnMissingBean
    ProtocolConfig protocol() {
        if (properties != null && properties.getProtocol() != null) {
            return properties.getProtocol();
        }
        return new ProtocolConfig();// 服务传输协议的配置
    }
}

再启动服务,不报错,服务启动完成。
接下来配置一下dubbo-admin 的配置,在config下配置application.properties 注册中心地址,访问端口

admin.registry.address=nacos://127.0.0.1:8848
admin.config-center=nacos://127.0.0.1:8848?group=dubbo
admin.metadata-report.address=nacos://127.0.0.1:8848

server.port=8028 //访问端口

在这里插入图片描述

启动dubbo-admin.bin目录中startup.cmd,访问localhost:8028(自己在配置中修改), 默认登录 root root 就能查看了
在这里插入图片描述

点此参考完整源码

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
根据您提供的引用内容,配置`dubbo.protocols.dubbo.port=-1`可以开启Dubbo端口的随机分配功能。这样配置后,Dubbo将自动选择一个可用的端口进行通信。 另外,要查看Dubbo进程的端口,可以使用`lsof -i | grep dubbo进程ID | grep LISTEN`命令。该命令会列出所有使用Dubbo协议的进程的信息,包括监听的端口。 关于Dubbo管理平台,您可以下载dubbo-admin.2.5.6.war包并部署自己的服务器上。您提到有一些免积分下载的方法,但是目前最低要求是2个积分。 最后,针对您提到的问题描述`Failed to bind properties under 'spring.dubbo.application' to com.alibaba.dubbo.config.ApplicationConfig: Property: spring.dubbo.application.logger Value: Slf4j Origin: class path resource [application.properties]:17:33 Reason: No such extension com.alibaba.dubbo.common.logger.LoggerAdapter by name Slf4j`,根据这个描述,看起来是在应用配置中使用了`spring.dubbo.application.logger`这个属性,但是`com.alibaba.dubbo.common.logger.LoggerAdapter`扩展中没有名为`Slf4j`的实现[3]。这可能是由于配置文件中的错误或者版本不匹配导致的。您可以检查配置文件中的属性是否正确,或者尝试更新Dubbo的版本来解决该问题。 总结起来,配置`dubbo.protocols.dubbo.port=-1`可以开启Dubbo端口的随机分配功能。您可以使用命令`lsof -i | grep dubbo进程ID | grep LISTEN`来查看Dubbo进程的端口。另外,您可以下载dubbo-admin.2.5.6.war包并部署自己的服务器上来使用Dubbo管理平台。对于问题描述中的错误,建议检查配置文件中的属性是否正确或者尝试更新Dubbo的版本。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值