SpringGateway 网关

SpringGateway 网关

经验文章nimo972025-03-20 12:04:0210A+A-

奈非框架简介

早期(2020年前)奈非提供的微服务组件和框架受到了很多开发者的欢迎

这些框架和Spring Cloud Alibaba的对应关系我们要知道

Nacos对应Eureka 都是注册中心

Dubbo对应ribbon+feign都是实现微服务间调用

Sentinel对应Hystrix都是项目限流熔断降级组件

Gateway对应zuul都是项目的网关

Gateway不是阿里巴巴的而是Spring提供的

什么是网关

"网关"网是网络,关是关口\关卡

关口\关卡的意思就是"统一入口"

网关:就是网络中的统一入口

程序中的网关就是微服务项目提供的外界所有请求统一访问的微服务项目

因为提供了统一入口之后,方便对所有请求进行统一的检查和管理

网关

网关的主要功能有

  • 将所有请求统一由经过网关
  • 网关可以对这些请求进行检查
  • 网关方便记录所有请求的日志
  • 网关可以统一将所有请求路由到正确的模块\服务上

路由的近义词就是"分配"

Spring Gateway简介

我们使用Spring Gateway作为当前项目的网关框架

Spring Gateway是Spring自己编写的,也是SpringCloud中的组件

SpringGateway官网

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/

简单网关演示

网关是一个我们创建的项目,不是一个需要安装的软件

网关也是当前微服务项目的一员,也要注册到Nacos,所以保证Nacos的运行

运行之前,我们看一下网关演示项目已经存在的基本结构

beijing和shanghai是编写好的两个项目

gateway没有编写yml文件配置

要想实现网关的路由效果需要修改yml文件如下

server:
  port: 9000
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
gateway:
  routes:  # gateway开始配置路由信息
      - id: gateway-shanghai
      uri: lb://shanghai
      predicates:
      - Path=/sh/**
        # 如果java访问这个数字元素的方式:spring.cloud.gateway.routes[0].predicates[0]
      # routes属性实际上是一个数组,yml文件中出现 "- ...."配置时表示当前配置时一个数组元素
        - id: gateway-beijing # 这个配置指定这个路由的名称,这个名称和其他任何位置没有关联
      # 只需要注意不能再和其他路由名称重复
      # uri设置路由的目标
      # lb是LoadBalance(负载均衡)的缩写,beijing是注册到nacos的服务名称
      uri: lb://beijing
      # 我们需要设置一个条件,当访问路径满足特定条件是,使用当前路由规则
      predicates:
      # predicates翻译为断言,所谓断言就是判断一个条件是否满足
      # Path 是路径断言,意思是满足路径为XXX时使用这个路由
        - Path=/bj/**
        # http://localhost:9000/bj/show 会路由到 9001/bj/show

内置断言

断言就是判断一个条件,如果条件满足就执行某个操作

predicates就是断言的意思

我们前面章节使用的Path就是内置断言中的一种,指访问的路径是否满足条件

除了路径断言之外,还有很多内置断言常见的内置断言列表

  • after
  • before
  • between
  • cookie
  • header
  • host
  • method
  • path
  • query
  • remoteaddr

时间相关

after,before,between

在指定时间之后,之前或之间

判断是否满足时间条件,如果满足才允许访问

我们先使用下面代码获得当前包含时区的系统时间表

ZonedDateTime.now()

使用After设置必须在指定时间之后访问

- id: gateway-shanghai
  uri: lb://shanghai
  predicates:
    - Path=/sh/**
    - After=2022-06-24T15:30:30.999+08:00[Asia/Shanghai]

使用Before设置必须在指定时间之后访问

- id: gateway-shanghai
  uri: lb://shanghai
  predicates:
    - Path=/sh/**
    - Before=2022-06-24T15:34:00.999+08:00[Asia/Shanghai]

使用Between设置必须在指定时间之间访问

- id: gateway-shanghai
  uri: lb://shanghai
  predicates:
    - Path=/sh/**
    - Between=2022-06-24T15:34:00.999+08:00[Asia/Shanghai],2022-06-24T15:36:20.999+08:00[Asia/Shanghai]

要求指定参数

Query断言,要求必须包含指定的参数才能访问资源

- id: gateway-shanghai
  uri: lb://shanghai
  predicates:
    - Path=/sh/**
    - Query=name

内置过滤器

Gateway还提供的内置过滤器

不要和我们学习的filter混淆

内置过滤器允许我们在路由请求到目标资源的同时,对这个请求进行一些加工或处理

下面我们使用AddRequestParameter过滤器,想请求中添加参数

- id: gateway-shanghai
  uri: lb://shanghai
  predicates:
    - Path=/sh/**
    - Query=name
  filters:
    - AddRequestParameter=age,80

shanghai项目的控制器接收这个参数

@GetMapping("/show")
public String show(String name,Integer age){
    return "这里是上海!"+name+","+age;
}

重启网关和shanghai项目

例如输入如下路径

http://localhost:9000/sh/show?name=tom

因为过滤器的存在,控制可以获取网关过滤器添加的参数值

其他内置过滤器和自定义过滤器的使用,同学们可以查阅相关文档自己了解

动态路由

如果项目微服务数量多

那么gateway项目yml文件配置也会越来越冗余,维护的工作量也会越来越大

所谓我们希望能够根据固定特征自动的路由到每个微服务模块

这个功能就是SpringGateway的动态路由功能

只需要在配置文件中配置开启动态路由功能即可

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          # 开启Spring Gateway的动态路由功能
          # 规则是根据注册到Nacos的项目名称作为路径的前缀,就可以访问到指定项目了
          enabled: true

开启之后访问项目的格式以beijing为例

localhost:9000/beijing/bj/show

以下是案例

创建gateway网关子项目

父子相认

子项目pom文件为



    4.0.0
    
        cn.celinf
        csmall
        0.0.1-SNAPSHOT
    
    cn.celinf
    gateway
    0.0.1-SNAPSHOT
    gateway
    Demo project for Spring Boot

    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
        
            org.springframework.cloud
            spring-cloud-starter-loadbalancer
        
        
        
            org.springframework.cloud
            spring-cloud-starter-gateway
        
        
        
            com.github.xiaoymin
            knife4j-spring-boot-starter
        
    

也删除test测试文件夹

application.yml文件内容如下

spring:
  application:
    name: gateway-server
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          # 开启网关动态路由
          enabled: true
  main:
    web-application-type: reactive
server:
  port: 10000

knife4j网关配置

我们希望配置网关之后,在使用knife4j测试时

就不来回切换端口号了

我们需要配置Knife4j才能实现

创建包cn.celinf.gateway.config包

SwaggerProvider

@Component
public class SwaggerProvider implements SwaggerResourcesProvider {
  /**
     * 接口地址
     */
  public static final String API_URI = "/v2/api-docs";
  /**
       * 路由加载器
       */
  @Autowired
  private RouteLocator routeLocator;
  /**
       * 网关应用名称
       */
  @Value("${spring.application.name}")
  private String applicationName;

  @Override
  public List get() {
    //接口资源列表
    List resources = new ArrayList<>();
    //服务名称列表
    List routeHosts = new ArrayList<>();
    // 获取所有可用的应用名称
    routeLocator.getRoutes().filter(route -> route.getUri().getHost() != null)
      .filter(route -> !applicationName.equals(route.getUri().getHost()))
      .subscribe(route -> routeHosts.add(route.getUri().getHost()));
    // 去重,多负载服务只添加一次
    Set existsServer = new HashSet<>();
    routeHosts.forEach(host -> {
      // 拼接url
      String url = "/" + host + API_URI;
      //不存在则添加
      if (!existsServer.contains(url)) {
      existsServer.add(url);
      SwaggerResource swaggerResource = new SwaggerResource();
      swaggerResource.setUrl(url);
      swaggerResource.setName(host);
      resources.add(swaggerResource);
    }
  });
  return resources;
  }
}

