

SpringCloud Gateway微服务网关实战与源码分析-上 - itxiaoshen
source link: https://www.cnblogs.com/itxiaoshen/p/16460647.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

SpringCloud Gateway微服务网关实战与源码分析-上
Spring Cloud Gateway 官网地址 https://spring.io/projects/spring-cloud-gateway/ 最新版本3.1.3
Spring Cloud Gateway 文档地址 https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
Spring Cloud Gateway GitHub源码地址 https://github.com/spring-cloud/spring-cloud-gateway
Spring Cloud Gateway使用了WebFlux技术,而WebFlux技术底层又基于高性能的Reactor模式通信框架Netty。Spring Cloud Gateway基于Spring 5、Spring Boot 2和project Reactor技术上构建异步非阻塞的高吞吐量API网关,提供一种简单且有效的方式来路由到API,并为它们提供横切关注点如安全性、监控/指标和弹性等。Spring Cloud Gateway特性如下:
Spring Cloud Gateway特性如下:
- 能够在任何请求属性上匹配路由。
- 谓词和过滤器是特定于路由的。
- 集成断路器。
- 集成Spring Cloud DiscoveryClient
- 编写谓词和过滤器编写易用。
- 限制请求速率。

网关作为系统的唯一流量入口,封装内部系统的架构,所有请求都先经过网关,由网关将请求路由到合适的微服务,优势如下:
- 简化客户端的工作。网关将微服务封装起来后,客户端只需同网关交互,而不必调用各个不同服务。
- 降低函数间的耦合度。 一旦服务接口修改,只需修改网关的路由策略,不必修改每个调用该函数的客户端,从而减少了程序间的耦合性。
- 解放开发人员把精力专注于业务逻辑的实现。由网关统一实现服务路由(灰度与ABTest)、负载均衡、访问控制、流控熔断降级等非业务相关功能,而不需要每个服务 API 实现时都去考虑。
- 现在前后端分离大趋势下,目前大部分浏览器安全同源策略出现前端请求的跨域问题,网关也是解决跨域问题的一种较完美方式。
流量网关与微服务网关

流量网关(如典型Nginx网关)是指提供全局性的、与后端业务应用无关的策略,例如 HTTPS证书卸载、Web防火墙、全局流量监控等。而微服务网关(如Spring Cloud Gateway)是指与业务紧耦合的、提供单个业务域级别的策略,如服务治理、身份认证等。也就是说,流量网关负责南北向流量调度及安全防护,微服务网关负责东西向流量调度及服务治理。

- Kong 网关:Kong 的性能非常好,非常适合做流量网关,但是对于复杂系统不建议业务网关用 Kong,主要是工程性方面的考虑。基于OpenResty或Nginx+Lua实现。
- Zuul1.x 网关:Zuul 1.0 的落地经验丰富,但是性能差、基于同步阻塞IO,适合中小架构,不适合并发流量高的场景,因为容易产生线程耗尽,导致请求被拒绝的情况。
- Gateway 网关:功能强大丰富,性能好,官方基准测试 RPS (每秒请求数)是Zuul的1.6倍,能与 SpringCloud 生态很好兼容,单从流式编程+支持异步上也足以让开发者选择它了。
- Zuul 2.x:性能与 gateway 差不多,基于非阻塞的,支持长连接,但 SpringCloud 没有集成 zuul2 的计划,并且 Netflix 相关组件都宣布进入维护期,前景未知。
从发展趋势上看,Spring Cloud Gateway作为Spring Cloud生态体系中的网关,目标替代Netflix的Zuul且势在必行。
进一步研究 Spring Cloud Gateway 的配置及其使用之前,我们先了解几个 Spring Cloud Gateway 的核心术语
- Route(路由):网关的基本组成部分。它由一个ID、一个目标URI、一组谓词和一组过滤器定义。如果聚合谓词或者说断言为真,则匹配路由。
- Predicate(谓词):这是一个Java 8函数谓词,输入类型是Spring Framework serverwebexchange,匹配HTTP请求中的任何内容,如头或参数。
- Filter(过滤器):这些是使用特定工厂构建的GatewayFilter实例,可以在发送下游请求之前或之后修改请求和响应。


