前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Knife4j】小试牛刀,基于gateway的集成

【Knife4j】小试牛刀,基于gateway的集成

作者头像
小尘哥
发布2022-09-01 14:48:18
6080
发布2022-09-01 14:48:18
举报
文章被收录于专栏:小尘哥的专栏小尘哥的专栏

上回书说到,knife4j基于注册中心eureka集成,由于有些小伙伴可能使用了nacos、Consul、zk等注册中心,均有对应的集成方法,但是一旦切换了注册中心(比如从eureka切换成nacos),则需要重新做集成。因此本文介绍一下基于gateway的集成方式。

一、工程结构

  • eboot-center:eureka注册中心(服务端)
  • eboot-knife4j:文档服务,本文不需要
  • eboot-common:包含了一些基础的认证、全局异常等处理,本文暂不需要
  • eboot-gateway:spring-cloud-gateway,大多是情况下微服务均走网关
  • eboot-modulars:即各个业务子系统,这里分了三个:认证管理、文件管理、系统管理
  • eboo-ui:前端项目,本文不需要

二、center、modulars配置

均参考上篇文章即可,可回头看《【Knife4j】小试牛刀,基于eureka的集成

三、gateway聚合配置

此处建议先看官网配置

pom配置

代码语言:javascript
复制
    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
        </dependency>

原springboot基于tomcat,而gateway基于webflux,因此需要以下操作:

springfox-swagger提供的分组接口是swagger-resource,返回的是分组接口名称、地址等信息。在Spring Cloud微服务架构下,我们需要重写该接口,主要是通过网关的注册中心动态发现所有的微服务文档,代码如下:

代码语言:javascript
复制
package com.mos.eboot.gateway.config.properties;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

import java.util.ArrayList;
import java.util.List;

/**
 * swagger资源配置
 *
 * @author 小尘哥
 * @date 2022/05/30
 */
@Slf4j
@AllArgsConstructor
@Component
@Primary
public class SwaggerResourceConfig implements SwaggerResourcesProvider {

    private final RouteLocator routeLocator;
    private final GatewayProperties gatewayProperties;

    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> resources = new ArrayList<>();
        List<String> routes = new ArrayList<>();
        routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
        gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> {
            route.getPredicates().stream()
                    .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
                    .forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(),
                            predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
                                    .replace("**", "v2/api-docs"))));
        });
        return resources;
    }

    private SwaggerResource swaggerResource(String name, String location) {
        log.info("name:{},location:{}",name,location);
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion("2.0");
        return swaggerResource;
    }
}

接口如下

代码语言:javascript
复制
package com.mos.eboot.gateway.handler;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.*;

import java.util.Optional;

@RestController
public class SwaggerHandler {

    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;

    @Autowired(required = false)
    private UiConfiguration uiConfiguration;

    private final SwaggerResourcesProvider swaggerResources;

    @Autowired
    public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
        this.swaggerResources = swaggerResources;
    }


    @GetMapping("/swagger-resources/configuration/security")
    public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("/swagger-resources/configuration/ui")
    public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("/swagger-resources")
    public Mono<ResponseEntity> swaggerResources() {
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }
}

yaml配置(基于yaml中的routes进行分组)

代码语言:javascript
复制
server:
  port: 9005
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
  instance:
    appname: eboot-gateway
    leaseRenewalIntervalInSeconds: 1
    lease-expiration-duration-in-seconds: 2
    prefer-ip-address: true
    instance-id: 127.0.0.1:9005

spring:
  main:
    allow-bean-definition-overriding: true
    allow-circular-references: true
    web-application-type: REACTIVE
  cloud:
    gateway:
      discovery:
        locator:
          # 因为这时为每个服务创建了2个router
          enabled: true
          # 将请求路径上的服务名配置为小写(因为服务注册的时候,向注册中心注册时将服务名转成大写的了)
          lowerCaseServiceId: true
      routes:
        - id: boot-system
          uri: http://localhost:9093
          predicates:
            - Path=/system/**
        - id: eboot-auth
          uri: http://localhost:9091
          predicates:
            - Path=/auth/**
      #   filters:
      #     - SwaggerHeaderFilter
      #     - StripPrefix=1

四、展示

五、注意点

在集成Spring Cloud Gateway网关的时候,会出现没有basePath的情况(即定义的例如/user、/order等微服务的前缀),这个情况在使用zuul网关的时候不会出现此问题,因此,在Gateway网关需要添加一个Filter实体Bean

由于我使用的springcloud版本为3.1.2,未出现该问题,若出现请参考官网的解决方案,添加以下过滤器,同时放开yaml中的routes.filters配置项

代码语言:javascript
复制
@Component
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
    private static final String HEADER_NAME = "X-Forwarded-Prefix";

    private static final String URI = "/v2/api-docs";

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getPath();
            if (!StringUtils.endsWithIgnoreCase(path,URI )) {
                return chain.filter(exchange);
            }
            String basePath = path.substring(0, path.lastIndexOf(URI));
            ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
            ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
            return chain.filter(newExchange);
        };
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 陌与尘埃 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、工程结构
  • 二、center、modulars配置
  • 三、gateway聚合配置
  • 五、注意点
相关产品与服务
微服务引擎 TSE
微服务引擎(Tencent Cloud Service Engine)提供开箱即用的云上全场景微服务解决方案。支持开源增强的云原生注册配置中心(Zookeeper、Nacos 和 Apollo),北极星网格(腾讯自研并开源的 PolarisMesh)、云原生 API 网关(Kong)以及微服务应用托管的弹性微服务平台。微服务引擎完全兼容开源版本的使用方式,在功能、可用性和可运维性等多个方面进行增强。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档