cn.celinf.gateway.controller

SwaggerController类

@RestController
@RequestMapping("/swagger-resources")
public class SwaggerController {
    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;
    @Autowired(required = false)
    private UiConfiguration uiConfiguration;
    private final SwaggerResourcesProvider swaggerResources;
    @Autowired
    public SwaggerController(SwaggerResourcesProvider swaggerResources) {
        this.swaggerResources = swaggerResources;
    }
    @GetMapping("/configuration/security")
    public Mono<ResponseEntity> securityConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
    }
    @GetMapping("/configuration/ui")
    public Mono<ResponseEntity> uiConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
    }
    @GetMapping("")
    public Mono swaggerResources() {
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }
}

cn.celinf.gateway.filter

SwaggerHeaderFilter类

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

启动Nacos\Seata\Sentinel

启动cart\stock\order\business

在保证一般访问正常的情况下

再启动gateway

可以通过下面路径访问之前的各个模块的业务

http://localhost:10000/nacos-stock/doc.html

http://localhost:10000/nacos-cart/doc.html

http://localhost:10000/nacos-order/doc.html

http://localhost:10000/nacos-business/doc.html

如果不使用网关一切正常,但是启动网关访问失败的话,就是gateway项目配置问题

Gateway和SpringMvc依赖冲突问题和解决

网关依赖


    org.springframework.cloud
    spring-cloud-starter-gateway

SpringMvc依赖


    org.springframework.boot
    spring-boot-starter-web

这两个依赖在同一个项目中时,默认情况下启动会报错

SpringMvc依赖中自带一个Tomcat服务器

而Gateway依赖中自带一个Netty服务器

因为在启动服务时这个两个服务器都想启动,会因为争夺端口号和主动权而发生冲突

我们需要在yml文件中添加配置解决

spring:
  main:
    web-application-type: reactive

学习记录,如有侵权请联系删除

点击这里复制本文地址 以上内容由nimo97整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

尼墨宝库 © All Rights Reserved.  蜀ICP备2024111239号-7