大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE稳定放心使用
目录
配套资料,免费下载
链接:https://pan.baidu.com/s/1la_3-HW-UvliDRJzfBcP_w
提取码:lxfx
复制这段内容后打开百度网盘手机App,操作更方便哦
第一章 Gateway介绍
1.1、什么是Gateway
Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代Zuul,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全鉴权、指标监控以及熔断限流等。
官方文档地址:https://docs.spring.io/spring-cloud-gateway/docs/2.2.6.RELEASE/reference/html/
1.2、为啥用Gateway
Spring Cloud Gateway可以看做是一个Zuul 1.x的升级版和代替品,比Zuul 2更早的使用Netty实现异步IO,从而实现了一个简单、比Zuul 1.x更高效的、与Spring Cloud紧密配合的API网关。Spring Cloud Gateway里明确的区分了Router和Filter,并且一个很大的特点是内置了非常多的开箱即用功能,并且都可以通过Spring Boot配置或者手工编码链式调用来使用。
第二章 Gateway三大核心
第三章 Gateway工作流程
客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler。Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
第四章 Gateway路由功能
4.1、项目准备与启动
我们接下来的所有操作均是在Zuul
最后完成的工程上进行操作,相关代码请到配套资料中寻找。
打开已经准备好的项目以后,我们需要启动以下服务,eureka-server7001
启动会报错,暂时不用理会,等eureka-server7002
启动起来过一会就好了。
- eureka-server7001
- eureka-server7002
- service-provider8001
- service-provider8002
- service-consumer9001
- service-consumer9002
随便访问一个注册中心,查看我们当前注册的所有服务,登录账户:root
,登录密码:123456
,访问地址:http://localhost:7001
我们随便访问一个服务地址,访问地址:http://localhost:9002/consumer/product/findByPid?pid=0
4.2、工程搭建与测试
(1)在父工程spring-cloud-study
下创建子工程gateway-cloud5002
(2)在刚创建好的子工程gateway-cloud5002
的pom.xml
中添加依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
(3)在刚创建好的子工程gateway-cloud5002
中创建配置文件application.yaml
,配置如下:
server:
port: 5002
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
#开启服务列表发现并拥有默认路由功能
discovery:
locator:
enabled: true
eureka:
client:
#是否将自己注册到注册中心,默认为 true
register-with-eureka: false
#表示 Eureka Client 间隔多久去服务器拉取注册信息,默认为 30 秒
registry-fetch-interval-seconds: 10
#设置服务注册中心地址
service-url:
defaultZone: http://root:123456@eureka-server7001.com:7001/eureka/,http://root:123456@eureka-server7002.com:7002/eureka/
#日志记录,在开发阶段,建议开启
logging:
level:
org.springframework.cloud.gateway: debug
org.springframework.http.server.reactive: debug
org.springframework.web.reactive: debug
(4)新建启动类,在启动类中加入以下代码
@SpringBootApplication
public class GatewayCloud5002Application {
public static void main(String[] args) {
SpringApplication.run(GatewayCloud5002Application.class);
}
}
(5)启动当前项目,这样简单的Gateway就搭建好了,我们可以通过默认的路由规则来访问指定的服务方法,比如:
格式:“http://”+Gateway网关的域名+”:”+Gateway网关的端口+”/”+微服务的名称(一定大写)+微服务的服务路径(就是你Controller方法上标注的那个路径)
例如:http://localhost:5002/SERVICE-CONSUMER9002/consumer/product/findAll
例如:http://localhost:5002/SERVICE-CONSUMER9003/consumer/product/findAll
4.3、配置自定义路由
现在,我们已经基本实现了Gateway默认路由的功能,但是,一般我们也可以自定义路由配置,为什么要自定义,细心的你会发现,现在你访问指定的服务,必须要加注册服务的名称(例如:SERVICE-CONSUMER9002
,SERVICE-CONSUMER9003
),这个名称可能很长,也可能会暴露你这个服务的一些性质,我们想要使用我们自己的路由规则,就必须使用自定义路由配置,自定义路由配置其实很简单,只需要在gateway-cloud5002
中的application.yaml
加上相对应的路由配置就行了,具体代码如下所示:
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
#开启服务列表发现并拥有默认路由功能
discovery:
locator:
enabled: true
#自定义路由映射,可以配置多条路由规则,一个路由是由id、uri、predicates、filters组成
routes:
- id: SERVICE-CONSUMER9002-ROUTE #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:9002 #匹配后提供服务的路由地址
predicates:
- Path=/consumer/product/find* #断言,只有路径相匹配才能进行路由
- id: SERVICE-CONSUMER9003-ROUTE #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:9003 #匹配后提供服务的路由地址
predicates:
- Path=/consumer/product/miaoSha #断言,只有路径相匹配才能进行路由
如果你还有更多服务你还可以照着上边的规则继续往下写(服务提供者和服务消费者都算服务),写完后,请重新启动当前的项目,然后依次访问如下地址测试:
地址1:http://localhost:5002/consumer/product/findAll
地址2:http://localhost:5002/consumer/product/miaoSha
4.4、禁止默认的路由
虽然实现了自定义路由设置,但是如果你使用之前默认的路由规则,他还是可以访问的,我们想要禁用掉,默认的那个路由规则,只需要修改一段配置,如下:
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
#开启服务列表发现并拥有默认路由功能,true开启,false关闭,默认就是false,以下三行也可以删除或者注释
discovery:
locator:
enabled: false
#自定义路由映射,可以配置多条路由规则,一个路由是由id、uri、predicates、filters组成
routes:
- id: SERVICE-CONSUMER9002-ROUTE #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:9002 #匹配后提供服务的路由地址
predicates:
- Path=/consumer/product/find* #断言,只有路径相匹配才能进行路由
- id: SERVICE-CONSUMER9003-ROUTE #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:9003 #匹配后提供服务的路由地址
predicates:
- Path=/consumer/product/miaoSha #断言,只有路径相匹配才能进行路由
重新启动当前的项目,然后依次访问如下地址测试:
例如:http://localhost:5002/SERVICE-CONSUMER9002/consumer/product/findAll
例如:http://localhost:5002/SERVICE-CONSUMER9003/consumer/product/findAll
4.5、配置动态的路由
我们之前编写的自定义路由中的一项uri,我们直接写的是目标服务的地址,这样的写法很死板,不灵活,我们能不能把目标uri不指向目标服务的地址,而是让uri跟eureka的注册应用挂钩,这样,你目标服务随便扩容缩容,我网关这里感觉不到,因为网关直接找eureka,要想实现这样的效果只需要修改uri为对应的注册应用名称就好,不过,我们还需要在这个注册名称之前加一个协议前缀,这个前缀可以使用配置修改,但是一般默认即可,具体配置如下:
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
#开启服务列表发现并拥有默认路由功能
discovery:
locator:
enabled: false
#自定义路由映射,可以配置多条路由规则,一个路由是由id、uri、predicates、filters组成
routes:
- id: SERVICE-CONSUMER9002-ROUTE #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:9002 #匹配后提供服务的路由地址
uri: lb://SERVICE-CONSUMER9002 #uri的协议为lb,表示启用Gateway的负载均衡功能。
predicates:
- Path=/consumer/product/find* #断言,只有路径相匹配才能进行路由
- id: SERVICE-CONSUMER9003-ROUTE #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:9003 #匹配后提供服务的路由地址
uri: lb://SERVICE-CONSUMER9003 #uri的协议为lb,表示启用Gateway的负载均衡功能。
predicates:
- Path=/consumer/product/miaoSha #断言,只有路径相匹配才能进行路由
请重新启动当前的项目,然后依次访问如下地址测试:
地址1:http://localhost:5002/consumer/product/findAll
地址2:http://localhost:5002/consumer/product/miaoSha
4.6、用代码来配路由
Gateway网关路由有两种配置方式,一种是使用配置文件的方式,另一种是使用代码的方式来配置,具体代码请参考:
com.caochenlei.config.GatewayConfig
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
return routeLocatorBuilder.routes()
//可以使用通配符,例如:/provider/product/**
.route("SERVICE-PROVIDER-ROUTE1", r -> r.path("/provider/product/findAll").uri("lb://SERVICE-PROVIDER"))
.route("SERVICE-PROVIDER-ROUTE2", r -> r.path("/provider/product/findByPid").uri("lb://SERVICE-PROVIDER"))
.build();
}
}
请重新启动当前的项目,然后依次访问如下地址测试:
地址1:http://localhost:5002/provider/product/findAll
地址2:http://localhost:5002/provider/product/findByPid?pid=0
4.7、为下章节做准备
下载地址:https://curl.se/windows/,如果下载失败,请到配套资料中寻找该软件。
解压软件:
配置环境变量:
测试安装状态:
注释当前配置:
#这里只给出了局部配置,其他配置保持不变
#spring:
# application:
# name: gateway-cloud5002
# cloud:
# gateway:
# #开启服务列表发现并拥有默认路由功能
# discovery:
# locator:
# enabled: false
# #自定义路由映射,可以配置多条路由规则,一个路由是由id、uri、predicates、filters组成
# routes:
# - id: SERVICE-CONSUMER9002-ROUTE #路由的ID,没有固定规则但要求唯一,建议配合服务名
# #uri: http://localhost:9002 #匹配后提供服务的路由地址
# uri: lb://SERVICE-CONSUMER9002 #uri的协议为lb,表示启用Gateway的负载均衡功能。
# predicates:
# - Path=/consumer/product/find* #断言,只有路径相匹配才能进行路由
# - id: SERVICE-CONSUMER9003-ROUTE #路由的ID,没有固定规则但要求唯一,建议配合服务名
# #uri: http://localhost:9003 #匹配后提供服务的路由地址
# uri: lb://SERVICE-CONSUMER9003 #uri的协议为lb,表示启用Gateway的负载均衡功能。
# predicates:
# - Path=/consumer/product/miaoSha #断言,只有路径相匹配才能进行路由
第五章 Gateway断言功能
5.1、谓词:Path
谓词介绍:通过请求路径匹配,多个请求路径可以使用逗号进行分隔,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
#匹配所有以/consumer/product/开头的请求,匹配到以后使用Ribbon的负载均衡请求Eureka注册中心的SERVICE-CONSUMER9002服务
- Path=/consumer/product/**
请求地址:curl -XGET http://localhost:5002/consumer/product/findAll
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Path=/foo/{
segment},/bar/{
segment}
这个路由匹配这样路径的请求,比如:/foo/1 或 /foo/bar 或 /bar/baz
5.2、谓词:After
谓词介绍:该谓词匹配在指定日期时间之后发生的请求,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
#匹配所有以/consumer/product/开头的请求,匹配到以后使用Ribbon的负载均衡请求Eureka注册中心的SERVICE-CONSUMER9002服务
- Path=/consumer/product/**
#匹配所有时间在2021年1月2日23点59分59秒以后访问的请求,多个谓词之间必须都符合才能通过
- After=2021-01-02T23:59:59.000+08:00[Asia/Shanghai]
生成时间:上边的时间戳可以使用下边这段代码来进行生成,然后针对具体时间部分进行简单修改。
public class ZonedDateTimeDemo {
public static void main(String[] args) {
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime);
}
}
请求地址:curl -XGET http://localhost:5002/consumer/product/findAll
5.3、谓词:Before
谓词介绍:该谓词匹配在指定日期时间之前发生的请求,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
#匹配所有以/consumer/product/开头的请求,匹配到以后使用Ribbon的负载均衡请求Eureka注册中心的SERVICE-CONSUMER9002服务
- Path=/consumer/product/**
#匹配所有时间在2021年2月2日23点59分59秒以前访问的请求,多个谓词之间必须都符合才能通过
- Before=2021-02-02T23:59:59.000+08:00[Asia/Shanghai]
请求地址:curl -XGET http://localhost:5002/consumer/product/findAll
5.4、谓词:Between
谓词介绍:该谓词匹配在指定日期时间之间发生的请求,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
#匹配所有以/consumer/product/开头的请求,匹配到以后使用Ribbon的负载均衡请求Eureka注册中心的SERVICE-CONSUMER9002服务
- Path=/consumer/product/**
#匹配所有时间在2021年1月2日23点59分59秒以后,在2021年2月2日23点59分59秒以前访问的请求,多个谓词之间必须都符合才能通过
- Between=2021-01-02T23:59:59.000+08:00[Asia/Shanghai],2021-02-02T23:59:59.000+08:00[Asia/Shanghai]
请求地址:curl -XGET http://localhost:5002/consumer/product/findAll
5.5、谓词:Cookie
谓词介绍:可以接收两个参数,一个是 Cookie name , 一个是正则表达式,路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
#匹配所有以/consumer/product/开头的请求,匹配到以后使用Ribbon的负载均衡请求Eureka注册中心的SERVICE-CONSUMER9002服务
- Path=/consumer/product/**
#你的请求必须带有Cookie,并且其中有Cookie的名称叫password,Cookie的值会跟你自己定义的Java正则匹配,这里的\d+,代表密码必须为数字
- Cookie=password,\d+
正确请求地址:curl -XGET http://localhost:5002/consumer/product/findAll --cookie "password=123"
错误请求地址:curl -XGET http://localhost:5002/consumer/product/findAll --cookie "password=zhangsan"
5.6、谓词:Header
谓词介绍:Header谓词和Cookie谓词一样,也是接收 2 个参数,一个 header 中属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
#匹配所有以/consumer/product/开头的请求,匹配到以后使用Ribbon的负载均衡请求Eureka注册中心的SERVICE-CONSUMER9002服务
- Path=/consumer/product/**
#你的请求必须带有Header,并且其中有Header的名称叫Authorization,Header的值会跟你自己定义的Java正则匹配,这里的\d+,代表值必须为数字
- Header=Authorization,\d+
正确请求地址:curl -XGET http://localhost:5002/consumer/product/findAll -H "Authorization:123456789"
错误请求地址:curl -XGET http://localhost:5002/consumer/product/findAll -H "Authorization:zhangsan"
5.7、谓词:Host
谓词介绍:Host谓词接收一组参数,一组匹配的域名列表,多个域名之间使用逗号分隔,它通过参数中的主机地址作为匹配规则,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
#匹配所有以/consumer/product/开头的请求,匹配到以后使用Ribbon的负载均衡请求Eureka注册中心的SERVICE-CONSUMER9002服务
- Path=/consumer/product/**
#如果请求的Host标头值为www.somehost.org或beta.somehost.org或此路由匹配www.anotherhost.org。匹配通过才可路由
- Host=**.somehost.org,**.anotherhost.org
正确请求地址:curl -XGET http://localhost:5002/consumer/product/findAll -H "Host:www.somehost.org"
错误请求地址:curl -XGET http://localhost:5002/consumer/product/findAll -H "Host:www.baidu.com"
5.8、谓词:Method
谓词介绍:该谓词可以通过你请求的方式(GET、POST、PUT、DELETE、…)来进行匹配,只有指定方式的请求才可以匹配成功,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
#匹配所有以/consumer/product/开头的请求,匹配到以后使用Ribbon的负载均衡请求Eureka注册中心的SERVICE-CONSUMER9002服务
- Path=/consumer/product/**
#如果请求类型是GET或者POST请求才能匹配成功,其他请求类型均失败,失败就会404
- Method=GET,POST
正确请求地址:curl -XGET http://localhost:5002/consumer/product/findAll
正确请求地址:curl -XPOST http://localhost:5002/consumer/product/findAll
错误请求地址:curl -XPUT http://localhost:5002/consumer/product/findAll
错误请求地址:curl -XDELETE http://localhost:5002/consumer/product/findAll
5.9、谓词:Query
谓词介绍:该谓词也支持传入两个参数,一个是属性名一个为属性值,属性值可以是正则表达式,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
#匹配所有以/consumer/product/开头的请求,匹配到以后使用Ribbon的负载均衡请求Eureka注册中心的SERVICE-CONSUMER9002服务
- Path=/consumer/product/**
#如果请求参数中包含smile参数,并且smile参数必须符合逗号后边的正则表达式的值,只有匹配成功才可以路由
- Query=smile,\d+
正确请求地址:curl -XGET http://localhost:5002/consumer/product/findAll?smile=521
错误请求地址:curl -XGET http://localhost:5002/consumer/product/findAll?smile=no
5.10、谓词:RemoteAddr
谓词介绍:该谓词支持通过设置某个 ip 区间号段,支持单个 ip 地址,还支持接受 cidr 符号 (IPv4 或 IPv6) 字符串的列表(最小大小为 1),例如 192.168.0.1/16 (其中 192.168.0.1 是客户端发送请求的 ip 地址,16 是子网掩码(255.255.0.0)),示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
#匹配所有以/consumer/product/开头的请求,匹配到以后使用Ribbon的负载均衡请求Eureka注册中心的SERVICE-CONSUMER9002服务
- Path=/consumer/product/**
#请求必须是192.168.1.3,并且子网掩码为255.255.255.0的地址发出的才可以路由
- RemoteAddr=192.168.1.3/24
查看本机以太网的ipv4地址以及子网掩码,使用ipconfig
命令:
正确请求地址:curl -XGET http://192.168.1.3:5002/consumer/product/findAll
错误请求地址:curl -XGET http://localhost:5002/consumer/product/findAll
5.11、谓词:Weight
谓词介绍:该Weight
谓词有两个参数:group
和weight
(一个int)。权重是按组计算的,以下示例配置权重路由谓词:
#这里只给出了局部配置,其他配置保持不变
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2
这条路线会将大约80%的流量转发到weighthigh.org
,将大约20%的流量转发到weightlow.org
。
第六章 Gateway过滤功能
Spring Cloud Gateway根据作用域划分为GatewayFilter
和GlobalFilter
两类。
6.1、内置的网关过滤器
GatewayFilter
:网关过滤器,需要通过spring.cloud.routes.filters
配置在具体路由下,只作用在当前路由上或通过spring.cloud.default-filters
配置在全局,作用在所有路由上。内置有好几十种网关过滤器,我们在这里只讲解最常见到的几种过滤器。
官网过滤器列表:https://docs.spring.io/spring-cloud-gateway/docs/2.2.6.RELEASE/reference/html/#gatewayfilter-factories
6.1.1、RewritePath
过滤器介绍:RewritePath 网关过滤器工厂采用路径正则表达式参数和一个替换参数,使用 Java 正则表达式灵活地重写请求路径,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
#匹配请求地址以/api-gateway/开头的请求,实际上,我们后边SERVICE-CONSUMER9002中根本就没有以/api-gateway/开头的请求
#你直接访问,例如:http://localhost:5002/api-gateway/consumer/product/findAll,你访问肯定报404
#你要是访问这个地址也能访问成功对应的业务,那你就需要使用正则表达式,把真正的请求地址提取出来,然后重写,然后再访问
- Path=/api-gateway/**
filters:
#(?<segment>/?.*)就相当于一个模板参数,用来匹配/**这里的路径,可以是多级,然后重写到逗号后边的那个$\{segment}地址
#这里本应该是直接引用${segment},但是由于YAML规范,$应将替换$\
#如果访问的是http://localhost:5002/api-gateway/consumer/product/findAll
#那么(?<segment>/?.*)就代表了:/consumer/product/findAll
#经过重写之后就是http://localhost:5002/consumer/product/findAll,这样我们就能正常访问对应的业务了
- RewritePath=/api-gateway(?<segment>/?.*), $\{
segment}
在浏览器输入对应的地址进行测试:http://localhost:5002/api-gateway/consumer/product/findAll
6.1.2、StripPrefix
过滤器介绍:StripPrefix 网关过滤器采用一个参数StripPrefix,该参数表示在将请求发送到下游之前从请求中剥离的路径个数,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
#匹配请求地址以/api-gateway/开头的请求,实际上,我们后边SERVICE-CONSUMER9002中根本就没有以/api-gateway/开头的请求
#你直接访问,例如:http://localhost:5002/api-gateway/consumer/product/findAll,你访问肯定报404
#你要是访问这个地址也能访问成功对应的业务,我们还有一种方法,可以直接忽略/api-gateway,/**该怎么请求还怎么请求
- Path=/api-gateway/**
filters:
#你想要跳过几层就写几,注意,这个不是从0开始的
- StripPrefix=1
在浏览器输入对应的地址进行测试:http://localhost:5002/api-gateway/consumer/product/findAll
6.1.3、PrefixPath
过滤器介绍:PrefixPath 网关过滤器为指定匹配的路径添加统一前缀,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
- Path=/**
filters:
- PrefixPath=/consumer/product
在浏览器输入对应的地址进行测试:http://localhost:5002/findAll
6.1.4、SetPath
过滤器介绍:SetPath 网关过滤器采用路径模板参数,它提供了一 种通过允许模板化路径段来操作请求路径的简单方法,使用了Spring Framework中的URI模板,允许多个匹配段,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
- Path=/{
segment}
filters:
#拿到{segment}重写路径/consumer/product/{segment}
- SetPath=/consumer/product/{
segment}
在浏览器输入对应的地址进行测试:http://localhost:5002/findAll
6.1.5、SetStatus
过滤器介绍:SetStatus过滤器可以在响应后设置响应状态码,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
- Path=/consumer/product/**
filters:
#访问无论成不成功,都返回404
- SetStatus=404
在浏览器输入对应的地址进行测试:http://localhost:5002/consumer/product/findAll
6.1.6、AddRequestParameter
过滤器介绍:在用户请求的时候可以给用户添加请求参数,示例配置如下参考(拷贝完成请重启应用):
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
- Path=/consumer/product/**
filters:
#/consumer/product/findByPid,后边必须带一个参数pid,我们也可以不带,给他设置一个默认值
- AddRequestParameter=pid,0
在浏览器输入对应的地址进行测试:http://localhost:5002/consumer/product/findByPid
6.1.7、AddRequestHeader
过滤器介绍:对于所有匹配的请求,将会给传给下游的请求添加一个请求头 X-Request-Foo:Bar,这里就不演示了。
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
- Path=/consumer/product/**
filters:
- AddRequestHeader=X-Request-Foo,Bar
6.1.8、AddResponseHeader
过滤器介绍:对于所有匹配的请求,添加一个响应头 X-Response-Foo:Bar,这里就不演示了。
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
- Path=/consumer/product/**
filters:
- AddResponseHeader=X-Response-Foo,Bar
6.2、内置的全局过滤器
GlobalFilter
:全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter
包装成GatewayFilterChain
可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务请求地址的核心过滤器,不需要配置系统初始化时加载,并作用在每个路由上。
官网过滤器列表:https://docs.spring.io/spring-cloud-gateway/docs/2.2.6.RELEASE/reference/html/#global-filters
6.3、自定义网关过滤器
6.3.1、创建过滤器
自定义网关过滤器需要实现两个接口:GatewayFilter
、Ordered
。
com.caochenlei.filter.MyGatewayFilter
public class MyGatewayFilter implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("==========自定义网关过滤器开始==========");
System.out.println(exchange.getRequest().getPath());
System.out.println("==========自定义网关过滤器结束==========");
return chain.filter(exchange);
}
//过滤器链执行顺序,数值越小,执行顺序越靠前
@Override
public int getOrder() {
return 0;
}
}
6.3.2、注册过滤器
修改全局配置文件中的使用代码进行路由的部分,代码如下:
com.caochenlei.config.GatewayConfig
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
return routeLocatorBuilder.routes()
//可以使用通配符,例如:/provider/product/**
.route("SERVICE-PROVIDER-ROUTE1",
r -> r.path("/provider/product/findAll")
.uri("lb://SERVICE-PROVIDER")
.filter(new MyGatewayFilter())
)
.route("SERVICE-PROVIDER-ROUTE2",
r -> r.path("/provider/product/findByPid")
.uri("lb://SERVICE-PROVIDER")
.filter(new MyGatewayFilter())
)
.build();
}
}
6.3.3、测试过滤器
重启当前应用,打开浏览器进行访问:http://localhost:5002/provider/product/findAll
6.4、自定义全局过滤器
6.4.1、创建过滤器
自定义网关过滤器需要实现两个接口:GlobalFilter
、Ordered
。
com.caochenlei.filter.MyGlobalFilter
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("==========自定义全局过滤器开始==========");
System.out.println(exchange.getRequest().getPath());
System.out.println("==========自定义全局过滤器结束==========");
return chain.filter(exchange);
}
//过滤器链执行顺序,数值越小,执行顺序越靠前
@Override
public int getOrder() {
return 0;
}
}
6.4.2、注册过滤器
全局过滤器不用注册,因为过滤器类上有@Component
。
6.4.3、测试过滤器
重启当前应用,打开浏览器进行访问:http://localhost:5002/provider/product/findAll
第七章 Gateway熔断降级
7.1、熔断降级含义
熔断降级:在分布式系统中,网关作为流量的入口,大量请求进入网关,向后端远程系统或服务发起调用,后端服务不可避免的会产生调用失败(超时或者异常),失败时不能让请求堆积在网关上,需要快速失败并返回回去,这就需要在网关上做熔断、降级操作。
7.2、添加依赖文件
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
7.3、编写配置文件
application.yaml
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
cloud:
gateway:
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
- Path=/consumer/product/**
filters:
- name: Hystrix
args:
name: default
fallbackUri: 'forward:/defaultFallback' #服务降级转发到/defaultFallback
# hystrix 信号量隔离,2秒后自动超时
hystrix:
shareSecurityContext: true
command:
default:
execution:
isolation:
strategy: SEMAPHORE
thread:
timeoutInMilliseconds: 2000
7.4、编写降级方法
com.caochenlei.controller.DefaultHystrixController
@RestController
public class DefaultHystrixController {
@RequestMapping("/defaultFallback")
public Map<String, String> defaultFallback() {
Map<String, String> map = new HashMap<>();
map.put("code", "500");
map.put("msg", "服务降级");
map.put("data", "null");
return map;
}
}
7.5、网关熔断测试
重新启动当前应用,然后快速刷新错误的地址,我们这里连刷20回(为了更好的出效果),然后你再快速刷新正常的地址,发现正常的地址也不能访问,没有关系,继续刷新,慢慢的,该服务又恢复了正常,这就是服务熔断,在这里,我不好截图,只贴出正常地址和错误地址,自己测试吧。
错误地址:http://localhost:5002/consumer/product/findByPid?pid=-1
正常地址:http://localhost:5002/consumer/product/findByPid?pid=0
超时地址:http://localhost:5002/consumer/product/findByPid?pid=1
最终调用的业务模拟了各种的逻辑:
7.6、网关降级测试
我们手动把service-consumer9002
停止,模拟目标服务宕机,然后访问正常地址,观察页面:http://localhost:5002/consumer/product/findByPid?pid=0
测试完成以后,你需要把service-consumer9002
重新启动。
第八章 Gateway网关限流
8.1、网关限流含义
网关上有大量请求,对指定服务进行限流,可以很大程度上提高服务的可用性与稳定性,限流的目的是通过对并发访问/请求进行限速,或对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级。
8.2、网关限流算法
一般来说,实现网关限流有三种常见的算法:
计数器算法:
计数器算法是限流算法里最简单也是最容易实现的一种算法。比如我们规定,对于A接口来说,我们1分钟的访问次数不能超过100个。那么我们可以这么做:在一开始的时候,我们可以设置一个计数器 counter,每当一个请求过来的时候,counter就加1,如果counter的值大于100并且该请求与第一个请求的间隔时间还在 1分钟之内,触发限流,如果该请求与第一个请求的间隔时间大于1分钟,重置counter重新计数,具体算法的示意图如下:
这个算法虽然简单,但是有一个十分致命的问题,那就是临界问题,我们看下图:
从上图中我们可以看到,假设有一个恶意用户,他在0:59时,瞬间发送了100个请求,并且1:00又瞬间发送了100个请求,那么其实这个用户在1秒里面,瞬间发送了200个请求。我们刚才规定的是1分钟最多100个请求,也就是每秒钟最多1.7个请求,用户通过在时间窗口的重置节点处突发请求,可以瞬间超过我们的速率限制,用户有可能通过算法的这个漏洞,瞬间压垮我们的应用。除了安全问题以外,还存在资源分配不均的问题,如果你前30秒钟就用完了100个请求,就会导致后30秒无法请求服务等等问题。
桶漏算法:
漏桶算法其实也很简单,可以粗略的认为就是注水漏水的过程,往桶中以任意速率流入水,以一定速率流出水,当水超过桶流量则丢弃,因为桶容量是不变的,保证了整体的速率,具体算法的示意图如下:
桶漏算法是使用队列机制实现的。
这种算法也有问题,当大量的请求堆积到网关这里,很有可能导致网关服务不可用,并且后续的请求也无法进入到队列桶中,导致请求的丢失。
令牌桶算法:
令牌桶算法是对漏桶算法的一种改进,漏桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌。
他运用的场景大概是这样的:桶中一直有大量的可用令牌,这时进来的请求可以直接拿到令牌执行,比如设置QPS为100/s,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,等服务启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。当桶中没有令牌时,请求会进行等待,最后相当于以一定的速率执行。
Spring Cloud Gateway内部使用的就是该算法,大概描述如下:
- 所有的请求在处理之前都需要拿到一个可用的令牌才会被处理。
- 根据限流大小,设置按照一定的速率往桶里添加令牌。
- 桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝。
- 请求到达后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除。
- 令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流。
8.3、添加依赖文件
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
8.4、编写配置文件
application.yaml
#这里只给出了局部配置,其他配置保持不变
spring:
application:
name: gateway-cloud5002
redis:
host: localhost
port: 6379
database: 0
lettuce:
pool:
max-active: 1024 #最大连接数
max-wait: 10000 #最大等待时间,单位毫秒
max-idle: 256 #最大空闲连接
min-idle: 8 #最小空闲连接
cloud:
gateway:
#路由配置
routes:
- id: study-predicates-route
uri: lb://SERVICE-CONSUMER9002
predicates:
- Path=/consumer/product/**
filters:
#服务的熔断和降级配置(内置的过滤器)
- name: Hystrix
args:
name: default
fallbackUri: 'forward:/defaultFallback' #服务降级转发到/defaultFallback
#网关的限流过滤器配置(内置的过滤器)
- name: RequestRateLimiter
args:
key-resolver: '#{@pathKeyResolver}' #使用 SpEL 表达式按名称引用bean,一共有三种,名称请查看配置对象
redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充速率
redis-rate-limiter.burstCapacity: 2 #令牌桶总的容量大小,写2方便测试
com.caochenlei.config.GatewayConfig
@Configuration
public class GatewayConfig {
...
...
/** * 如果你想要根据用户的ip地址来限流,请打开这个@Bean注解,三种方式不能同时存在 * application.yaml请配置,key-resolver: '#{@ipKeyResolver}' */
//@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
/** * 如果你想要根据用户的名称来限流,请打开这个@Bean注解,但是,请求地址一定要包含userId这个参数,三种方式不能同时存在 * application.yaml请配置,key-resolver: '#{@userKeyResolver}' * 注意:此种方式并不是一定是userId,你可以指定任意参数名称,前提是你的请求中一定要包含该参数,所以这种方法又叫做根据请求参数限流 */
//@Bean
KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}
/** * 如果你想要根据用户的请求地址来限流,请打开这个@Bean注解,三种方式不能同时存在 * application.yaml请配置,key-resolver: '#{@pathKeyResolver}' */
@Bean
KeyResolver pathKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
}
8.5、启动redis应用
Windows版本的Redis请在此地址下载:https://github.com/MicrosoftArchive/redis/releases,如果下载失败,请到配套资料中寻找该软件。
下载完成以后,请你解压到一个不包含中文以及空格的目录当中,然后在此路径下使用cmd
命令行的方式启动Redis应用:
8.6、网关限流测试
请重新启动当前应用,然后在浏览器的地址中快速刷新4次(桶内容量2个,每秒生成1个,刷新4次保险,有可能在第3次的时候就已经限流了)。
正常业务地址:http://localhost:5002/consumer/product/findByPid?pid=0
第九章 Gateway跨域问题
您可以配置网关以控制CORS行为。“全局” CORS配置是URL模式到Spring Framework CorsConfiguration
的映射。以下示例配置了CORS:
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
# 允许携带认证信息
# 允许跨域的源(网站域名/ip),设置*为全部
# 允许跨域请求里的head字段,设置*为全部
# 允许跨域的method, 默认为GET和OPTIONS,设置*为全部
# 跨域允许的有效期
allow-credentials: true
allowed-origins:
- "http://localhost:9002"
- "http://localhost:9003"
allowed-headers: "*"
allowed-methods:
- OPTIONS
- GET
- POST
max-age: 3600
#exposed-headers:
# 允许response的head信息
# 默认仅允许如下6个:
# Cache-Control
# Content-Language
# Content-Type
# Expires
# Last-Modified
# Pragma
第十章 Gateway统一鉴权
关于网关访问时必须要统一验证请求是否有效时,有很多种办法,我这里给出Spring Boot+Spring Security+JWT实现单点登录的解决思路来实现网关统一验证。
学习地址:https://caochenlei.blog.csdn.net/article/details/112973713
实现思路:
- 创建一个网关认证的服务,来统一进行登录认证,认证成功给用户返回一个token值。
- 在每一个网关中,都创建一个全局过滤器,这个过滤器的作用就是获取用户请求时所带来的
token
值,然后对token
的有效性进行验证。 - 验证成功就继续执行过滤器链,验证失败就直接给用户响应一个
json
串数据,告诉用户认证失败。
注意:这种方法可以应用到网关集群模式,因为每一个网关只负责校验
token
,并不做认证处理,认证都统一在网关认证服务中来完成,网关集群的搭建没啥特别的,最后再上线的时候,在使用nginx
在对网关进行一层负载均衡处理,这样,一个高可用的网关集群就搭建好了。在这里,我就不详细贴出代码了,上边提供的那一片文章写的很详细,如果你是真的有需要,可以留言,后续我在更新此章节。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/183622.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...