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

本文目标

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: gateway
  cloud:
    gateway:
      discovery:
        locator:
          # 根据注册中心的服务自动生成路由
          enabled: true
          # 路由名转小写
          lower-case-service-id: true

# ------------以下内容可改为公共配置------------
# SpringDoc自定义配置
custom:
  info:
    title: ${spring.application.name}-api
    version: 0.0.1
    description: 这是一个使用SpringDoc生成的在线文档.
    terms-of-service: http://127.0.0.1:8000/test01
    gateway-url: http://127.0.0.1:8080
  license:
    name: Apache 2.0
  security:
    name: Authenticate
    token-url: http://kwqqr48rgo.cdhttp.cn/oauth2/token
    authorization-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;

    @Resource
    private 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();
    }

    @Override
    public 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());
        }
        // 刷新group
        this.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);
        }
    }

    @PostConstruct
    public void registerToNotifyCenter() {
        // 注册监听事件
        NotifyCenter.registerSubscriber((this));
    }

    @Override
    public 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}-api
    version: 0.0.1
    description: 这是一个使用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.0
  security:
    name: Authenticate
    token-url: http://kwqqr48rgo.cdhttp.cn/oauth2/token
    authorization-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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/202672.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C#图像处理OpenCV开发指南(CVStar,03)——基于.NET 6的图像处理桌面程序开发实践第一步

1 Visual Studio 2022 开发基于.NET 6的OpenCV桌面程序 1.1 为什么选择.NET 6开发桌面应用&#xff1f; 选择 .NET 6&#xff08;最早称为 .NET Core&#xff09;而非 Frameworks.NET 的理由是&#xff1a;&#xff08;1&#xff09;跨平台&#xff1b;已经支持Windows,Linux…

yolov1网络结构说明

文章目录 一. 网络结构二. 网络说明1. 网络的输入2. 网络的输出(1) 5 5表示:每个网格使用两个先验框进行预测。(2) “5”表示&#xff1a;每个先验框包含的预测信息的数量。(3) 20表示&#xff1a;20个分类预测值(4) 每个网格能预测几个目标&#xff1f; 一. 网络结构 论文下…

eNSP实验

前言 本文记录了使用eNSP进行组网&#xff0c;学习、巩固一些之前学的网络基础知识和协议。 一&#xff1a;同网段、网关互通 网络拓扑如下&#xff1a; AR1的配置&#xff1a; interface G0/0/0 ip address 192.168.10.1 24 PC1和PC2的配置(IP地址和网关设置) 最终实现PC1…

C/C++ Zlib库封装MyZip压缩类

Zlib是一个开源的数据压缩库&#xff0c;提供了一种通用的数据压缩和解压缩算法。它最初由Jean-Loup Gailly和Mark Adler开发&#xff0c;旨在成为一个高效、轻量级的压缩库&#xff0c;其被广泛应用于许多领域&#xff0c;包括网络通信、文件压缩、数据库系统等。其压缩算法是…

