网关

网关:

网关 = 路由转发 + 过滤器

网关统一服务入口,可方便实现对平台众多服务接口进行管控 ,对访问服务的身份认证、防报文重放与防数据篡改、功能调用的业务鉴权、响应数据的脱敏、流量与并发控制,甚至基于API调用的计量或者计费等等。

img点击并拖拽以移动

网关的作用:

  • 1.网关可以实现服务的统一管理
  • 2.网关可以解决微服务中通用代码的冗余问题(如权限控制,相应数据的脱敏,流量监控,限流,基于API调用的计量和计费等)

常用的网关组件:

1.zuul

zuul是从设备和网站到Netflix流媒体应用程序后端的所有请求的前门。作为一个边缘服务应用程序,zul被构建为支持动态路由、监视、弹性和安全性。

目前zuul组件已经从1.0更新到2.0,但是作为springcloud官方不再推荐使用zuul2.0,但是依然支持zuul2.

2.Gateway

提供了一个在springmvc之上构建API网关的库。springcloudgateway旨在提供一种简单而有效的方法来路由到api,并为api提供横切关注点,比如:安全性、监控/度量和弹性。

Gateway具有如下特性:

  • 基于springboot2.x 和 spring webFlux 和 Reactor 构建 响应式异步非阻塞IO模型
  • 动态路由
  • 请求过滤

网关的使用

网关服务是一个独立的SpringBoot应用

开发网关动态路由

1.引入gateway网关依赖:

注意:不用引入springboot-starter-web,因为gateway为了效率使用webflux进行异步非阻塞模型的实现,webflux也是一种web模型,如果因日springboot-starter-web,会产生冲突,报如下错误:

img点击并拖拽以移动

<!--引入springboot 网关中不能使用springmvc 的web模型-->
   <!--<dependency>-->
   <!--<groupId>org.springframework.boot</groupId>-->
   <!--<artifactId>spring-boot-starter-web</artifactId>-->
   <!--</dependency>-->

   <!--引入consul-->
   <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-consul-discovery</artifactId>
   </dependency>
   <!--引入actuator-->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-actuator</artifactId>
   </dependency>
   <!--引入gateway-->
   <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-gateway</artifactId>
   </dependency>

2.编写网关配置:

server:
  port: 7979
spring:
  application:
    name: GATEWAY
  cloud:
    consul:
      host: localhost
      port: 8500
    gateway:
      routes:
        - id: product_router #路由对象唯一标识
          uri: http://localhost:8799 #商品服务地址  
          predicates:  #断言 用来配置路由规则
            - Path=/product    
#-----------当访问http://localhost:7979/product时会转发到:http://localhost:8799/product

        - id: category_router #路由对象唯一标识
          uri: http://localhost:8787 #类别服务地址 如: 
          predicates:  #断言 用来配置路由规则
            - Path=/category
            
#-----------当访问http://localhost:7979/category时会转发到:http://localhost:8787/category

3.启动服务:网关服务,商品服务,类别服务

商品服务和类别服务接口可见:服务注册中心1.3.1 ↓ http://sovzn.github.io/2021/07/27/%E6%9C%8D%E5%8A%A1%E9%97%B4%E7%9A%84%E9%80%9A%E4%BF%A1/

img点击并拖拽以移动

4.测试

直接在网关服务的端口7979下访问/category和/product:

img点击并拖拽以移动

img点击并拖拽以移动

可以发现成功转发至相应服务

Java方式配置路由(了解)

创建一个配置类(优先级高于通过配置文件配置),效果与通过配置文件配置一样:

@Configuration
public class GatewayConfig {
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                //类别路由
                .route("category_router", r -> r.path("/category")
                        .uri("http://localhost:8787"))
                //商品路由
                .route("product_router", r -> r.path("/product")
                        .uri("http://localhost:8799"))
                //.....
                .build();
    }
}

网关的配置细节

1. - path后可写多个路径,且支持通配符如:

- Path=/product,//product/{id},/product/**

img点击并拖拽以移动

2.配置实现网关请求转发的负载均衡,Gateway组件底层集成了Ribbon组件。

语法:uri: lb://服务id 其中lb其实就是LoadBalance

如:

server:
  port: 7979