- 客户端向Spring Cloud Gateway发出请求。
- 如果网关处理程序映射决定一个请求匹配一个路由,它被发送到网关Web处理程序。
- 此处理程序通过特定于该请求的过滤器链运行请求。用虚线分隔过滤器的原因是,过滤器可以在发送代理请求之前和之后运行逻辑。
- 执行所有“预”筛选逻辑。然后发出代理请求。
- 发出代理请求后,运行“post”过滤器逻辑。
项目或模块中加入spring-cloud-starter-gateway
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
如果引入了启动器,但又不希望启用网关,则可以通过设置spring.cloud.gateway.enabled=false来禁用。全部详细配置可以查阅官网,Spring Cloud Gateway详细配置说明 https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/appendix.html
官网提供两种配置谓词和过滤器的方法,分别是shortcuts and fully expanded arguments,译为快捷方式和完全扩展的参数方式,后续例子我们都使用快捷方式,这种方式简洁舒畅,官方的例子也大都是使用快捷方式。
路由配置Route 主要由路由id、目标uri、断言集合和过滤器集合组成
- id:路由标识,要求唯一,名称任意(默认值 uuid,一般不用,需要自定义)
- uri:请求最终被转发到的目标地址
- order: 路由优先级,数字越小,优先级越高
- predicates:断言数组,即判断条件,如果返回值是boolean,则转发请求到 uri 属性指定的服务中
- filters:过滤器数组,在请求传递过程中,对请求做一些修改
网关路由初体验
利用前面库存微服务提供的deduct接口,端口为4080,启动库存微服务,访问http://localhost:4080/deduct,显示成功

