gateway简介和灰度发布实现方案
gateway介绍
官方文档:https://docs.spring.io/spring-cloud-gateway/docs/2.2.8.RELEASE/reference/html/#gateway-starter
网关请求处理过程

客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。此处理程序运行通过特定于请求的过滤器链发送请求。滤波器被虚线划分的原因是滤波器可以在发送代理请求之前或之后执行逻辑。执行所有“pre”过滤器逻辑,然后进行代理请求。在发出代理请求之后,执行“post”过滤器逻辑。
在没有端口的路由中定义的URI将分别为HTTP和HTTPS URI获取默认端口设置为80和443。
- DispatcherHandler:所有请求的调度器,负载请求分发
- RoutePredicateHandlerMapping:路由谓语匹配器,用于路由的查找,以及找到路由后返回对应的WebHandler,DispatcherHandler会依次遍历HandlerMapping集合进行处理
- FilteringWebHandler : 使用Filter链表处理请求的WebHandler,RoutePredicateHandlerMapping找到路由后返回对应的FilteringWebHandler对请求进行处理,FilteringWebHandler负责组装Filter链表并调用链表处理请求。
https://www.cnblogs.com/bjlhx/p/9588739.html
filter的作用和生命周期
作用
当我们有很多个服务时,比如下图中的user-service、goods-service、sales-service等服务,客户端请求各个服务的Api时,每个服务都需要做相同的事情,比如鉴权、限流、日志输出等。
对于这重复的工作,有没有办法做的更好,答案是肯定的。在微服务的上一层加一个全局的权限控制、限流、日志输出的Api Gatewat服务,然后再将请求转发到具体的业务服务层。这个Api Gateway服务就是起到一个服务边界的作用,外接的请求访问系统,必须先通过网关层。
生命周期
Spring Cloud Gateway同zuul类似,有“pre”和“post”两种方式的filter。客户端的请求先经过“pre”类型的filter,然后将请求转发到具体的业务服务,比如上图中的user-service,收到业务服务的响应之后,再经过“post”类型的filter处理,最后返回响应到客户端。
与zuul不同的是,filter除了分为“pre”和“post”两种方式的filter外,在Spring Cloud Gateway中,filter从作用范围可分为另外两种,一种是针对于单个路由的gateway filter,它在配置文件中的写法同predict类似;另外一种是针对于所有路由的global gateway filer。现在从作用范围划分的维度来讲解这两种filter。
gateway filter
过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。过滤器可以限定作用在某些特定请求路径上。 Spring Cloud Gateway包含许多内置的GatewayFilter工厂。
GatewayFilter工厂同上一篇介绍的Predicate工厂类似,都是在配置文件application.yml中配置,遵循了约定大于配置的思想,只需要在配置文件配置GatewayFilter Factory的名称,而不需要写全部的类名,比如AddRequestHeaderGatewayFilterFactory只需要在配置文件中写AddRequestHeader,而不是全部类名。在配置文件中配置的GatewayFilter Factory最终都会相应的过滤器工厂类处理。
Spring Cloud Gateway 内置很多过滤器工厂,在包``org.springframework.cloud.gateway.filter.factory包下
下
下面以AddRequestHeader GatewayFilter Factory
作为示例。
AddRequestHeader GatewayFilter Factory
若在yml文件中,加入以下的配置:
server:
port: 8081
spring:
profiles:
active: add_request_header_route
---
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://httpbin.org:80/get
filters:
- AddRequestHeader=X-Request-Foo, Bar
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
profiles: add_request_header_route
在上述的配置中,工程的启动端口为8081,配置文件为add_request_header_route,在add_request_header_route配置中,配置了roter的id为add_request_header_route,路由地址为http://httpbin.org:80/get
,该router有AfterPredictFactory,有一个filter为AddRequestHeaderGatewayFilterFactory(约定写成AddRequestHeader),AddRequestHeader过滤器工厂会在请求头加上一对请求头,名称为X-Request-Foo,值为Bar。为了验证AddRequestHeaderGatewayFilterFactory是怎么样工作的,查看它的源码,AddRequestHeaderGatewayFilterFactory的源码如下:
public class AddRequestHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest().mutate()
.header(config.getName(), config.getValue())
.build();
return chain.filter(exchange.mutate().request(request).build());
};
}
}
由上面的代码可知,根据旧的ServerHttpRequest创建新的 ServerHttpRequest ,在新的ServerHttpRequest加了一个请求头,然后创建新的 ServerWebExchange ,提交过滤器链继续过滤。
https://juejin.cn/post/6844903741867425800
来自客户端的请求经过filter后,会发往目标业务服务器。
灰度发布
灰度发布 Gray Release(又名金丝雀发布 Canary Release)
- 概念
不停机旧版本,部署新版本,低比例流量(例如:5%)切换到新版本,高比例流量(例如:95%)走旧版本,通过监控观察无问题,逐步扩大范围,最终把所有流量都迁移到新版本上,下线旧版本。属无损发布
- 优点
灵活简单,不需要用户标记驱动。安全性高,新版本如果出现问题,只会发生在低比例的流量上
- 缺点
流量配比递增的配置修改,带来额外的操作成本。用户覆盖狭窄,低比例流量未必能发现所有问题
以上是基于流量控制的方式进行灰度发布。
对于我们目前应用,并没有服务集群,没有流量百分比区分的要求。也没有服务监控,无法判断这小部分流量是否正常。
于是可以考虑基于版本控制的灰度发布。它和基于流量控制的方式类似,区别在于前端服务不属于路由范围;更新的功能是否正常由测试人员检测。由此带来一个增加操作复杂性的问题:用户需要经过前端进行标记,但这个问题目前能接受。
基于版本的灰度发布
我们在发布更新时,线上环境维护2个版本:beta和prod版本。
如线上正在运行的旧版本patient v1 和需要更新的新版本patient v2,其中patient v1是prod,patient v2是beta,更新完成后patient v2成为prod,patient v1的prod下线。
基于上图,95%用户
即目前线上的patient v1 5%用户
即新版本patient v2。
基于版本控制的灰度发布步骤:
假设当前线上版本v1,需要更新的版本是v2。步骤如下:
- 后端发布v2版本至生产环境即beta版本。前端发布beta版本连接后端beta版本;
- 测试人员访问beta版本,进行测试;
- 测试没有问题后,前端发布beta版本至线上,beta均晋升为prod版本。用户访问v2版本。
- 测试人员确认无误后。后端下线v1。
若更新后出现了之前未发现的BUG,视影响范围有2种解决方案。
- 影响核心功能的bug,且不能半小时内修复。前后端可选择将v2回滚至v1。
- 不影响核心功能的bug,考虑在下一个版本解决。更新步骤如上。
功能实现
上一个章节的gateway介绍中,可以看到前端请求经过一系列GatewayFilter后发送到具体的业务服务。
我们可以在请求发送到业务服务前修改目标地址为符合当前请求版本的服务ip和端口。
实现思路:
-
新建带版本控制的灰度路由
-
新建自定义filter
-
灰度路由从nacos拉取服务实例元数据,根据匹配算法,返回符合要求的实例给filter
-
filter设置请求地址到exchange,并进入下一个filter,且要保证请求地址不被更改。
实现如下:
添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
GrayLoadBalancerClientFilter
类
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultRequest;
import org.springframework.cloud.client.loadbalancer.reactive.Request;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
import org.springframework.cloud.gateway.support.DelegatingServiceInstance;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.cloud.gateway.support.