目 录CONTENT

文章目录

zuul聚合knife4j文档

醉酒的行者
2025-04-26 / 0 评论 / 0 点赞 / 86 阅读 / 0 字

使用 Zuul 来聚合多个微服务的 Knife4j 文档,将其整合为一个统一的文档界面。Knife4j 是基于 Swagger 增强的工具,提供更友好的界面和更丰富的功能。

1. 在每个微服务中配置 Knife4j

1.1 3.0版本的微服务

目标:确保每个微服务都集成并启用了 Knife4j,以暴露其 API 文档。

操作

在每个微服务的项目中添加 Knife4j 的依赖。例如,对于 Spring Boot 项目,可以在 pom.xml 中添加:

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>

配置一个 Docket bean 来定义 API 文档。例如:

//这里声明了Swagger的版本为OpenAPI 3.0
@Bean
public Docket api3() {
    return new Docket(DocumentationType.OAS_30)
            .select()
            .apis(RequestHandlerSelectors.basePackage("控制器包路径"))
            .paths(PathSelectors.any())
            .build()
            .apiInfo(new ApiInfoBuilder()
                    .title("微服务 API 文档")
                    .version("1.0")
                    .build());
}

完成后,每个微服务将通过类似 http://microsoft:9076/v3/api-docs 端点暴露其 OpenAPI 文档。

1.2 2.0版本的微服务

对于使用Swagger 2.0的微服务,不同点在于在 Docket 配置中设置DocumentationType.SWAGGER_2,如下:

//这里声明了Swagger的版本为Swagger 2.0
@Bean
public Docket api2() {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.basePackage("控制器包路径"))
            .paths(PathSelectors.any())
            .build()
            .apiInfo(new ApiInfoBuilder()
                    .title("微服务 API 文档")
                    .version("1.0")
                    .build());
}

完成后,每个微服务将通过类似 http://microsoft:9077/v2/api-docs 端点暴露其 OpenAPI 文档。

2. 配置Zuul API网关

2.1 添加Knife4j依赖

  • 目标:创建一个 Zuul 网关服务,用于将请求路由到各个微服务。

  • 操作:创建一个新的 Spring Boot 项目作为 Zuul 网关,添加 Knife4j 的依赖:

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>

在 application.yml 中配置路由规则,例如:

zuul:
  routes:
    service1-id:
      path: /service1/**
      serviceId: service1
    service2-id:
      path: /service2/**
      serviceId: service2
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

2.2 实现动态收集Knife4j 文档资源

  • 目标:通过 Zuul 动态收集各个微服务的 Knife4j 文档资源。

  • 操作:在 Zuul 网关服务中实现一个 SwaggerResourcesProvider,以便根据 Zuul 的路由动态生成 Swagger 资源。要求兼容 Swagger 2.0 和 3.0,例如:

@Data
@Component
//这里添加Primary目的是标记首选SwaggerResourcesProvider
//因为默认系统会生成inMemorySwaggerResourcesProvider和此处CustomSwaggerResourcesProvider产生冲突
//为了可以保留框架完整功能的同时解决冲突,故标记为Primary
@Primary
@ConfigurationProperties(prefix = "swagger")
public class CustomSwaggerResourcesProvider implements SwaggerResourcesProvider {
    private final RouteLocator routeLocator;

    public CustomSwaggerResourcesProvider(RouteLocator routeLocator) {
        this.routeLocator = routeLocator;
    }

    private List<Map<String, String>> services;

    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> resources = new ArrayList<>();
        Map<String, String> serviceVersionMap =
            services.stream().collect(Collectors.toMap(m -> m.get("name"), m -> m.get("version")));
        routeLocator.getRoutes().stream()
            .filter(n -> serviceVersionMap.containsKey(n.getId()))
            .forEach(
                route -> {
                    String name = route.getId();
                    String version = serviceVersionMap.getOrDefault(name, "3.0");
                    String apiDocsPath = version.startsWith("2") ? "v2/api-docs" : "v3/api-docs";
                    String location = route.getFullPath().replace("**", apiDocsPath);
                    resources.add(swaggerResource(view, location, version));
                });
        return resources;
    }

    private SwaggerResource swaggerResource(String name, String location, String version) {
        SwaggerResource resource = new SwaggerResource();
        resource.setName(name);
        resource.setUrl(location);
        resource.setSwaggerVersion(version);
        return resource;
    }
}

为了兼容 Swagger 2.0 和 3.0,我们在 application.yml 中定义了 swagger.services 属性,如下:

swagger:
  services:
    - { name: service1-id,version: 2.0 }
    - { name: service2-id,version: 3.0 }

显式的定义每个微服务使用 swagger 版本,在 CustomSwaggerResourcesProvider 中,通过 services 属性在 zuul 启动后,对不同的微服务赋予不同版本的文档 url。

2.3 通过Zuul服务访问Knife4j

您可以在 Zuul 网关中通过一个统一的 Knife4j 界面访问所有微服务的 API 文档。访问 Zuul 的 Knife4j UI(通常是 /doc.html),即可看到聚合后的文档。比如 zuul 的服务 http://zuul:8802/doc.html

您将看到一个包含 service1、service2 等微服务文档的下拉菜单或分组视图。如下图:

通过这种方法,您可以有效管理微服务架构中的 API 文档,为开发人员提供一个集中式的访问入口。

3 问题

问题:访问 http://service1:9901/v2/api-docs?group=xxx 正常返回 json 串,而访问 http://service1:9901/v2/api-docs 返回 404

分析:究其原因微服务配置了多组 Swagger 文档(配置 Docket 分组),没有给出默认的分组,并且 /v2/api-docs 的行为与分组配置有关。

Knife4j 的 doc.html 自动加载 xxx 分组的文档,因此界面正常。但直接访问 /v2/api-docs 失败,这是由于 Springfox 要求多组文档时通过 group 参数指定分组,否则返回 404。如果不加 group 参数,默认是加载名为 default 的分组文档,如果分组时没有指定,则会返回 404。

解决:最直接的解决办法就是配置 Docket 分组,直接指定一个名为 default 的分组。如下图所示:

希望上述文档对你有所帮助。

0

评论区