Spring Cloud Gateway集成SpringDoc,集中管理微服务API

news/2024/6/23 18:22:48

本文目标

Spring Cloud微服务集成SpringDoc,在Spring Cloud Gateway中统一管理微服务的API,微服务上下线时自动刷新SwaggerUi中的group组。

依赖版本

框架版本
Spring Boot3.1.5
Spring Cloud2022.0.4
Spring Cloud Alibaba2022.0.0.0
Spring Doc2.2.0
Nacos Server2.2.3

开始集成

项目模块

image.png

公共模块里的配置是之前文章中提到的内容,加了一个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信息,但是在测试接口时还是会出现问题,请求会直接发送至微服务,并不会经过网关代理,如下所示

image.png

image.png

所以说需要修改各微服务配置,指定当前服务访问的url,在SpringDoc配置中添加servers属性,并设置值为被网关代理的路径,如下所示

image.png

在引用的微服务中设置自定义配置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

image.png

其它微服务都是一些测试接口,没必要贴了,大家用自己的就好,或者去代码仓库拉取代码看看。

附录

  1. SpringDoc枚举字段处理与SpringBoot接收枚举参数处理
  2. SpringDoc基础配置和集成OAuth2登录认证教程
  3. 代码仓库:Gitee、Github

https://www.xjx100.cn/news/3118756.html

相关文章

数字化转型的核心是数据,还是应用?_光点科技

数字化转型是当今世界各行各业的热门话题。它不仅仅是将传统的业务流程、产品和服务数字化&#xff0c;更是一种全面的业务战略转变。在这个转变过程中&#xff0c;数据和应用都扮演着至关重要的角色。但究竟哪一个是数字化转型的核心&#xff1f;这个问题值得深入探讨。 我们来…

base64转PDF

今天做皖事通的对接&#xff0c;下载电子证照后发现回传的是base64&#xff0c;调试确认是个麻烦事&#xff0c;网上搜了一下没有base64转PDF的在线预览功能&#xff0c;只能自己写个调试工具了&#xff0c;以下是通过纯JS方式写的代码&#xff0c;可直接拿去使用&#xff1a; …

科研学习|论文解读——Deep learning for anomaly detection in log data: a survey

摘要 自动日志文件分析能够及早发现系统故障等相关事件。特别是&#xff0c;自学习异常检测技术能够捕捉日志数据中的模式&#xff0c;然后向系统操作员报告意外的日志发生&#xff0c;而无需提前提供或手动建模异常场景。最近&#xff0c;越来越多的利用深度学习方法来实现此目…

TS版LangChain实战:基于文档的增强检索(RAG) | 京东云技术团队

LangChain LangChain是一个以 LLM &#xff08;大语言模型&#xff09;模型为核心的开发框架&#xff0c;LangChain的主要特性&#xff1a; 可以连接多种数据源&#xff0c;比如网页链接、本地PDF文件、向量数据库等允许语言模型与其环境交互封装了Model I/O&#xff08;输入…

基于Spring、SpringMVC、MyBatis的在线云音乐网站

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于Spring、SpringMVC、MyBatis的在线云…

Pycharm2020.3.5激活方式

激活插件链接&#xff1a;https://pan.baidu.com/s/1tPd7V4pKUx0Z6fSKumLjTQ 提取码&#xff1a;lr12 1.pycharm主界面点开设置如下&#xff1a; 2.点击 Plugins 然后依次点击&#xff1a;小齿轮->选择本地安装&#xff08;下图&#xff09; 3.找到存放插件的目录&#xf…

如何跑通跨窗口渲染:multipleWindow3dScene

New 这是一个跨窗口渲染的示例&#xff0c;用 Three.js 和 localStorage 在同一源&#xff08;同产品窗口&#xff09;上跨窗口设置 3D 场景。而这也是本周推特和前端圈的一个热点&#xff0c;有不少人在争相模仿它的实现&#xff0c;如果你对跨窗口的渲染有兴趣&#xff0c;可…

PyQt基础_007_ 按钮类控件QCombox

import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import *class ComboxDemo(QWidget):def __init__(self, parentNone):super(ComboxDemo, self).__init__(parent)self.setWindowTitle("combox 例子") self.resize(300, 90) …