Springcloud-Gateway
网关概述
网关是一个处于应用程序或服务之间的系统,主要是对系统进行流量控制、保护内网服务;简单来说,网关就是整个微服务的入口,
Gateway简介
Gateway是在Spring生态系统之上构建的API网关服务,基于Spring 5,Spring Boot 2和 Project Reactor等技术。Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能, 例如:熔断、限流、重试等。
简单来说就是使用GateWay可以通过一个API来统一管理调用多个微服务。
Gateway的作用
- 反向代理:不暴露内网服务的ip,通过暴露网关ip来代理内网服务ip
- 鉴权:判断当前请求是否有权限访问服务
- 流量控制:为防止服务访问量过大而崩溃
- 熔断:当系统中的服务不可用时,网关可以服务降级
Gateway在微服务中网关所处的位置
为什么使用GateWay?
我们知道其实Springcloud还有一个网关组件教Zuul,那我们为什么不适用Zuul而是使用Gateway呢?其原因如下:
- Zuul2.0不靠谱,目前还是个半成品
- Gateway具有以下特性
- 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 进行构建
- 动态路由:能够匹配任何请求属性
- 可以对路由指定 Predicate(断言)和 Filter(过滤器)
- 集成Hystrix的断路器功能和SpringCloud的服务发现功能
- GateWay基于WebFlux模型
网关三大核心概念
-
路由器:一个路由是由若干个规则组成的请求转发规则模块,由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
-
断言:参考的是Java8的java.util.function.Predicate。开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由。
-
过滤器:指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
Gateway工作流程
- 客户端发送请求到网关
- 网关根据
路由器
将请求转发到对应的服务 - 执行过滤器链
快速入门
GateWay有两种配置网关的方式:
通过yml文件配置网关
和通过编程配置网关
。注意:在进行配置网关之前需要将当前服务注册到EurekaServer中并且不能导入
actuator
依赖。
通过yml文件配置网关
-
在pom文件中导入依赖
<!--gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
-
在yml文件中添加如下配置
spring: # 配置网关 cloud: gateway: routes: - id: payment_route1 # 路由的id,没有固定格式,但必须唯一 uri: http://localhost:8001 # 路由要匹配的微服务地址 predicates: - Path=/payment/** # 断言,路径相匹配的进行路由 - id: payment_route2 # 路由的id,没有固定格式,但必须唯一 uri: http://localhost:8001 # 路由要匹配的微服务地址 predicates: - Path=/payment/lb/** # 断言,路径相匹配的进行路由
通过编程配置网关
编写一个配置类返回一个RouteLocator对象
package cn.pigman.springcloud.config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GateWayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder locatorBuilder) {
RouteLocatorBuilder.Builder routes = locatorBuilder.routes();
//配置了一个id为path_route_pigman1的路由规则,
// 当访问地址为 http://localhost:9527/guonei 的时候会自动转发到地址:http://news.baidu.com/guonei
routes.route("path_route_pigman1",
r -> r.path("/guonei")
.uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
动态路由
上面通过yml配置网关的方式其实存在着一定的问题,我们配置路由的uri是写死的,但有时候uri对应的服务可能是采用集群的方式构建的,此时如果写死了uri就无法实现负载均衡。
所以默认情况下Gateway会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。
修改yml文件,添加如下配置
spring:
cloud:
gateway:
#开启从注册中心动态创建路由的功能,通过服务名进入路由
discovery:
locator:
enabled: true
routes:
- id: payment_route1 # 路由的id,没有固定格式,但必须唯一
uri: lb://CLOUD-PAYMENT-SERVICE # 路由要匹配的微服务地址
predicates:
- Path=/payment/** # 断言,路径相匹配的进行路由
- After=2022-09-26T15:19:38.469190500+08:00[Asia/Shanghai]
- Cookie=username,pigman
- id: payment_route2 # 路由的id,没有固定格式,但必须唯一
uri: lb://CLOUD-PAYMENT-SERVICE # 路由要匹配的微服务地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
断言
简介
Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以进行组合,组合之间用逻辑and来连接;通过这些断言工厂可以写出不同的路由规则。
常用断言
- Path:根据路径匹配
- After/Before/Between:根据时间规则匹配
- Cookie:根据是否携带cookie匹配
- Host:根据主机名匹配
- Method:根据请求方法进行匹配
总结:说白了,Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。
过滤器
简介
路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。Spring Cloud Gateway 内置了多种路由过滤器,他们都由GatewayFilter的工厂类来产生。
分类
SpringCloud的过滤器按生命周期可以分为 pre
和 post
,按种类可以分为 GlobalFileter
和 GatewayFilter
。
自定义过滤器
自定义一个全局GlobalFilter,需要实现两个接口:
GlobalFilter
:编写过滤器的业务逻辑Ordered
:规定当前过滤器的顺序
//全局过滤器
@Component
@Slf4j
public class MyLogGateWayFilter implements Ordered, GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//执行过滤器逻辑
log.info("*************** come in MyLogGateWayFilter:" + new Date());
ServerHttpRequest request = exchange.getRequest();//获取请求对象
MultiValueMap<String, String> params = request.getQueryParams();//获取所有请求参数
String uname = params.getFirst("uname");//从参数列表中获取key为uname的value
if (uname == null) {
log.info("**************用户名为null,非法的访问,😈");
ServerHttpResponse response = exchange.getResponse();//获取响应对象
response.setStatusCode(HttpStatus.NOT_ACCEPTABLE);//设置响应的状态码
return response.setComplete();
}
return chain.filter(exchange);//放行
}
@Override
public int getOrder() { //设置当前过滤器的顺序
return 0;
}
}