本文目标
Spring Cloud微服务集成SpringDoc,在Spring Cloud Gateway中统一管理微服务的API,微服务上下线时自动刷新SwaggerUi中的group组。
依赖版本
框架 | 版本 |
---|---|
Spring Boot | 3.1.5 |
Spring Cloud | 2022.0.4 |
Spring Cloud Alibaba | 2022.0.0.0 |
Spring Doc | 2.2.0 |
Nacos Server | 2.2.3 |
开始集成
项目模块
公共模块里的配置是之前文章中提到的内容,加了一个webmvc和webflux的适配,我会将文章和代码仓库的链接放在最下边,有需要的可以去看看。
引入依赖,配置依赖管理
在父模块中添加lombok、测试包和服务发现与注册的包,管理Spring Cloud、Spring Cloud Alibaba依赖版本,如下
不要忘了SpringDoc的依赖管理
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>spring-doc-spring-cloud</artifactId><version>0.0.1</version><packaging>pom</packaging><name>spring-doc-spring-cloud</name><description>spring-doc-spring-cloud</description><modules><module>spring-doc-cloud-common</module><module>spring-doc-cloud-gateway</module><module>spring-doc-cloud-webflux</module><module>spring-doc-cloud-webmvc</module></modules><properties><!-- 指定Java版本为Java17 --><java.version>17</java.version><!-- 公共模块版本 --><common.version>0.0.1</common.version><!-- 修复SpringBoot自带snakeyaml依赖版本的漏洞 --><snakeyaml.version>2.0</snakeyaml.version><!-- SpringDoc-OpenApi版本号 --><spring-doc.version>2.2.0</spring-doc.version><!-- SpringCloud版本 --><spring-cloud.version>2022.0.4</spring-cloud.version><!-- 指定打包插件版本 --><maven-surefire-plugin.version>3.2.2</maven-surefire-plugin.version><!-- Spring Cloud Alibaba版本号 --><spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version></properties><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 服务注册与发现 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency><!-- 适用于webmvc的SpringDoc依赖 --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>${spring-doc.version}</version></dependency><!-- 适用于webflux的SpringDoc依赖 --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webflux-ui</artifactId><version>${spring-doc.version}</version></dependency></dependencies></dependencyManagement></project>
spring-doc-cloud-gateway
模块说明
引入webflux、gateway、loadbalancer负载均衡和springdoc依赖,同时引入公共模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.example</groupId><artifactId>spring-doc-spring-cloud</artifactId><version>0.0.1</version></parent><artifactId>spring-doc-cloud-gateway</artifactId><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!-- webflux依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><!-- 网关 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!-- 负载均衡 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><!-- SpringDoc --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webflux-ui</artifactId></dependency><dependency><groupId>io.projectreactor</groupId><artifactId>reactor-test</artifactId><scope>test</scope></dependency><!-- 公共包,这里是对于swagger的自定义配置,可以参考之前的文章或直接查看代码仓库的实现 --><dependency><groupId>com.example</groupId><artifactId>spring-doc-cloud-common</artifactId><version>${common.version}</version></dependency></dependencies><build><plugins><plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><image><builder>paketobuildpacks/builder-jammy-tiny:latest</builder></image><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>
修改application.yml
开启gateway自动扫描,根据注册中心的服务自动生成路由,路由名转小写,添加自定义的swagger配置。
spring:application:name: gatewaycloud:gateway:discovery:locator:# 根据注册中心的服务自动生成路由enabled: true# 路由名转小写lower-case-service-id: true# ------------以下内容可改为公共配置------------
# SpringDoc自定义配置
custom:info:title: ${spring.application.name}-apiversion: 0.0.1description: 这是一个使用SpringDoc生成的在线文档.terms-of-service: http://127.0.0.1:8000/test01gateway-url: http://127.0.0.1:8080license:name: Apache 2.0security:name: Authenticatetoken-url: http://kwqqr48rgo.cdhttp.cn/oauth2/tokenauthorization-url: http://kwqqr48rgo.cdhttp.cn/oauth2/authorize
添加InstancesChangeEventListener
监听微服务启、停状态,微服务状态改变后刷新Swagger UI中的组。
package com.example.config;import com.alibaba.nacos.client.naming.event.InstancesChangeEvent;
import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.Subscriber;
import com.alibaba.nacos.common.utils.JacksonUtils;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.properties.AbstractSwaggerUiConfigProperties;
import org.springdoc.core.properties.SwaggerUiConfigProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ObjectUtils;import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;import static org.springdoc.core.utils.Constants.DEFAULT_API_DOCS_URL;
import static org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier.SERVICE_INSTANCE_CACHE_NAME;/*** 监听注册中心实例注册状态改变事件,微服务实例状态改变后刷新swagger ui的组(一个组等于一个微服务)* * @author vains*/
@Slf4j
@Configuration(proxyBeanMethods = false)
public class InstancesChangeEventListener extends Subscriber<InstancesChangeEvent> {private final String LB_SCHEME = "lb";private final RouteDefinitionLocator locator;@Resourceprivate CacheManager defaultLoadBalancerCacheManager;private final SwaggerUiConfigProperties swaggerUiConfigProperties;/*** 获取配置文件中默认配置的swagger组*/private final Set<AbstractSwaggerUiConfigProperties.SwaggerUrl> defaultUrls;public InstancesChangeEventListener(RouteDefinitionLocator locator,SwaggerUiConfigProperties swaggerUiConfigProperties) {this.locator = locator;this.swaggerUiConfigProperties = swaggerUiConfigProperties;// 构造器中初始化配置文件中的swagger组this.defaultUrls = swaggerUiConfigProperties.getUrls();}@Overridepublic void onEvent(InstancesChangeEvent event) {if (log.isDebugEnabled()) {log.info("Spring Gateway 接收实例刷新事件:{}, 开始刷新缓存", JacksonUtils.toJson(event));}Cache cache = defaultLoadBalancerCacheManager.getCache(SERVICE_INSTANCE_CACHE_NAME);if (cache != null) {cache.evict(event.getServiceName());}// 刷新groupthis.refreshGroup();if (log.isDebugEnabled()) {log.info("Spring Gateway 实例刷新完成");}}/*** 刷新swagger的group*/public void refreshGroup() {// 获取网关路由List<RouteDefinition> definitions = locator.getRouteDefinitions().collectList().block();if (ObjectUtils.isEmpty(definitions)) {return;}// 根据路由规则生成 swagger组 配置Set<AbstractSwaggerUiConfigProperties.SwaggerUrl> swaggerUrls = definitions.stream()// 只处理在注册中心注册过的(lb://service).filter(definition -> definition.getUri().getScheme().equals(LB_SCHEME)).map(definition -> {// 生成 swagger组 配置,以微服务在注册中心中的名字当做组名、请求路径(我这里使用的是自动扫描生成的,所以直接用了这个,其它自定义的按需修改)String authority = definition.getUri().getAuthority();return new AbstractSwaggerUiConfigProperties.SwaggerUrl(authority, authority + DEFAULT_API_DOCS_URL, authority);}).collect(Collectors.toSet());// 如果在配置文件中有添加其它 swagger组 配置则将两者合并if (!ObjectUtils.isEmpty(defaultUrls)) {swaggerUrls.addAll(defaultUrls);}// 重置配置文件swaggerUiConfigProperties.setUrls(swaggerUrls);if (log.isDebugEnabled()) {String groups = swaggerUrls.stream().map(AbstractSwaggerUiConfigProperties.SwaggerUrl::getName).collect(Collectors.joining(","));log.debug("刷新Spring Gateway Doc Group成功,获取到组:{}.", groups);}}@PostConstructpublic void registerToNotifyCenter() {// 注册监听事件NotifyCenter.registerSubscriber((this));}@Overridepublic Class<? extends Event> subscribeType() {return InstancesChangeEvent.class;}
}
网关启动时、微服务停止、微服务启动时网关会从注册中心获取最新的服务列表,然后根据服务列表生成路由配置,路由的代理路径就是微服务的名字,使用http://网关ip:网关端口/微服务名/**
访问对应的微服务。
在注册中心(Nacos)的服务列表更新时会有一个SpringEvent事件通知,也就是上边类中的监听实现,每次收到通知时就会根据网关的路由生成SwaggerUrl
列表,其中name是微服务的名字(application.name),路径是/{application.name}/v3/api-docs
,这样实际上就是通过网关将请求代理至各微服务了,获取到的api信息实际上也是各微服务的,如果某个微服务禁用swagger,在网关中也获取不到对应的api信息。以上内容就是之前提到的微服务状态改变后刷新Swagger UI中的组。
当然,虽然可以通过网关代理获取到微服务的api信息,但是在测试接口时还是会出现问题,请求会直接发送至微服务,并不会经过网关代理,如下所示
所以说需要修改各微服务配置,指定当前服务访问的url,在SpringDoc配置中添加servers
属性,并设置值为被网关代理的路径,如下所示
在引用的微服务中设置自定义配置custom.info.gateway-url
,相信看到这里就明白为什么上方网关的yml中会有这么一个配置了。
修改微服务yml
添加类似如下配置,设置spring.application.name
,设置SpringDoc自定义配置,设置custom.info.gateway-url
spring:application:name: webmvc# ------------以下内容可改为公共配置------------
# SpringDoc自定义配置
custom:info:title: ${spring.application.name}-apiversion: 0.0.1description: 这是一个使用SpringDoc生成的在线文档.terms-of-service: http://127.0.0.1:8200/test01# 设置当前服务在网关中的代理路径gateway-url: http://127.0.0.1:8080/${spring.application.name}license:name: Apache 2.0security:name: Authenticatetoken-url: http://kwqqr48rgo.cdhttp.cn/oauth2/tokenauthorization-url: http://kwqqr48rgo.cdhttp.cn/oauth2/authorize
server:port: 8200
查看效果
webflux访问地址,默认会有一个webjars
前缀
http://127.0.0.1:8080/webjars/swagger-ui/index.html
其它微服务都是一些测试接口,没必要贴了,大家用自己的就好,或者去代码仓库拉取代码看看。
附录
- SpringDoc枚举字段处理与SpringBoot接收枚举参数处理
- SpringDoc基础配置和集成OAuth2登录认证教程
- 代码仓库:Gitee、Github