springboot+dubbo整合swagger-ui+swagger-dubbo

场景

在项目开发中我们对dubbo接口通常可以通过junit编写单页测试来进行自测,配合spring-boot-starter-test,通常是如下方式:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = XxxApplication.class)
public class XxxServiceTest {
    
    @Autowired
    private XxxService xxxService;

    @Test    
    public void testFunc() throws Exception {
        xxxService.func();
    }
}

这种方式测试单个接口很方便,但有个不足是每次测试单个接口,基本上相当于重启了项目,当项目很大时运行可能比较耗时。
注:可通过执行整个测试类、或在structure里选中多个方法批量跑单元测试;或者在/src/test/resources/下单独定义测试需要的配置,这样能加快启动时间。

于是想能否项目只启动一次,然后调各个接口测试。
一个springboot项目启动好,当定义了controller时,我们可以通过浏览器或者postman这样的http工具调用接口,
那没有controller接口,只暴露了dubbo服务能否直接调用呢?
答案是肯定的,dubbo从很早的版本2.0.5开始就支持通过telnet命令来进行服务治理。

参考官方文档:http://dubbo.apache.org/zh/docs/v2.7/user/references/telnet/
比如项目的dubbo配置:

<dubbo:protocol name="dubbo" port="20001" threadpool="fixed" threads="200" />

dubbo端口是10001,通过telnet命令连接:
telnet localhost 20001

连接后,常用命令:
status -l:查看服务状态,包括服务状态是否OK、核心线程数、在运行线程数等。

ls:查看接口类列表。

ls -l com.cdfive.xxx.service.XxxService:查看某接口类下所有接口方法。

