重新定义cloud 第17章 spring cloud gateway上篇
gateway 概述
- 基于 spring 5.0
- spring boot 2.0
- project reactor
- 提供简单,有效且统一, api路由 管理方式
基于 filter 链的方式 提供了 网关基本的 功能
- 安全
- 监控/埋点
- 限流
- 协议适配
- 协议转发
- 安全策略 waf
- 防刷
- 流量
- 监控日志
- 网关的 核心 Filter 以及filter chain (filter 责任链)
路由 route(包含)
- id
- 目的 url
- 断言工厂
- 一组filter
- 断言为真,则 说明url和 配置的路由匹配
断言 predicate
- spring 5.0 的 serverWebExchange
- 允许开发者 去定义匹配 来自于 http Request中的任何信息(请求头,参数)
过滤器 filter
- 一个标准的spring filter
- gateWay filter
- globbal filter
- 过滤器 filter 将会 对 请求和响应 进行 修改处理。
工作原理
- 默认端口,80,https 443
- 启动容器只支持 netty
- gateway client
spring cloud gateway
- HttpWebHandlerAdapter 组装网关上下文
- DispatcheerHandler 分发处理器,循环遍历 Mapping,获取 Handler
- Route Predicate Handler Mapping 断言处理器,判断是否可用
- 断言成功
Filtering Web Handler 创建过滤器,调用过滤链
- pre filter
- post filter
- proxied service 代理服务器
入门案例
协议适配
- 路由信息转发
- 协议转发
- 本案例演示,Path路由断言工厂 实现 url 直接转发
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--Spring Cloud Gateway的Starter-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
主启动类
@SpringBootApplication
public class SpringCloudGatewayApplication {
/** * 基本的转发 * 当访问http://localhost:8080/jd * 转发到http://jd.com * @param builder * @return */
@Bean // Locator n. 定位器,探测器
public RouteLocator custom Route Locator(RouteLocatorBuilder builder) {
return builder.routes()
//basic proxy
.route(r ->r.path("/jd")
.uri("http://jd.com:80/").id("jd_route")
).build();
} //第一种配置
public static void main(String[] args) {
SpringApplication.run(SpringCloudGatewayApplication.class, args);
}
}
第二种配置。application.yml
spring:
cloud:gateway:
routes: #当访问http://localhost:8080/baidu,直接转发到https://www.baidu.com/
- id: baidu_route
uri: http://baidu.com:80/
predicates:
- Path=/baidu
application.yml
server:
port: 8080
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes: #当访问http://localhost:8080/baidu,直接转发到https://www.baidu.com/
- id: baidu_route
uri: http://baidu.com:80/
predicates:
- Path=/baidu
logging: ## Spring Cloud Gateway的日志配置
level:
org.springframework.cloud.gateway: TRACE
org.springframework.http.server.reactive: DEBUG
org.springframework.web.reactive: DEBUG
reactor.ipc.netty: DEBUG
management:
endpoints:
web:
exposure:
include: '*'
security:
enabled: false
trace
英 /treɪs/ 美 /treɪs/
v. (通过调查)找到;追溯;追踪;沿(特定路径)走;映描;(尤指用手指、脚趾)画;勾画出轮廓
n. 痕迹;踏出来的小路;(大脑上的学习或记忆)痕迹;语迹;跟踪;微量;丝毫;描记线;交线;缰绳
开启端点
- 如上配置
- 提供了 gateway actuator
- 关于 filter和 routes 信息查询,制定 route信息更新的 Rest API
http://localhost:8080/actuator/gateway/routes
[{
"route_id": "jd_route", //jd的转发 断言是这个类
"route_object": {
"predicate": "org.springframework.cloud.gateway.support.ServerWebExchangeUtils$$Lambda$293/70528019@27137286"
},
"order": 0
}, {
"route_id": "baidu_route",
"route_definition": {
"id": "baidu_route",
"predicaates": [{
"name": "Path",
"args": {
"_genkey_0": "/baidu" //本地的请求
}
}],
"filters": [],
"uri": "http://baidu.com:80/", //转发的路径
"order": 0
},
"order": 0
}]
路由 断言
- 路由匹配 是 spring webFlux的 Handler Mapping 为 基础实现的
- 有许多的 路由断言 工厂
- 路由断言工厂会根据配置的路由规则,对 请求进行 断言匹配
- 匹配成功进行下一步处理,否则 断言失败,返回错误信息
after路由器断言工厂
- after route predicate factory
- 取UTC 时间格式 UTC一般指协调世界时。协调世界时,又称世界统一时间、世界标准时间、国际协调时间。
- 当请求进来的当前时间 在配置的 UTF 时间之后,会匹配成功。否则失败。
- 当前请求在,配置时间之后。才会成功、
路由配置代码
@SpringBootApplication
public class SCGatewayApplication {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//生成比当前时间早一个小时的UTC时间
ZonedDateTime minusTime = LocalDateTime.now().minusHours(1).atZone(ZoneId.systemDefault());
return builder.routes()
.route("after_route", r -> r.after(minusTime)
.uri("http://baidu.com"))
.build();
}
public static void main(String[] args) {
SpringApplication.run(SCGatewayApplication.class, args);
}
}
如果:.plusHours(1) 就会失败,因为当前请求的时间,在之前了。
或者
spring:
cloud:gateway:
routes: #当访问http://localhost:8080/baidu,直接转发到https://www.baidu.com/
- id: baidu_route
uri: http://baidu.com:80/
predicates:
- Path=/baidu #要加百度的访问
- After=2020-11-13T14:26:38.584+08:00[Asia/Shanghai]
UTC工具类
/** * 生成UTC时间 */
public class UtcTimeUtil {
public static void main(String[] args) {
ZonedDateTime time= ZonedDateTime.now();
System.out.println("zonedDateTime:"+time);
String maxTime=ZonedDateTime.now().plusHours(1).format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
System.out.println("maxTime:"+maxTime);
//用这个会出现:Handler Terminated 处理程序终止 无法跳转
String minTime=ZonedDateTime.now().minusHours(1).format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
System.out.println("minTime:"+minTime);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str=time.format(formatter);
System.out.println(str);
}
}
测试
- 如果使用类配置,直接访问:http://localhost:8081
- 如果用类配置,不会配置请求http://localhost:8081/baidu
- r.path(“/baidu”)
.uri("http://baidu.com").id("aaa")
before 路由器断言 工厂
当请求进来的时间,在 路由器断言工厂之前 ,会匹配成功
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//加一天,保证当前的请求,是在 之前的。
//Handler Terminated(如果当前的请求在 之后)
ZonedDateTime datetime = LocalDateTime.now().plusDays(1).atZone(ZoneId.systemDefault());
return builder.routes()
.route("before_route", r -> r.before(datetime)
.uri("http://baidu.com"))
.build();
}
- http://localhost:8080 会转发到baidu
Between断言工厂
代码控制
@SpringBootApplication
public class SCGatewayApplication {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
ZonedDateTime datetime1 = LocalDateTime.now().minusDays(1).atZone(ZoneId.systemDefault());
ZonedDateTime datetime2 = LocalDateTime.now().plusDays(1).atZone(ZoneId.systemDefault());
return builder.routes()
.route("between_route", r -> r.between(datetime1,datetime2)
.uri("http://baidu.com"))
.build();
}
public static void main(String[] args) {
SpringApplication.run(SCGatewayApplication.class, args);
}
}
yaml控制
spring:
cloud:
gateway:
routes:
- id: between_route
uri: http://xujin.org
predicates:
- name: Between
args:
datetime1: 2018-03-15T00:02:48.513+08:00[Asia/Shanghai]
datetime2: 2018-03-15T02:02:48.516+08:00[Asia/Shanghai]
cookie 断言工厂
- 会取, key 和 value
- 当请求携带的 cookie 和 cookied 断言工厂中配置的 cookie 一致,匹配成功
代码控制
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("cookie_route", r -> r.cookie("chocolate", "ch.p")
.uri("http://localhost:8071/test/cookie"))
.build();
}
yaml 控制
server:
port: 8080
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: between_route
uri: http://xujin.org
predicates:
- Cookie=chocolate, ch.p
请求到的代码
@GetMapping("/test/cookie")
public String testGateway(HttpServletRequest request, HttpServletResponse response){
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
System.out.println(cookie.getName()+":"+cookie.getValue());
}
}
return "Spring Cloud Gateway,Hello world!";
}
测试
- http://localhost:8080/
- header 增加:key为:Cookie ,value: chocolate=ch.p
- 只有用这个headr头 才能访问
header 路由断言工厂
header 断言的两种方法
根据 路由的 header 信息断言
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("header_route", r -> r.header("X-Request-Id", "xujin")
.uri("http://localhost:8071/test/head"))
.build();
}
或者
spring:
cloud:gateway:
routes:
- id: header_route
uri: http://localhost:8071/test/head
predicates:
- Header=X-Request-Id, xujin
被请求的项目
/** * 测试Head路由断言工厂 * @param request * @param response * @return */
@GetMapping("/test/head")
public String testGatewayHead(HttpServletRequest request, HttpServletResponse response){
String head=request.getHeader("X-Request-Id");
return "return head info:"+head;
}
Host 路由断言工厂
对请求中的 Host 进行断言处理,断言成功则进行路由转发。
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("host_route", r -> r.host("**.baidu.com:8080")
.uri("http://jd.com"))
.build();
}
server:
port: 8080
spring:
application:name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: header_route
uri: http://jd.com
predicates:
- Host=**.baidu.com:8080
更改host文件,把vip.baidu.com映射到:127.0.0.1
- 访问:http://vip.baidu.com:8080 即可跳转到京东
Method 断言工厂
根据路由信息配置的 method对 请求方法 是 Get 或者 Post 等 进行断言
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("method_route", r -> r.method("GET")
.uri("http://jd.com"))
.build();
}
server:
port: 8080
spring:
application:name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: header_route
uri: http://jd.com
predicates:
- Method=GET
访问:http://localhost:8080/ 即可。
Query 断言工厂
- 从请求中 获取两个参数,和 Query 断言路由器中的配置 进行匹配
http://localhost:8080/?foo=baz 和 r.query(“foo”,“baz”) 配置对比
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("query_route", r -> r.query("foo","baz")
.uri("http://baidu.com"))
.build();
}
server:
port: 8080
spring:
application:name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: header_route
uri: http://jd.com
predicates:
- Query=foo, baz
RemoteAddr 路由器断言工厂
- ipv4 或 v6 网段,或者IP
当请求的 IP 在网段内,匹配成功,否则失败
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("remoteaddr_route", r -> r.remoteAddr("127.0.0.1")
.uri("http://baidu.com"))
.build();
}
spring:
cloud:gateway:
routes:
- id: remoteaddr_route
uri: http://baidu.com
predicates:
- RemoteAddr=127.0.0.1
访问:http://localhost:8080/ 不行。
- 访问:http://127.0.0.1:8080/ 转发成功
- 或者 配本地的IP,也是可以测试的。
Spring cloud gateway 的 内置 filter
- 路由过滤器 允许 以某种方式 修改请求进来的 http请求
- 或 返回的 http 响应
- 主要作用:处理 特定路由
- 过滤器 将近20多个
分为7类
- Header
- Paramter
- Path
- Status
- Redirect跳转
- Hystrix熔断
- RateLimiter
AddRequestHeader 过滤器工厂
对匹配的请求 加上 Header
@Bean
public RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("add_request_header_route", r ->
r.path("/test").filters(f ->
f.addRequestHeader
("X-Request-Acme", "ValueB"))
.uri("http://localhost:8071/test/head"))
.build();
}
/* @param request @param response @return */
@GetMapping("/test/head")
public String testGatewayHead(HttpServletRequest request, HttpServletResponse response){
String head=request.getHeader("X-Request-Acme");
return "return head info:"+head;
}
访问:http://localhost:8080/test
- 返回:return head info:ValueB
AddRequestParameter过滤器
对 匹配上的请求路由 添加 请求参数
@Bean
public RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("add_request_parameter_route", r ->
r.path("/addRequestParameter").filters(f -> f.addRequestParameter("example", "ValueB"))
.uri("http://localhost:8071/test/addRequestParameter"))
.build();
}
@GetMapping(“/test/addRequestParameter”)
public String addRequestParameter(HttpServletRequest request, HttpServletResponse response){
String parameter=request.getParameter("example");
return "return addRequestParameter info:"+parameter;
}
访问:http://localhost:8080/addRequestParameter
- 返回:return addRequestParameter info:ValueB
RewritePath过滤器
RewritePath 替代 Zuul 的 StripPrefix
path: /demo/**
stripPrefix: true
url: http://demo.com
- 所有 /demo/xxxx的请求 转发给 http://demo.com/xxx ,去掉demo 前缀
gateway使用的是 RewritePath
@Bean
public RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewritepath_route", r ->
r.path("/foo/**").filters(f -> f.rewritePath("/foo/(?<segment>.*)","/$\\{segment}"))
.uri("http://www.baidu.com"))
.build();
}
访问:http://localhost:8080/foo/cache/sethelp/help.html
- 将会去掉:/foo。http://www.baidu.com/cache/sethelp/help.html
- /foo/(?.*) 替换为 /$\{segment}
Add Response Header过滤器
对网关 返回的 响应添加 header
@Bean
public RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("add_request_header_route", r ->
r.path("/test").filters(f -> f.addResponseHeader("X-Response-Foo", "Bar"))
.uri("http://www.baidu.com"))
.build();
}
//很神奇,去掉www,后转发成功了,之后加上www也可以了。
//好吧,确实不能加 www,缓存问题http://localhost:8080/test
- 转发的京东 请写:http://jd.com:80/
StripPrefix过滤器
- StripPrefix Gateway Filter Factory
- 针对请求 url前缀进行处理的 filter工厂,用于去除前缀
Prefix Path Gateway Filter Factory 是用于增加前缀
server:
port: 8080
spring:
application:name: spring-cloud-gateway
spring:
cloud:gateway:
routes:
- id: baidu_route
uri: http://www.baidu.com
predicates:
- Path=/baidu/test/**
filters:
- StripPrefix=2
logging:
level:org.springframework.cloud.gateway: TRACE
org.springframework.http.server.reactive: DEBUG
org.springframework.web.reactive: DEBUG
reactor.ipc.netty: DEBUG
访问:http://localhost:8080/baidu/test
- 跳转到: http://www.baidu.com 。去除:/baidu/test/
Retry过滤器
- 对网络请求 进行重试
使用 重试 filter 进行重试
@Bean
public RouteLocator retryRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("retry_route", r -> r.path("/test/retry")
.filters(f ->f.retry(config -> config.setRetries(2).setStatuses(HttpStatus.INTERNAL_SERVER_ERROR)))
.uri("http://localhost:8071/retry?key=abc&count=2"))
.build();
}
设置重试 次数为 两次
- 代理服务 调用失败时,设置 返回的状态码 为 500 (服务器内部错误)
重试到 action的代码
ConcurrentHashMap<String, AtomicInteger> map = new ConcurrentHashMap<>();
@GetMapping("/retry")
public String testRetryByException(@RequestParam("key") String key, @RequestParam(name = "count") int count) {
AtomicInteger num = map.computeIfAbsent(key, s -> new AtomicInteger());
//对请求或重试次数计数
int i = num.incrementAndGet();
log.warn("重试次数: "+i);
//计数i小于重试次数2抛出异常,让Spring Cloud Gateway进行重试
if (i < count) {
throw new RuntimeException("Deal with failure, please try again!");
}
//当重试两次时候,清空计数,返回重试两次成功
map.clear();
return "重试"+count+"次成功!";
}
测试
- http://localhost:8080/test/retry
- 第一次:AtomicInteger num 为0,num.incrementAndGet(); 为1
- 第二次:AtomicInteger num 为1,num.incrementAndGet(); 为2
- key=abc 作为:ConcurrentHashMap 的 key 统计 计数
hystrix 过滤器
- 熔断,自我保护,服务降级,快速失败
- gateway调用后端服务,后端服务一致异常。对服务进行降级。
xml
<dependencies>
<!-- Spring Cloud Gateway Starter-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- hystrix的Starter-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
yaml
server:
port: 8080
spring:
application:
name: spring-cloud-gateway
spring:
cloud:
gateway:
routes:
- id: prefix_route
uri: http://localhost:8071/test/Hystrix?isSleep=true
predicates:
- Path=/test/Hystrix
filters:
- name: Hystrix # Hystrix Filter的名称
args: # Hystrix配置参数
name: fallbackcmd #HystrixCommand的名字
fallbackUri: forward:/fallback #fallback对应的uri
#Hystrix的fallbackcmd的时间
hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 5000
logging:
level:
org.springframework.cloud.gateway: TRACE
org.springframework.http.server.reactive: DEBUG
org.springframework.web.reactive: DEBUG
reactor.ipc.netty: DEBUG
本项目的回退
@RestController
public class FallbackController {
@GetMapping("/fallback")
public String fallback() {
return "Spring Cloud Gateway Fallback!";
}
}
远程请求的 接口
@GetMapping("/test/Hystrix")
public String index(@RequestParam("isSleep") boolean isSleep) throws InterruptedException {
log.info("issleep is " + isSleep);
//isSleep为true开始睡眠,睡眠时间大于Gateway中的fallback设置的时间
if (isSleep) {
TimeUnit.MINUTES.sleep(10); //这里怎么睡成分钟了
}
return "No Sleep";
}
测试
- http://localhost:8080/test/Hystrix
- 返回:Spring Cloud Gateway Fallback!
- 如果改成睡4秒,返回为:No Sleep
还没有评论,来说两句吧...