本文介绍: 本文目标为Spring Cloud服务集成SpringDoc,在Spring Cloud Gateway中统一管理服务的API,微服务上下线时自动刷新SwaggerUi中的group组。

本文目标

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

开始集成

项目模块

image.png

公共模块里的配置是之前文章中提到的内容,加了一个webmvcwebflux适配,我会将文章代码仓库链接放在最下边,有需要可以看看

引入依赖配置依赖管理

在父模块添加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">
    &lt;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、gatewayloadbalancer负载均衡和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. 代码仓库GiteeGithub

原文地址:https://blog.csdn.net/weixin_43356507/article/details/134695093

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_9551.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注