invoke com.cdfive.xxx.service.XxxService.func({"name":"111","class":"xxx""}:调用某接口方法。

invoke命令让我们可以调用想测试的某个接口。
这种方式解决了的上面接口测试的问题,即项目启动1次,然后调想任意想测试的接口。
但它仍有不足是:操作步骤较繁琐,要先telnet连接,然后invoke调接口,而telnet窗口不像shell里有方便的自动完成功能,
并且接口方法和参数需要提前准备好。

如果有可视化的方式,当启动好项目能在界面上直接调dubbo接口就更方便了。

swagger和swagger-ui

官方:https://github.com/springfox/springfox/tree/master/springfox-swagger-ui

swagger是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
在项目中使用swagger提供的@Api@ApiOperation@ApiModel@ApiModelProperty等一系列注解标识在具体的接口和参数上,
然后通过swagger-ui提供的web界面,查看接口文档、测试接口调用。

集成步骤:

  1. 引入swaggerswagger-ui依赖
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.7.0</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.7.0</version>
</dependency>
  1. 创建swagger配置类
/**
 * @author cdfive
 */
@ConditionalOnProperty(name = "swagger.enable", matchIfMissing = false)
@EnableSwagger2
@Configuration
public class SwaggerConfig {

    @Bean
    public Docket docket() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.cdfive.xxx.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("xxx-service")
                .description("xxx-service description")
                .version("0.0.1")
                .build();
    }
}

注:swagger.enable用于区分不同环境(如:开发、测试、生产),在生产环境需要禁用swagger。

  1. 在controller类的接口方法使用swagger注解

如:

@ApiOperation(value="修改用户密码", notes="根据用户id修改密码")
@ApiImplicitParams({
    @ApiImplicitParam(paramType="query", name = "userId", value = "用户id", required = true, dataType = "Integer"),
    @ApiImplicitParam(paramType="query", name = "password", value = "旧密码", required = true, dataType = "String"),
    @ApiImplicitParam(paramType="query", name = "newPassword", value = "新密码", required = true, dataType = "String")
})
@RequestMapping("/modifyPassword")
public void modifyPassword(@RequestParam(value="userId") Integer userId
        , @RequestParam(value="password") String password, 
        @RequestParam(value="newPassword") String newPassword) {
     ...
}

启动项目,访问http://ip:port/swagger-ui.html查看swagger-ui的主页面。

swagger-dubbo

官方:https://github.com/Sayi/swagger-dubbo/

swagger-dubbo解析项目中的dubbo接口,基于swagger规范生成文档,然后我们可通过swagger-ui直接调用dubbo接口。

集成步骤:

  1. 引入swagger-dubbo依赖
<dependency>
    <groupId>com.deepoove</groupId>
    <artifactId>swagger-dubbo</artifactId>
    <version>2.0.1</version>
</dependency>
  1. 创建swagger-dubbo配置类
@ConditionalOnProperty(name = "swagger.dubbo.enable", matchIfMissing = false)
@EnableDubboSwagger
@Configuration
public class SwaggerDubboConfig {
    ...
}

注:swagger.dubbo.enable用于区分不同环境(如:开发、测试、生产),在生产环境需要禁用swagger-dubbo。

启动项目,访问http://ip:port/swagger-dubbo/api-docs查看swagger-dubbo解析生成的文档。

访问http://ip:port/swagger-ui.html,发现顶部的下拉列表里只有1个选项:default(/v2/api-docs),
默认swagger-ui只展示了controller的接口,swagger-dubbo解析生成的文档打开是一个json串,并没有展示在swagger-ui上。

注:此时虽然swagger-ui页面没有展示,但可以通过http rest接口http://ip:port/h/com.xxx.XxService/xxxMethod调用dubbo接口。

运行官方的dubbo-provider-springboot示例,发现顶部的下拉列表变成了输入框,当启用跨域访问后就能看到解析除的dubbo接口。
在工程的/src/main/resources/static/distv2的目录下,有定制的swagger-ui相关文件。
但如果每个项目中都加入这些前端相关的文件有点麻烦,希望在官方默认的swagger-ui页面中就能看到项目中的dubbo接口。

springfox-swagger-ui的官方介绍里有一句话:
Adds a JSON endpoint /swagger-resources which lists all of the swagger resources and versions configured for a given application.

在启动项目的日志里也能看到:
Mapped "{[/swagger-resources/configuration/ui]}" onto public org.springframework.http.ResponseEntity<springfox.documentation.swagger.web.UiConfiguration> springfox.documentation.swagger.web.ApiResourceController.uiConfiguration()
Mapped "{[/swagger-resources/configuration/security]}" onto public org.springframework.http.ResponseEntity<springfox.documentation.swagger.web.SecurityConfiguration> springfox.documentation.swagger.web.ApiResourceController.securityConfiguration()
Mapped "{[/swagger-resources]}" onto public org.springframework.http.ResponseEntity<java.util.List<springfox.documentation.swagger.web.SwaggerResource>> springfox.documentation.swagger.web.ApiResourceController.swaggerResources()
springfox.documentation.swagger.web.ApiResourceController暴露了3个地址,其中/swagger-resources就是swagger-ui页面顶部下拉列表的数据源。
打开该controller的源码,发现是通过SwaggerResourcesProvider接口实现的,该接口继承了guava的Supplier接口。
查看接口只有1个实现类InMemorySwaggerResourcesProvider,里面的get方法就是获取数据源。

swagger-dubbo解析的dubbo接口已生成了数据源,通过/swagger-dubbo/api-docs暴露,因此对SwaggerDubboConfig做一些改造:

/**
 * @author cdfive
 */
@ConditionalOnProperty(name = "swagger.dubbo.enable", matchIfMissing = false)
@EnableDubboSwagger
@Configuration
public class SwaggerDubboConfig {

    @Primary
    @Bean
    public SwaggerResourcesProvider swaggerResourcesProvider(Environment environment, DocumentationCache documentationCache) {
        return new CustormSwaggerResourcesProvider(environment, documentationCache);
    }

    static class CustormSwaggerResourcesProvider extends InMemorySwaggerResourcesProvider {

        public CustormSwaggerResourcesProvider(Environment environment, DocumentationCache documentationCache) {
            super(environment, documentationCache);
        }

        @Override
        public List<SwaggerResource> get() {
            List<SwaggerResource> swaggerResources = super.get();

            SwaggerResource swaggerDubboResource = new SwaggerResource();
            swaggerDubboResource.setName("dubbo");
            swaggerDubboResource.setLocation("/swagger-dubbo/api-docs");
            swaggerDubboResource.setSwaggerVersion("2.0");
            swaggerResources.add(swaggerDubboResource);
            return swaggerResources;
        }
    }
}

思路是自定义CustormSwaggerResourcesProvider继承InMemorySwaggerResourcesProvider,里面将dubbo对应的/swagger-dubbo/api-docs
数据源添加进去,通过@Primary注解标识该实现类优先使用。

改造后重新启动项目,访问http://ip:port/swagger-ui.html页面,顶部下拉列表就有了2个选项:
default(/v2/api-docs)和dubbo(/swagger-dubbo/api-docs),切换到dubbo后项目中的dubbo接口在界面展示了出来,并且输入参数值即可调用。

总结

  • 项目可能经常需要在本地对dubbo接口进行自测,通常使用junit单元测试,结合spring-boot-test,如果项目很大启动测试可能很耗时。
  • 先启动好项目,通过telnet连接dubbo服务,通过invoke命令能直接调用接口测试,但需要在命令里准备好接口和参数,不能可视化。
  • 通过引入swagger-ui并结合swagger-dubbo能直接在界面调用dubbo接口,方便自测、测试和调试,能在一定程度上帮助提高开发效率。

参考

原文地址:https://www.cnblogs.com/cdfive2018/p/14330299.html