spring:
  application:
    name: GATEWAY
  cloud:
    consul:
      host: localhost
      port: 8500
    gateway:
      routes:
        - id: product_router #路由对象唯一标识
          #uri: http://localhost:8799 #商品服务地址  http://localhost:8799/product
          uri: lb://PRODUCT   #PRODUCT:商品服务服务名,实现请求负载均衡处理 
          predicates:  #断言 用来配置路由规则
            - Path=/product,/product/{id},/product/**
        - id: category_router #路由对象唯一标识
          #uri: http://localhost:8787 #类别服务地址 如: http://localhost:8787/list
          uri: lb://CATEGORY   #CATEGORY:类别服务服务名,实现请求负载均衡处理
          predicates:  #断言 用来配置路由规则
            - Path=/category

3.查看网关路由规则列表

gateway提供路由访问规则列表的web界面,但是默认是关闭的,如果想要查看服务路由规则可以在配置文件中开启:

management:
  endpoints:
    web:
      exposure:
        include: "*"   #开启所有web端点暴露

访问路由管理列表地址:

http: //IP:网关服务端口/actuator/gateway/routes

img点击并拖拽以移动

网关的断言和过滤

常用的断言

1.Gateway支持多种方式的predicate:

img点击并拖拽以移动

- After=2020-07-21T11:33:33.993+08:00[Asia/Shanghai]  			`指定日期之后的请求进行路由
- Before=2021-09-20T20:38:55.675+08:00[Asia/Shanghai]     `指定日期之前的请求进行路由
- Between=2021-09-20T20:38:55.675+08:00[Asia/Shanghai], 2021-09-21T20:38:55.675+08:00[Asia/Shanghai]   
- Cookie=username,sovzn																		`基于指定cookie的请求进行路由
- Cookie=username,[A-Za-z0-9]+															`基于指定cookie的请求进行路由	
	`curl http://localhost:7979/product --cookie "username=sovzn"
- Header=X-Request-Id, \d+																 `基于请求头中的指定属性的正则匹配路由(这里全是整数)
	`curl http://localhost:7979/product -H "X-Request-Id:11"
- Method=GET,POST		 `基于指定的请求方式请求进行路由

官方更多: https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.3.RELEASE/reference/html/#the-cookie-route-predicate-factory

- After

指定日期之后的请求进行路由:如下:在2021-09-20 20:38:55 后的请求才有效

gateway:
      routes:
        - id: product_router #路由对象唯一标识
          #uri: http://localhost:8799 #商品服务地址  
          uri: lb://PRODUCT   #PRODUCT:商品服务服务名,实现请求负载均衡处理 如:/product/product/list
          predicates:  #断言 用来配置路由规则
            - Path=/product,/product/{id},/product/**
            - After=2021-09-20T20:38:55.675+08:00[Asia/Shanghai]
            # ---------在2021-09-20 20:38:55 后的请求才有效------

其中,after后的参数为jdk1.8后的新属性:ZonedDateTime(当前区域时间):

public class Test {
    public static void main(String[] args) {
        System.out.println(ZonedDateTime.now());
    }
}




2021-09-20T20:50:36.804+08:00[Asia/Shanghai]

Process finished with exit code 0

测试:在2021-09-20 20:38:55之前的请求↓

img点击并拖拽以移动 请求报404

在2021-09-20 20:38:55之后的请求↓:

img点击并拖拽以移动

请求成功,返回相应数据

基于指定cookie的请求进行路由

对于- cookie的测试,可以通过curl命令(Win10已预装curl)向请求中添加cookie键值对参数

1.指定cookie中含有某键值信息的请求: Cookie=username,sovzn

gateway:
      routes:
        - id: product_router #路由对象唯一标识
          #uri: http://localhost:8799 #商品服务地址  
          uri: lb://PRODUCT   #PRODUCT:商品服务服务名,实现请求负载均衡处理 如:/product/product/list
          predicates:  #断言 用来配置路由规则
            - Path=/product,/product/{id},/product/**
            - After=2021-09-20T20:38:55.675+08:00[Asia/Shanghai]
            - Cookie=username,sovzn

img点击并拖拽以移动

**2.支持正则表达式: - Cookie=username,[A-Za-z0-9]+ **

**3.可以同时配置多条 - Cookie **

请求中必须还有指定的请求头:如 Header=X-Request-Id,1024

gateway:
      routes:
        - id: product_router #路由对象唯一标识
          #uri: http://localhost:8799 #商品服务地址  
          uri: lb://PRODUCT   #PRODUCT:商品服务服务名,实现请求负载均衡处理 如:/product/product/list
          predicates:  #断言 用来配置路由规则
            - Path=/product,/product/{id},/product/**
            - After=2021-09-20T20:38:55.675+08:00[Asia/Shanghai]
            #- Before=2021-04-20T10:23:22.124+08:00[Asia/Shanghai]
            #- Between=2021-04-20T10:23:22.124+08:00[Asia/Shanghai],2021-04-20T10:25:22.124+08:00[Asia/Shanghai]
            #- Cookie=username,sovzn
            #- Cookie=username,[A-Za-z0-9]+
            - Header=X-Request-Id,1024

请求头中未加入X-Request-Id=1024的访问:

img点击并拖拽以移动

请求头中加入X-Request-Id=1024的访问:成功,并返回相应数据

img点击并拖拽以移动

- Method

指定请求的请求方式,如:- Method=GET

——- 综上。断言可以理解为一种过滤,只转发符合某种要求的请求

常用的Filter

路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由筛选器的作用域是特定路由。

内置Filter

  • AddRequestHeader:给转发的请求加入指定请求头信息
  • AddRequestParamete:给转发的请求加入指定参数
  • AddResponseHeader :给转发请求的响应加入指定头信息
  • PrefixPath:给转发的请求的URL加入指定前缀信息
  • StripPrefix=n:去掉锁转发的请求的n个前缀

测试:

    gateway:
      routes:
        - id: product_router #路由对象唯一标识
          #uri: http://localhost:8799 #商品服务地址 
          uri: lb://PRODUCT   #PRODUCT:商品服务服务名,实现请求负载均衡处理 
          predicates:  #断言 用来配置路由规则
            - Path=/product,/product/{id},/product/**
            - After=2021-09-20T20:38:55.675+08:00[Asia/Shanghai]
          filters:
            - AddRequestHeader=User-Name, sovzn
            - AddRequestParameter=color, blue
            - AddResponseHeader=X-Response-Red, Blue
            #- PrefixPath=/mypath #加入指定前缀filter
            #- StripPrefix=1 #去掉请求路径中n级前缀

        - id: category_router #路由对象唯一标识
          uri: lb://CATEGORY
          predicates:  #断言 用来配置路由规则
            - Path=/category
#          filters:
#            - StripPrefix=1  # /list

在商品服务PRODUCT的方法中添加接收参数的方法:

@GetMapping("product")
   public String product(HttpServletRequest request,String color){
       System.out.println("获取Filter中设置在请求头信息:"+request.getHeader("User-Name"));
       System.out.println("获取Filter中设置的请求参数:"+color);
       return "product ok,当前服务端口"+port;
   }

测试访问:

img点击并拖拽以移动

img点击并拖拽以移动

img点击并拖拽以移动

- PrefixPath=/mypath #加入指定前缀filter

- PrefixPath=/mypath #加入指定前缀filter

在商品服务PRODUCT中加入URL为”/mypath/product”的方法

@GetMapping("/mypath/product")
   public String mypathproduct(HttpServletRequest request,String color){
       return "product ok,URL为/mypath/product,当前服务端口"+port;
   }

测试:

img点击并拖拽以移动

- StripPrefix=1 #去掉请求路径中n级前缀  不测试了🥱

注意:

如果两个路由中有同样的请求路径,如下的/list

gateway:
     routes:
       - id: product_router 
         uri: http://localhost:8799
         # uri: lb://PRODUCT   
         predicates: 
           - Path=/list

       - id: category_router
         uri: http://localhost:8787 
         #uri: lb://CATEGORY
         predicates:  
           - Path=/list

会按照路由配置顺序转发,即当通过网关服务访问/list时,会转发到http://localhost:8799/list

自定义全局Filter

注意:自定义的全局Filter是针对当前网关服务的所有请求,而在路由中配置的filters只是对当前路由要转发的请求进行一系列处理。

@Configuration
class MyGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("进入自定义的filter");
        if(exchange.getRequest().getQueryParams().get("name")!=null){
            System.out.println("用户身份信息合法,放行请求继续执行!!!");
            return chain.filter(exchange);
        }
        System.out.println("非法用户,拒绝访问!!!");
        return exchange.getResponse().setComplete();
    }

    @Override
    public int getOrder() {
/**
 *当有多个自定义Filter时用来排序,int数字来指定filter的执行顺序
 * 默认按照自然数字进行排序,-1表示在任何filter之前执行
 **/
        return -1;
    }
}

1.不带参直接访问:http://localhost:7979/product

img点击并拖拽以移动

2.带参name访问:

img点击并拖拽以移动