创建网关微服务模块,pom文件依赖如下,由于后面我们有Gateway整合Nacos和Sentinel的示例,所以这里把其他依赖也先加进来
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>simple-ecommerce</artifactId>
<groupId>cn.itxs</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ecom-gateway</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<name>ecom-gateway</name>
<description>a simple electronic commerce platform demo tutorial for gateway service</description>
<properties>
<spring-cloud-loadbalancer.version>3.1.3</spring-cloud-loadbalancer.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<version>${spring-cloud-loadbalancer.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies>
</project>
bootstrap.yml配置文件加入建议路由配置如下
server:
port: 4090
spring:
application:
name: ecom-gateway
cloud:
gateway:
routes:
- id: storage_route
uri: http://localhost:4080
predicates:
- Path=/storage-service/**
filters:
- StripPrefix=1
启动网关微服务

访问网关提供api接口http://localhost:4090/storage-service/deduct,匹配storage-service为真后通过过滤器去掉一层也即是storage-service路径去掉,然后转发至uri地址,最终转发url为http://localhost:4080/deduct ,成功返回结果

整合Nacos
本地配置文件bootstrap.yml改为如下,commons-dev.yaml包含服务注册的组
spring:
application:
name: ecom-gateway
profiles:
active: dev
cloud:
nacos:
# 注册中心信息放在配置中心上,每个程序一般只配置配置中心的信息
server-addr: 192.168.50.95:8848
config:
server-addr: ${spring.cloud.nacos.server-addr}
file-extension: yaml
namespace: a2b1a5b7-d0bc-48e8-ab65-04695e61db01
group: gateway-group
extension-configs:
- dataId: commons-dev.yaml
group: commons-group
refresh: true
username: itsx
password: itxs123
enabled: true # 默认为true,设置false 来完全关闭 Spring Cloud Nacos Config
refresh-enabled: true # 默认为true,当变更配置时,应用程序中能够获取到最新的值,设置false来关闭动态刷新,我们使用注册中心场景大部分就是动态感知,因此基本使用默认的
将路由配置也一并放在Nacos中配置ecom-gateway-dev.yaml,内容如下,uri这里使用的是库存微服务名称,lb是做负载均衡处理
server:
port: 4090
spring:
cloud:
gateway:
routes:
- id: storage_route
uri: lb://ecom-storage-service
predicates:
- Path=/storage-service/**
filters:
- StripPrefix=1
Nacos中commons-dev.yaml的关于Nacos注册中心使用配置如下,库存微服务也是使用这个,服务注册和发现都在ecom-group组
spring:
cloud:
nacos:
discovery:
server-addr: ${spring.cloud.nacos.server-addr}
group: ecom-group
namespace: a2b1a5b7-d0bc-48e8-ab65-04695e61db01
username: itsx
password: itxs123

启动库存微服务和网关微服务,都注册到同一个组里面

再次访问http://localhost:4090/storage-service/deduct ,正常返回结果,到此我们已经成功整合Nacos
路由断言工厂
Route Predicate Factories为路由断言工厂,官网提供12种路由工厂,如果都没有满足你的需求,还可以自定义路由断言工厂

我们先配置一个未来时间的after断言- After=2022-07-09T23:42:47.789-08:00[Asia/Shanghai]

可以直接访问本机IP,返回一个错误的页面
将after断言改为- Before=2022-07-09T23:42:47.789-08:00[Asia/Shanghai]后则可以正常访问。
- 其他还有许多规则详细可以查阅官网,如下面,各位可以自己一一尝试
- Between=2022-07-08T23:42:47.789-08:00[Asia/Shanghai], 2022-07-09T23:42:47.789-08:00[Asia/Shanghai]
- Cookie=chocolate, ch.p
- Header=X-Request-Id, \d+
- Host=**.somehost.org,anotherhost.org
- Method=GET,POST
- Query=green
- RemoteAddr=192.168.1.1/24
- Weight=group1, 8 Weight=group1, 2
- XForwardedRemoteAddr=192.168.1.1/24
自定义路由断言工厂
当官方提供的所有断言工厂无法满足业务需求时,还可以自定义断言工厂。添加自定义断言工厂类自定断言工厂主要注意一下几点:
- 需要声明是Springboot的Bean,添加注解@Component,名称必须以RoutePredicateFactory结尾,这个是命名约束。如果不按照命名约束来命名,那么就会找不到该断言工厂。前缀就是配置中配置的断言。
- 可以直接复制Gateway中已经实现的断言工厂,修改对应的内容,避免踩坑。
- 继承父类AbstractRoutePredicateFactory,并重写方法。
- 需要定义一个Config静态内部类,声明属性来接收 配置文件中对应的断言的信息。
- 在重写的shortcutFieldOrder方法中,绑定Config中的属性。传入数组的内容需要与Config中的属性一致。
- 在重写的apply方法中,实现具体验证逻辑, true就是匹配成功 false匹配失败。
新建一个库存数量的路由断言工厂QuantityRoutePredicateFactory.java,如库存在100和200之间可以访问
package cn.itxs.ecom.gateway.factory;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
// 自定义路由断言工厂
@Component
public class QuantityRoutePredicateFactory extends AbstractRoutePredicateFactory<QuantityRoutePredicateFactory.Config>{
public QuantityRoutePredicateFactory() {
super(QuantityRoutePredicateFactory.Config.class);
}
// 将配置文件中的值按返回集合的顺序,赋值给配置类
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList(new String[]{"minQuantity", "maxQuantity"});
}
@Override
public Predicate<ServerWebExchange> apply(Consumer<Config> consumer) {
return super.apply(consumer);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
// 创建网关断言对象
// 检查
return serverWebExchange -> {
// TODO 获取请求参数age,判断是否满足如配置的[100, 200)
MultiValueMap<String, String> queryParams = serverWebExchange.getRequest().getQueryParams();
String quantity = queryParams.getFirst("quantity");
if (StringUtils.hasText(quantity) && quantity.matches("[0-9]+")) {
int iQuantity = Integer.parseInt(quantity);
if (iQuantity >= config.getMinQuantity() && iQuantity < config.getMaxQuantity()) {
return true;
}
}
return false;
};
}
// 配置类,属性用于接收配置文件中的值
@Validated
public static class Config {
private int minQuantity;
private int maxQuantity;
public int getMinQuantity() {
return minQuantity;
}
public void setMinQuantity(int minQuantity) {
this.minQuantity = minQuantity;
}
public int getMaxQuantity() {
return maxQuantity;
}
public void setMaxQuantity(int maxQuantity) {
this.maxQuantity = maxQuantity;
}
}
}
Nacos网关的配置中增加自定义路由断言工厂配置Quantity
server:
port: 4090
spring:
cloud:
gateway:
routes:
- id: storage_route
uri: lb://ecom-storage-service
predicates:
- Path=/storage-service/**
- Quantity=100,200
filters:
- StripPrefix=1
启动网关微服务,访问http://localhost:4090/storage-service/deduct?quantity=99 ,没有匹配路由策略

而访问http://localhost:4090/storage-service/deduct?quantity=100 ,能够正确返回库存微服务接口结果
**本人博客网站 **IT小神 www.itxiaoshen.com
Recommend
-
70
作为Netflix Zuul的替代者,Spring Cloud Gateway是一款非常实用的微服务网关,在Spring Cloud微服务架构体系中发挥非常大的作用。本文对Spring Cloud Gateway常见使用场景进行了梳理,希望对微服务开发人员提供一些帮助。 微服...
-
16
@ 目录 前言 由于项目采用了微服务架构,业务功能都在相应各自的模块中,每个业务模块都是以独立的项目运行着,对外提供各自的服务接口,如没有类似网关之类组件的话,相应的鉴权,限流等功能实现起来不能够...
-
4
SpringCloud Alibaba微服务实战三十二 大家好,我是飘渺Jam,一名来自三流城市三流公司的三流程序员,这是我们的第159篇原创文章,如果你喜欢请记得给我一个点赞与转发。 这篇文章来源于粉丝...
-
22
SpringCloud系列教程(五)之SpringCloud Gateway 网关聚合开发文档 swagger knife4j 和登录权限统一验证 August 04, 2021 14786 阅读提醒: 本...
-
5
服务网关:项目整合 SpringCloud Gateway 网关-51CTO.COM
-
8
SpringCloudAlibaba分布式事务解决方案Seata实战与源码分析-上 ...
-
6
【SpringCloud技术专题】「入门实战」微服务网关服务的Gateway全流程开发实践指南 推荐 原创 洛神灬殇
-
8
系列文章目录和关于我 源码基于 spring-cloud-netflix-zuul-2.2.6.RELEASE.jar 需要具备SpringMVC源码功底 推荐学习https://www.cnblogs.com/cuzzz/p/16538064.html
-
7
云原生API网关全生命周期管理Apache APISIX探究实操 ...
-
2
springcloud~gateway网关 有时间,我们在搭建微服务时,...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK