Spring Cloud Gateway微服务网关
@[toc](Spring Cloud Gateway微服务网关)
代码地址:https://gitee.com/interface_xiongtete/spring-cloud2022
去年我发过两篇网关的文章:Spring Cloud Gateway(微服务网关),gateway网关的断言(predicate)和过滤(filter)
笔记为本人根据周阳老师的SpringCloud视频手敲总结出来的。
1、Gateway网关概述
1.1 什么是Gateway?
官网文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/
该项目提供了一个库,用于在 Spring WebFlux 之上构建 API 网关。Spring Cloud Gateway 旨在提供一种简单而有效的方式来路由到 API,并为它们提供横切关注点,例如:安全性、监控/指标和弹性。
SpringCloud Gateway 使用的Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架。
1.2 Spring Cloud Gateway 特点
- 基于 Spring Framework 5、Project Reactor 和 Spring Boot 2.0 构建
- 能够匹配任何请求属性的路由。
- 谓词和过滤器特定于路由。
- 断路器集成。
- Spring Cloud Discovery客户端集成
- 易于编写谓词和过滤器
- 请求速率限制
- 路径重写
1.3 Spring Cloud Gateway能干什么?
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
- 等等
1.4 微服务架构中网关在什么位置?
1.5 为什么Gateway使用WebFlux模型?
传统的Web框架,比如说:struts2,springmvc等都是基于Servlet API与Servlet容器基础之上运行的。
但是在Servlet3.1之后有了异步非阻塞的支持。而WebFlux是一个典型异步非阻塞的框架,它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上。非阻塞式+函数式编程(Spring5必须让你使用java8)
Spring WebFlux 是 Spring 5.0 引入的新的响应式框架,区别于 Spring MVC,它不需要依赖Servlet API,它是完全异步非阻塞的,并且基于 Reactor 来实现响应式流规范。
2、Gateway三大核心概念
- Route(路由):构建网关的基本模块,由ID、目标URI、一系列的断言和过滤器组成,如果断言为true就匹配该路由。
- Predicate(断言):这是一个Java 8 函数谓词。输入类型是Spring Framework
ServerWebExchange
。这使您可以匹配来自 HTTP 请求的任何内容,例如请求头或请求参数。如果请求与断言相匹配,就进行路由。 - Filter(路由):Spring框架中的GatewayFilter实例通过使用过滤器可以在路由前后对请求进行修改。
总体来看:
web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。
predicate就是我们的匹配条件;而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了
3、Gateway工作流程(重点)
SpringCloud Gateway的工作机制大概如下:
- Gateway接收客户端请求
- 客户端请求与路由信息进行匹配,匹配成功被发往相应的下游服务。
- 请求经过Filter过滤器链,执行pre(前置过滤器)处理逻辑,如修改请求头信息等。
- 请求转被转发志下游服务并返回响应。
- 响应经过Filter过滤器链,执行post(后置过滤器)处理逻辑。
- 向客户端响应应答。
4、入门配置实战
4.1 新建cloud-gateway-gateway9527模块
4.2 pom.xml
<dependencies>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--一般基础配置类-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
4.3 application.yml
server:
port: 9527
spring:
application:
name: cloud-gateway
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
4.4 主启动类
4.5 9527端口的网关如何做路由映射?
我们去cloud-provider-payment8001看看controller的访问地址
我们现在的需求是:不想暴露8001端口,希望在8001外面套一层9527,只让外界访问网关,让网关统一进行路由的转发。
4.6 yml新增网关配置
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
4.7 测试
启动eureka7001注册中心模块
启动cloud-provider-payment8001微服务
启动cloud-gateway-gateway9527网关微服务
访问测试:
添加网关之前的访问方式:http://localhost:8001/payment/get/31
添加网关之后的访问方式:http://localhost:9527/payment/get/31
Gateway其实有两种配置方式:yml配置和代码注入方式配置。由于代码注入方式看起来有点不美观所以这里暂且写,详细的去查官网,这种方式其实用的不多。
5、通过微服务名实现动态路由
5.1 配置
默认情况下Gateway会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。
我们启动一个eureka7001,和两个服务提供者payment8001和payment8002,顺便测试下负载均衡(前提是引入了Ribbon)。
新的网关配置文件如下:
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
需要注意的是uri的协议为lb
,表示启用Gateway的负载均衡功能。
lb://serviceName
是spring cloud gateway在微服务中自动为我们创建的负载均衡uri
5.2 测试
确保服务都正确启动
查看服务注册中心:localhost:7001
可以看到网关服务和两个服务提供者都已经注册成功。
访问:http://localhost:9527/payment/lb
第一次:
第二次:
即使不断地刷新,输出结果一直在8001和8002之间切换,因为Ribbon默认就是轮询算法,说明负载均衡是成功的。
6、Predicate的使用
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以进行组合。
Spring Cloud Gateway 创建 Route 对象时, 使用 RoutePredicateFactory 创建 Predicate 对象,Predicate 对象可以赋值给 Route。 Spring Cloud Gateway 包含许多内置的Route Predicate Factories。
所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合,并通过逻辑and。
6.1 常用的Route Predicate
After Route Predicate
after 路由谓词工厂采用一个参数,即日期时间。此谓词匹配在指定日期时间之后发生的请求
Before Route Predicate:before 路由谓词工厂采用一个参数 a datetime
。此谓词匹配在指定的 之前发生的请求datetime
。
Between Route Predicate:路由谓词工厂之间有两个参数,datetime1
和datetime2
。此谓词匹配发生在 afterdatetime1
和 before的请求datetime2
。datetime2
参数必须在之后datetime1
。
Cookie Route Predicate:cookie 路由谓词工厂有两个参数,cookie 名称和正则表达式。此谓词匹配具有给定名称且其值与正则表达式匹配的 cookie。
Header Route Predicate:标头路由谓词工厂有两个参数,标头名称和正则表达式。此谓词与具有给定名称且值与正则表达式匹配的标头匹配。
Host Route Predicate:主机路由谓词工厂采用一个参数:主机名模式列表。该模式是一种 Ant 风格的模式,.
以分隔符为分隔符。此谓词匹配Host
与模式匹配的标头。
Method Route Predicate:Method Route Predicate Factory 采用一个或多个参数:要匹配的 HTTP 方法。
Path Route Predicate:Path Route Predicate Factory 有两个参数:一个 SpringPathMatcher
模式列表和一个名为 的可选标志matchOptionalTrailingSeparator
Query Route Predicate:查询路由谓词工厂有两个参数:一个 requiredparam
和一个 optional regexp
。
Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。
7、Filter的使用
7.1 Filter是什么?
路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。
Spring Cloud Gateway 内置了多种路由过滤器,他们都由GatewayFilter的工厂类来产生
7.2 Filter的分类
- GatewayFilter:Spring Cloud Gateway 包含许多内置的 GatewayFilter 工厂。这个太多了,一般不用。
- GlobalFilter:全局过滤器。
7.3 自定义全局过滤器测试
7.3.1 实现两个接口:GlobalFilter,Ordered
7.3.2 全局Filter能做什么?
- 全局日志记录
- 统一网关鉴权
- 统一配置所有微服务的跨域
- 等等
7.3.3 案例代码:
/**
* 自定义全局GlobalFilter
*/
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("**********come in MyLogGateWayFilter: "+new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if(uname==null){
log.info("*******用户名为null,非法用户!!!");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
上面配置的意思是,请求种必须带有uname参数,不带不进行正常的路由转发并返回自定义状态码。
7.3.4 测试
启动
访问带uname参数的:http://localhost:9527/payment/lb?uname=z3
访问不带uname参数的:http://localhost:9527/payment/lb
此时,无法正常使用转发功能。
到此,微服务网关的基本功能就介绍完了,高级配置后面碰到再另写一篇。
- 点赞
- 收藏
- 关注作者
评论(0)