微服务架构在给我们带来好处的同时也制造了一些麻烦,下面就介绍一下我们遇到的麻烦。
swagger是集成在各个微服务内部的,这种情况下查看接口文档就很费劲,看服务a的文档要进a的地址,看服务b的文档又要进b的地址,如果想要通过网关访问直接就凉凉了。所以我们希望能有一个统一的入口查看swagger文档,那么针对这种情况该怎么办呢?
swagger可以分为两部分,一部分是swagger-ui用来展示的,另一部分是数据也就是接口文档。swagger-ui本身是具备展示多个接口文档的能力的,所以我们只要把多个接口文档给同一个swagger-ui展示就解决问题了。
很容易就可以发现swagger-ui列出的文档是由swagger-resources接口提供的,再通过swagger-resources接口的源码发现只要实现自己的SwaggerResourcesProvider返回的(所以我们只要实现自己的SwaggerResourcesProvider返回所有微服务的文档信息即可),swagger主要源码如下
@Controller@ApiIgnore@RequestMapping("/swagger-resources")public class ApiResourceController { private final SwaggerResourcesProvider swaggerResources; @Autowired public ApiResourceController(SwaggerResourcesProvider swaggerResources) { this.swaggerResources = swaggerResources; } @RequestMapping @ResponseBody ResponseEntity> swaggerResources() { return new ResponseEntity>(swaggerResources.get(), HttpStatus.OK); }}
接下来就是统一入口的问题,这个当然非网关莫属了,只要解决到各个微服务文档接口的映射即可(也就是正确的路由到各个微服务的/v2/api-docs接口),最简单的方式就是修改url,只需要定义一下springfox.documentation.swagger.v2.path,swagger主要源码如下
@Controller@ApiIgnorepublic class Swagger2Controller { public static final String DEFAULT_URL = "/v2/api-docs"; @ApiIgnore @RequestMapping(value = "${springfox.documentation.swagger.v2.path:" + DEFAULT_URL + "}", method = RequestMethod.GET) public @ResponseBody ResponseEntity getDocumentation( @RequestParam(value = "group", required = false) String swaggerGroup) { String groupName = Optional.fromNullable(swaggerGroup).or(Docket.DEFAULT_GROUP_NAME); Documentation documentation = documentationCache.documentationByGroup(groupName); if (documentation == null) { return new ResponseEntity(HttpStatus.NOT_FOUND); } Swagger swagger = mapper.mapDocumentation(documentation); swagger.host(hostName()); return new ResponseEntity(jsonSerializer.toJson(swagger), HttpStatus.OK); }}
当然每个微服务都需要定义,我们定义的是 微服务名称 + "/v2/api-docs",这样的好处是网关不需要再增加额外的路由规则
# 网关路由配置zuul: routes: serviceA: url: http://192.168.1.100/serviceA/** serviceB: url: http://192.168.1.200/serviceB/**
# 配置微服务A的swagger文档接口地址springfox: documentation: swagger: v2: path: "/serviceA/v2/api-docs"
# 配置微服务B的swagger文档接口地址springfox: documentation: swagger: v2: path: "/serviceB/v2/api-docs"
接下来就是实现我们自己的SwaggerResourcesProvider了
@Bean @Primary public SwaggerResourcesProvider swaggerResourcesProvider( InMemorySwaggerResourcesProvider inMemorySwaggerResourcesProvider, ZuulProperties zuulProperties) { return new SwaggerResourcesProvider() { @Override public List get() { List result = new ArrayList<>(); List list = inMemorySwaggerResourcesProvider.get(); if (!CollectionUtils.isEmpty(list)) { result.addAll(list); } Map routes = zuulProperties.getRoutes(); for (Map.Entry entry : routes.entrySet()) { String key = entry.getKey(); ZuulProperties.ZuulRoute route = entry.getValue(); SwaggerResource swaggerResource = new SwaggerResource(); swaggerResource.setName(entry.getKey()); swaggerResource.setSwaggerVersion("2.0"); String path = route.getPath(); path = path.replace(Matcher.quoteReplacement("**"), "v2/api-docs"); swaggerResource.setLocation(path); result.add(swaggerResource); } return result; } }; }