SpringCloud之Gateway学习笔记


前言

在微服务架构里,服务的粒度被进一步细分,各个业务服务可以被独立的设计、开发、测试、部署和管理。这时,各个独立部署单元可以用不同的开发测试团队维护,可以使用不同的编程语言和技术平台进行设计,这就要求必须使用一种语言和平 台无关的服务协议作为各个单元间的通讯方式。


一、API 网关是什么?

网关的角色是作为一个 API 架构,用来保护、增强和控制对于 API 服务的访问。
API 网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。因此,隐藏在 API 网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。

二、什么是Gateway

Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。

Spring Cloud Gateway 可以看做是一个 Zuul 1.x 的升级版和代替品,比 Zuul 2 更早的使用 Netty 实现异步 IO,从而实现了一个简单、比 Zuul 1.x 更高效的、与 Spring Cloud 紧密配合的 API 网关。

Spring Cloud Gateway 里明确的区分了 Router 和 Filter,并且一个很大的特点是内置了非常多的开箱即用功能,并且都可以通过 SpringBoot 配置或者手工编码链式调用来使用。比如内置了 10 种 Router,使得我们可以直接配置一下就可以随心所欲的根据 Header、或者 Path、或者 Host、或者 Query 来做路由。比如区分了一般的 Filter 和全局 Filter,内置了 20 种 Filter 和 9 种全局 Filter,也都可以直接用。当然自定义 Filter 也非常方便。

三、核心概念

  • 路由(Route): 路由信息的组成:由一个ID、一个目的URL、一组断言工厂、一组Filter组成。如果路由断言为真,说明请求URL和配置路由匹配。

  • 断言(Predicate): Spring Cloud Gateway中的断言函数输入类型是Spring 5.0框架中的 ServerWebExchange。Spring Cloud Gateway的断言函数允许开发者去定义匹配来自于HTTP Request中的 任何信息比如请求头和参数。

  • 过滤器(Filter): 一个标准的Spring WebFilter。 Spring Cloud Gateway中的Filter分为两种类型的Filter,分 别是Gateway Filter和Global Filter。过滤器Filter将会对请求和响应进行修改处理

四、举个栗子

为了更好的理解gateway, 直接上代码吧!!!

首先我们先创造一个gateway工程

Maven依赖

需要导入的依赖如下:

<dependencies>
	<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>
</dependencies>

实体类

然后编写实体类
不要忘记加 @EnableDiscoveryClient 注解(Gateway也需要在Eureka注册)

@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
	public static void main(String[]args){
		SpringApplication.run(GatewayApplication.class, args);
	} 
}

配置文件

接下来编写配置文件(Gateway工程下的application.yml)

server:
  port: 10010
eureka:
  client:
    service-url:
      defaultZone: HTTP://127.0.0.1:10086/eureka
  instance: 
    prefer-ip-address: true