NX二次开发UF_CURVE_create_bridge_curve 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_create_bridge_curve Defined in: uf_curve.h int UF_CURVE_create_bridge_curve(int bridge_method, tag_t curve_ids [ 2 ] , double parms [ 2 ] , int reverse_tangent…

MySQL安装与配置教程

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

基于Java SSM框架实现美食推荐管理系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现美食推荐管理系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&a…

什么是透明加密技术?透明加密有哪些优势?

透明加密技术是一种特殊的加密方法&#xff0c;它在用户毫不知情的情况下对数据进行加密和解密&#xff0c;保障了数据的安全性。用户在使用这种加密技术时&#xff0c;无需改变他们的日常操作习惯&#xff0c;加密和解密过程在后台自动进行&#xff0c;使得用户在享受数据安全…

Python语言学习笔记之六(程序调试及异常处理)

本课程对于有其它语言基础的开发人员可以参考和学习&#xff0c;同时也是记录下来&#xff0c;为个人学习使用&#xff0c;文档中有此不当之处&#xff0c;请谅解。 1、Python程序常见的错误 语法错误:不正确的缩进、未定义的变量、括号不匹配等.运行时错误: 尝试访问不存在的…

PyQt基础_009_ 按钮类控件QSlider

基本功能 import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import *class SliderDemo(QWidget):def __init__(self, parentNone):super(SliderDemo, self).__init__(parent)self.setWindowTitle("QSlider 例子") self.resize…

Python函数定义、函数调用详解

函数是 Python 程序的重要组成单位&#xff0c;一个 Python 程序可以由很多个函数组成。前面我们己经用过大量函数&#xff0c;如 len()、max() 等&#xff0c;使用函数是真正开始编程的第一步。 比如在程序中定义了一段代码&#xff0c;这段代码用于实现一个特定的功能。问题来…

掌握Flask:从入门到精通指南

掌握Flask&#xff1a;从入门到精通指南 Flask 是一个轻量级的 Python Web 应用程序框架&#xff0c;具有简单易学、灵活性高等特点&#xff0c;适合用于快速开发 Web 应用程序。本文将全面介绍 Flask 框架的各个方面&#xff0c;包括基本概念、路由、模板渲染、表单处理、数据…

abapgit 安装及使用

abapgit 需求 SA[ BASIS 版本 702 及以上 版本查看路径如下&#xff1a; 安装步骤如下&#xff1a; 1. 下载abapgit 独立版本 程序 链接如下&#xff1a;raw.githubusercontent.com/abapGit/build/main/zabapgit_standalone.prog.abap 2.安装开发版本 2.1 在线安装 前置条…

使用凌鲨管理本地git仓库

把本地git仓库添加到凌鲨后&#xff0c;可以更方便的获取git仓库的信息&#xff0c;比如查看commit记录&#xff0c;统计代码提交量&#xff0c;获取远程仓库的issue等功能。 功能 查看提交/分支/标记列表 查看提交差异 查看远程仓库和相关issue 每天代码量统计 添加本地仓库…

在线yml和properties相互转换

目前搜索到的大部分代码都存在以下问题&#xff1a; 复杂结构解析丢失解析后顺序错乱 所以自己写了一个&#xff0c;经过不充分测试&#xff0c;基本满足使用。可以直接在线使用 在线地址 除了yml和properties互转之外&#xff0c;还可以生成代码、sql转json等&#xff0c;可…

NSSCTF第14页(1)

[suctf 2019]checkin 利用了几种方式&#xff0c;发现都不行 1是修改mime类型&#xff0c;2是修改php标签为js标签&#xff0c;3是修改文件后缀 在试试用配置文件来上传 发现上传.user.ini文件成功 发现上传成功 上传的png图片 访问上传路径发现可以访问&#xff0c;上马成…

「Verilog学习笔记」状态机-非重叠的序列检测

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 根据题意 定义一个五位的中间变量lock 每次始终上升沿来临时 判断当前寄存器的低四位新数据是否等于10111 如果等于 则下一时刻lock应被清空 否则lock等于当前的lock的低四…

windows+deepin v23 linux 双系统 安装前后 与 删除后 的硬盘efi分区情况,deepin v23 beta2的一些体验

知乎版&#xff1a;https://zhuanlan.zhihu.com/p/669429404 windows下安装deepin v23 beta2 电脑8GB内存&#xff0c;一个256GB固态硬盘&#xff0c;已经安装windows11。 安装双系统前分区情况&#xff1a;主要包含 windows EFI分区 和 系统分区&#xff0c;并预留了64GB给d…

【产品经理】AI在SaaS产品中的应用及挑战

随着ChatGPT大模型在全球的爆火&#xff0c;AI迅速在各个行业内&#xff0c;助力于各行业的效率提升。而SaaS领域&#xff0c;AI同样也大有可为。 AI&#xff08;人工智能&#xff0c;Artificial Intelligence的缩写&#xff09;近一年来一直处于舆论风口&#xff0c;随着ChatG…

nacos配置变更导致logback日志异常

问题背景: 线上的服务突然内存爆满&#xff0c;查服务器突然发现&#xff0c;日志全部打印到了/tmp/tomcat.xxx.port目录下&#xff0c;后来对应操作时间&#xff0c;和nacos修改配置是同一时间发生的&#xff0c;但是疑惑的点是&#xff0c;nacos配置变更为什么会引起logback的…