目 录CONTENT

文章目录

zuul聚合knife4j文档

醉酒的行者
2025-04-26 / 0 评论 / 0 点赞 / 11 阅读 / 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

评论区