spring:
  application:
    name: api-gateway 
  cloud:
    gateway:
      routes:
        # 路由id,可以随意写
        - id: bill-service
          # 代理的服务地址
          uri: HTTP://127.0.0.1:9091 
          # 路由断言,可以配置映射路径 
          predicates:
            - Path=/bill/**

将符合 Path 规则的一切请求,都代理到 uri 参数指定的地址 本例中,我们将路径中包含有 /user/** 开头的请求, 代理到http://127.0.0.1:9091

http://localhost:10010/user/7 -> http://localhost:9091/user/7


面向服务的路由

但是, 在刚才的路由规则中,把路径对应的服务地址写死了!如果同一服务有多个实例的话,这样做显然不合理。 应该根据服务的名称,去Eureka注册中心查找服务对应的所有实例列表,然后进行动态路由!

server:
  port: 10010
eureka:
  client:
    service-url:
      defaultZone: HTTP://127.0.0.1:10086/eureka
  instance: 
    prefer-ip-address: true
spring:
  application:
    name: api-gateway 
  cloud:
    gateway:
      routes:
        # 路由id,可以随意写
        - id: bill-service
          # 代理的服务地址
          uri: lb://bill-service 
          # 路由断言,可以配置映射路径 
          predicates:
            - Path=/bill/**



Predicate

说白了 Predicate 就是为了实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理,接下来我们接下 Spring Cloud GateWay 内置几种 Predicate 的使用。

1. 通过请求路径匹配

在刚刚的配置文件中我们其实已经用到了path断言,Path Route Predicate 接收一个匹配路径的参数来判断是否走路由。

spring:
  cloud:
    gateway:
      routes:
      - id: bill-service
        uri: lb://bill-service 
        predicates:
        - Path=/bill/**

如果请求路径符合要求,则此路由将匹配,例如:/bill/1 或者 /bill/list

使用 curl 测试,命令行输入:

curl http://localhost:10010/bill/1
curl http://localhost:10010/bill/2
curl http://localhost:10010/bull/1

经过测试第一和第二条命令可以正常获取到页面返回值,最后一个命令报 404,证明路由是通过指定路由来匹配。


2. 通过时间匹配

Predicate 支持设置一个时间,在请求进行转发的时候,可以通过判断在这个时间之前或者之后进行转发。比如我们现在设置只有在 2019 年 1 月 1 日才会转发到我的网站,在这之前不进行转发,我就可以这样配置:

spring:
  cloud:
    gateway:
      routes:
      - id: bill-service
        uri: lb://bill-service 
        predicates:
         - After=2018-01-20T06:06:06+08:00[Asia/Shanghai]

那么After后面这一串时间该怎么写呢?
我们可以直接运行下面的代码来得到当前的时间:

import java.time.ZonedDateTime

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

Before Route Predicate 刚好相反,在某个时间之前的请求的请求都进行转发。我们把上面路由规则中的 After 改为 Before,如下:

spring:
  cloud:
    gateway:
      routes:
      - id: bill-service
        uri: lb://bill-service 
        predicates:
         - Before=2018-01-20T06:06:06+08:00[Asia/Shanghai]

就表示在这个时间之前可以进行路由,在这时间之后停止路由,修改完之后重启项目再次访问地址http://localhost:10010,页面会报 404 没有找到地址。

除过在时间之前或者之后外,Gateway 还支持限制路由请求在某一个时间段范围内,可以使用 Between Route Predicate 来实现。

spring:
  cloud:
    gateway:
      routes:
      - id: bill-service
        uri: lb://bill-service 
        predicates:
         - Between=2018-01-20T06:06:06+08:00[Asia/Shanghai], 2019-01-20T06:06:06+08:00[Asia/Shanghai]

这样设置就意味着在这个时间段内可以匹配到此路由,超过这个时间段范围则不会进行匹配。通过时间匹配路由的功能很酷,可以用在限时抢购的一些场景中。


3. 通过 Cookie 匹配

Cookie Route Predicate 可以接收两个参数,一个是 Cookie name , 一个是正则表达式,路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。

spring:
  cloud:
    gateway:
      routes:
      - id: bill-service
        uri: lb://bill-service 
        predicates:
         - Cookie=guan, ch.p

使用 curl 测试,命令行输入:

curl http://localhost:10010 --cookie "guan=ch.p"

则会返回页面代码,如果去掉–cookie “guan=ch.p”,后台汇报 404 错误。

Header Route Predicate 和 Cookie Route Predicate 一样,也是接收 2 个参数,一个 header 中属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。

spring:
  cloud:
    gateway:
      routes:
      - id: bill-service
        uri: lb://bill-service 
        predicates:
        - Header=X-Request-Id, \d+

使用 curl 测试,命令行输入:

curl http://localhost:10010  -H "X-Request-Id:2333" 

则返回页面代码证明匹配成功。将参数-H "X-Request-Id:666666"改为-H "X-Request-Id:abc"再次执行时返回 404 证明没有匹配。


4. 通过 Host 匹配

Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个 ant 分隔的模板,用.号作为分隔符。它通过参数中的主机地址作为匹配规则。

spring:
  cloud:
    gateway:
      routes:
      - id: bill-service
        uri: lb://bill-service 
        predicates:
        - Host=**.niubi.com

使用 curl 测试,命令行输入:

curl http://localhost:10010  -H "Host: www.niubi.com" 
curl http://localhost:10010  -H "Host: md.niubi.com" 

经测试以上两种 host 均可匹配到 host_route 路由,若不写host 参数则会报 404 错误。


5. 通过请求方式匹配

可以通过是 POST、GET、PUT、DELETE 等不同的请求方式来进行路由。

spring:
  cloud:
    gateway:
      routes:
      - id: bill-service
        uri: lb://bill-service 
        predicates:
        - Method=GET

使用 curl 测试,命令行输入:

# curl 默认是以 GET 的方式去请求
curl http://localhost:8080

测试返回页面代码,证明匹配到路由,我们再以 POST 的方式请求测试。

# curl 默认是以 GET 的方式去请求
curl -X POST http://localhost:8080

返回 404 没有找到,证明没有匹配上路由。


6. 通过请求参数匹配

Query Route Predicate 支持传入两个参数,一个是属性名一个为属性值,属性值可以是正则表达式。

spring:
  cloud:
    gateway:
      routes:
      - id: bill-service
        uri: lb://bill-service 
        predicates:
        - Query=name

这样配置,只要请求中包含 name 属性的参数即可匹配路由。

使用 curl 测试,命令行输入:

curl localhost:10010?name=zhangsan

经过测试发现只要请求汇总带有 name 参数即会匹配路由,不带 name 参数则不会匹配。

还可以将 Query 的值以键值对的方式进行配置,这样在请求过来时会对属性值和正则进行匹配,匹配上才会走路由。

spring:
  cloud:
    gateway:
      routes:
      - id: bill-service
        uri: lb://bill-service 
        predicates:
        - Query=name,ca.

这样只要当请求中包含 name 属性并且参数值是以 ca 开头的长度为三位的字符串才会进行匹配和路由。

使用 curl 测试,命令行输入:

curl localhost:10010?name=cat

测试可以返回页面代码,将 keep 的属性值改为 calc 再次访问就会报 404, 证明路由需要匹配正则表达式才会进行路由。


7. 通过请求 ip 地址进行匹配

Predicate 也支持通过设置某个 ip 区间号段的请求才会路由,RemoteAddr Route Predicate 接受 cidr 符号 (IPv4 或 IPv6) 字符串的列表(最小大小为 1),例如 192.168.0.1/16 (其中 192.168.0.1 是 IP 地址,16 是子网掩码)。

spring:
  cloud:
    gateway:
      routes:
      - id: bill-service
        uri: lb://bill-service 
        predicates:
        - RemoteAddr=192.168.1.1/24

可以将此地址设置为本机的 ip 地址进行测试。


Filter

Gateway作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作往往是通过网关提供的过滤器来实现 的。前面的 路由前缀 章节中的功能也是使用过滤器实现的。

Gateway自带过滤器有几十个,常见自带过滤器有:

过滤器名称说明
AddRequestHeader对匹配上的请求加上Header
AddRequestParameters对匹配上的请求路由添加参数
AddResponseHeader对从网关返回的响应添加Header
StripPrefix对匹配上的请求路径去除前缀

详细的说明参考官方文档

1. 执行生命周期

Spring Cloud Gateway 的 Filter 的生命周期也类似Spring MVC的拦截器有两个:“pre” 和 “post”。“pre”和 “post” 分别会在请求被执行前调用和被执行后调用
在这里插入图片描述
这里的 pre 和 post 可以通过过滤器的 GatewayFilterChain 执行filter方法前后来实现。


2. 默认过滤器

server:
  port: 10010
spring:
  application:
    name: api-gateway 
  cloud:
    gateway:
      routes:
      - id: bill-service
        uri: lb://bill-service 
        predicates:
          - Path=/api/bill/
        filters:
          - StripPrefix=1
      default-filters:
	    - AddResponseHeader=X-Response-Foo, Bar
	    - AddResponseHeader=abc-myname,lxs
eureka:
  client: 
    service-url:
	  defaultZone: http://127.0.0.1:10086/eureka 
  instance:
    prefer-ip-address: true

上述配置后,再访问 http://localhost:10010/api/bill/7 的话;那么可以从其响应中查看到如下信息:
在这里插入图片描述

3. 自定义全局过滤器

需求:编写全局过滤器,在过滤器中检查请求中是否携带token请求头。如果token请求头存在则放行;如果token 为空或者不存在则设置返回的状态码为:未授权也不再执行下去。

在gateway工程编写全局过滤器类MyGlobalFilter

@Component
  public class MyGlobalFilter implements GlobalFilter, Ordered {
  
      @Override
      public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
	  	System.out.println("--------------全局过滤器MyGlobalFilter------------------"); 
	  	String token = exchange.getRequest().getHeaders().getFirst("token"); 		if(StringUtils.isBlank(token)){
	    	exchange.getResponse().setStatusCode(HTTPStatus.UNAUTHORIZED); 				return exchange.getResponse().setComplete();
		}
		return chain.filter(exchange); 
	}
	
      @Override
      public int getOrder() {
       //值越小越先执行
		return 1; 
	}
	
}

访问 http://localhost:10010/api/user/7 ,报401错误
http://localhost:10010/api/user/7?token=abc ,通过

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值