一、什么是Gateway?
Gateway是Spring生产系统上构建的API服务网关,基于Spring5、SpringBoot2和ProjectReactor等技术。它的目标是提供一种简单有效的方式对API进行路由,以及提供一些强大的过滤器功能,包括熔断、限流、重试等。
SpringCloud Gateway是SpringCloud的一个全新项目,基于WebFlux框架实现的,而webFlux底层使用了高性能的Reactor模式通信框架Netty。SpringCloud Gateway为微服务架构提供一种简单有效的统一的API路由管理方式,目的是为了替代Zuul成为全新一代的服务网关。
前面提到了服务网关,那什么又是服务网关呢?它是一个网络关口、通道,是整个微服务平台所有请求的统一入口,所有的客户端和服务消费端都只能通过这个关口来访问微服务。除了作为统一入口之外,它还可以处理非业务功能,承担认证授权、访问控制、路由、负载均衡、日志、缓存、映射、过滤、熔断、注册、服务编排、API管理、监控、统计分析等职责。所以选择一个优秀的服务网关对于开发微服务体系系统至关重要。以下是借鉴某伙伴的服务架构图:
二、Gateway的工作流程
要理解工作流程,先了解以下三个核心名词
1、Route路由:路由是构建网关的基本模块,由一系列的断言和过滤器组成,如果断言为true则匹配该路由。
2、predicate断言:预先设置的规则条件。开发人员可以匹配http请求中的内容,如果请求与断言相匹配则进行路由匹配。
3、Filte过滤器:过滤器是GatewayFilter的一个实例,它可以在请求被路由前后完成对请求的修改。Gateway中存在一系列过滤链,用于完成不同的处理(请求前可以做权限校验、流量监控、日志输出、协议转换等,请求后响应内容修改等)。
一个web请求进来,Gateway首先会进行条件匹配,定位真正的服务节点,并在转发web请求的前后进行一些额外的处理,这些处理就通过过滤器来实现,而前面的匹配就是通过断言来实现。以下是网关内部的工作流程图
三、服务网关模块的搭建
搭建服务网关模块的步骤:
1、引入jar包:除了引入gateway的jar包外,网关本身也是一种微服务,也需要注册到服务注册中心,因此还需要引入相应的注册中心客户端jar包,进行相应的配置(配置方式参考服务注册中心相关文档)。最后要注意的是,作为网关的模块请不要引入
与spring-boot-starter-web相关的包,否则会出现冲突报错。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
2、修改application.yml文件,配置网关路由规则。路由是多个,可以配置在routes节点下,如图所示表示配置了两个路由规则:
server: port: 9527 spring: application: name: cloud-gateway cloud: gateway: routes: - id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates: - Path=/payment/get/** #断言,路径相匹配的进行路由 - id: payment_routh2 #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates: - Path=/payment/lb/** #断言,路径相匹配的进行路由
当进行上述配置后,我们就可以不直接访问http://localhost:8001/payment/get/来调用微服务,而是通过http://localhost:9527/payment/get/来间接调用8001微服务。当客户端访问http://localhost:9527/****/***时,网关服务模块就根据路由规则进行匹配,从而调用不同的微服务(网关服务模块获取到请求地址后,用请求地址去断言,断言uri指定的微服务下是否存在predicates指定的路径模式,如果为true则调用)。
3、完成以上两步,启动网关服务模块,客户端向微服务的请求全部通过访问该服务来实现,到此简单的网关服务模块搭建完成。
除了2中配置文件实现路由配置的方式外,还可以通过硬编码的方式(代码中注入RouteLocator的Bean)来实现路由匹配:新建网关配置文件GatewayConfig类,写入如下类似代码即可
@Configuration public class GatewayConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) { RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes(); routes.route( id: "path_route_name", r -> r.path("/aaaa").uri("http://xxx.com/bbbb") /* /aaaa路径将会被路由到http://xxx.com/bbbb */ ).build();
return routes.build(); }
四、动态路由配置
默认情况下,Gateway会根据注册中心注册的服务列表,以微服务的名称为路径创建动态路由进行转发,实现动态路由功能。开启动态路由只需要在上述简单路由的基础上修改yml配置文件,开启动态路由功能即可,要注意创建动态路由时uri要写成服务名称的形式,如下所示:
server: port: 9527 spring: application: name: cloud-gateway cloud: gateway: discovery: locator : enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由 routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: http://cloud-server-name #这里写微服务的名称,不要用地址 predicates: - Path=/payment/get/** #断言,路径相匹配的进行路由
五、Gateway过滤器Filter
过滤器允许以某种方式修改传入的HTTP请求或返回的HTTP响应。过滤器作用于某些特定路由。Spring Cloud Gateway包括许多内置的 Filter工厂,这些工厂负责创建Filter过滤器实例,同时也支持自定义过滤器。多个过滤器结合使用可以形成过滤器链对http请求进行处理。
1、内置的过滤器:分为单一过滤器GatewayFilter和全局过滤器GlobalFilter,作用于业务逻辑之前或业务逻辑之后。使用方式与断言类似,通过配置实现过滤功能,yml配置参考代码如下
server: port: 9527 spring: application: name: cloud-gateway cloud: gateway: discovery: locator : enabled: true routes: - id: payment_routh uri: http://cloud-server-name filters: - AddRequestParameter=X-Request-Id,1024 #过搪器工厂会在匹配的请求头加上一对请求头,名称为X-Request-Id值为1024 predicates: - Path=/payment/get/**
2、自定义全局过滤器:新建MyGatewayFilter类,实现GlobalFilter,Orderd接口,重写filter方法和getOrder方法,参考代码如下
@Component public class MyGatewayFilter implements GlobalFilter,ordered /*实现接口*/ { @override public Mono<Void> filter(ServerwebExchange exchange,GatewayFilterchain chain) /*exchange是请求的上下文,chain表示下一过滤器*/ { string uname = exchange.getRequest().getQueryParams( ).getFirst( key: "uname"); if(uname == nul1) { exchange.getResponse().setstatuscode(Httpstatus.NOT_ACCEPTABLE); return exchange.getResponse().setcomplete(); /*如果uname为空,则设置状态为不被接受,然后返回结果*/ } return chain.filter(exchange); /*继续执行下一过滤器*/ } @Override public int getorder() { return 0; /*设置过滤器顺序*/ } }
以上代码的作用是过滤非法用户,只需完成以上编码,无需额外配置即可实现全局过滤功能。内置的过滤器此处不再详述。
六、Gateway常用的断言Predicate
分析前面的yml配置文件,发现predicates是复数形式,表明我们可以设置多个断言,进行更精确的匹配。Gateway提供了如下几种断言规则:
规则 | 配置格式 | 说明 |
After |
- After=2020-02-05T15:10:03.685+08:00[Asia/Shanghai] |
匹配在此时间之后的访问 |
Before | - Before=2020-02-05T15:10:03.685+08:00[Asia/Shanghai] | 匹配在此时间之前的访问 |
Between | - Between=2020-02-05T15:10:03.685+08:00[Asia/Shanghai], 2020-02-05T15:10:03.685+08:00[Asia/Shanghai] | 匹配在该时间段的访问 |
Cookie | - Cookie=username,zhangsan | 匹配cookie名称为zhangsan |
Header | - Header=X-Request-Id,\d+ | 匹配请求头要有x-Request-Id属性并且值为整数 |
Host | - Host=**.yy.com | 匹配主机名为.yy.com |
Method | - Method=Get | 匹配get请求 |
Path | - Path=/payment/get/** | 匹配路径 |
Query | - Query=username,\d+ | 匹配查询字符串存在username并且值为整数 |
RemoteAddr | - RemoteAddr =192.168.0.1/16 | 匹配Remote Adress为192.168.0.1 |