这一章开始接上zuul路由网关,接着写一下服务网gateway。
相比于zuul而言,gateway的功能更加强大,可以说zuul有的路由和过滤 gateway都有,还比zuul多一个断言功能。
所谓断言就是在http进行请求到路由的时候会将请求和路由进行匹配,匹配的过程就会用到断言,之后会决定走哪一个路由。
而匹配方式有很多种,会涉及到很多的路由工厂。
如:
After 路由断言工厂
Before路由断言工厂
Between路由断言工厂
Cookie路由断言工厂
Header路由断言工厂
Host路由断言工厂
Method路由断言工厂
Path路由断言工厂
Query路由断言工厂
RemoteAddr路由断言工厂 等。
进入到路由后还可以使用 pre 和 post 的两种方式进行过滤器的一系列操作。过滤器的工厂如下:
AddRequestHeader网关过滤工厂----添加请求头
AddRequestParameter网关过滤工厂----添加请求参数
AddResponseHeader网关过滤工厂----添加响应头
DedupeResponseHeader网关过滤工厂----剔除重复响应头
CircuitBreaker网关过滤工厂----熔断(替代之前的hystrix)
FallbackHeaders网关过滤工厂----请求转发
MapRequestHeader
网关过滤工厂----扩充请求头
PrefixPath
网关过滤工厂----添加前缀
PreserveHostHeader
网关过滤工厂----保留原请求头
RequestRateLimiter
网关过滤工厂----请求限流
RedirectTo
网关过滤工厂----重定向
RemoveRequestHeader网关过滤工厂----删除请求头
RemoveResponseHeader
网关过滤工厂----删除响应头
RemoveRequestParameter
网关过滤工厂----删除请求参数
RewritePath
网关过滤工厂----重写路径
RewriteLocationResponseHeader
网关过滤工厂----重写响应头
RewriteResponseHeader
网关过滤工厂----以正则表达式的方式重写响应头的值
SaveSession
网关过滤工厂----保存session
SecureHeaders
网关过滤工厂----添加一系列的安全头
SetPath
网关过滤工厂----设置路径
SetRequestHeader
网关过滤工厂----替换请求头
SetResponseHeader
网关过滤工厂----替换响应头
SetStatus
网关过滤工厂----修改状态
StripPrefix
网关过滤工厂----阶段路径
Retry
网关过滤工厂----重试
RequestSize
网关过滤工厂----设置请求数据大小
ModifyRequestBody网关过滤工厂----修改请求体
ModifyResponseBody网关过滤工厂----修改响应体
default网关过滤工厂----默认的,可以应用于所有的路由
---------------------------------------------------------------
有点蒙?
首先来一个小Demo练练手,熟悉一下流程。就知道gateway是干嘛的了
1.1、创建gateway-service服务
修改pom文件(添加)
org.springframework.cloud
spring-cloud-starter-contract-stub-runner
spring-boot-starter-web
org.springframework.boot
1.2、配置过滤器
package com.example.gatewayservice.config;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@SpringBootConfiguration
@Component
public class GatewayConfig {
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri("http://httpbin.org:80"))
.route(p -> p
.path("/delay/**")
// .host("www.hystrix.com")
.filters(f -> f.hystrix(config -> config
.setName("mycmd")
.setFallbackUri("forward:/fallback")))
.uri("http://httpbin.org:80"))
.build();
}
}
说明:
第一个route,里面是函数的匿名表达式,在header中添加"Hello":"World"和请求转发处理。
第二个route,增加了hystrix的熔断降级处理
1.3、添加熔断处理方法
package com.example.gatewayservice.controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
public class GatewayController {
@RequestMapping("/fallback")
public Mono fallback() {
return Mono.just("fallback");
}
}
1.4、使用curl进行测试
1.4.1 测试请求转发
curl localhost:8080/get
{
"args": {},
"headers": {
"Accept": "*/*",
"Content-Length": "0",
"Forwarded": "proto=http;host=\"localhost:8080\";for=\"0:0:0:0:0:0:0:1:52020\"",
"Hello": "World",
"Host": "httpbin.org",
"User-Agent": "curl/7.55.1",
"X-Amzn-Trace-Id": "Root=1-5f198f9a-8bc3e804b36e6ae4f32721d2",
"X-Forwarded-Host": "localhost:8080"
},
"origin": "0:0:0:0:0:0:0:1, 58.33.79.49",
"url": "http://localhost:8080/get"
}
1.4.2 测试熔断降级
curl localhost:8080/delay/3
fallback
根据官网案例测试一下host
>curl --dump-header - --header 'Host: www.hystrix.com' http://localhost:8080/delay/3
curl -H 'host: www.hystrix.com' http://localhost:8080/delay/3
curl: (6) Could not resolve host: www.hystrix.com'
HTTP/1.1 404 Not Found
Content-Type: application/json
Content-Length: 136
{"timestamp":"2020-07-23T13:48:10.129+00:00","path":"/delay/3","status":404,"error":"Not Found","message":null,"requestId":"7c365d7e-7"}
什么鬼?我可是按照API来的,curl语法也没错吧!!!
经过一番爬坑发现,换成双引号就行了,有句mmp不知当讲不当讲
--------------------------------------------------------------------------------------
以上是练手部分,虽然可以在config bean 中进行配置,但是总觉得怪怪的。
之前也说了有那么多的断言工厂,接下来将以yml配置文件的形式一个个来。
1.注释或删除掉之前的相关配置,如config.java、application.properties
2.创建application.yml (如果习惯于用properties文件格式也没问题)
2.1、After路由断言工厂
修改application.yml文件
server:
port: 8080
spring:
application:
name: gateway-service
profiles:
active: after_route
添加application-after_route.yml(配置文件的调用,后面不再做说明)
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://httpbin.org/
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
以上是 匹配路由为 2017年1月20日17:42时区(丹佛)之后的任何请求。然后将请求代理转发
测试:
curl localhost:8080/get
{
"args": {},
"headers": {
"Accept": "*/*",
"Content-Length": "0",
"Forwarded": "proto=http;host=\"localhost:8080\";for=\"0:0:0:0:0:0:0:1:64374\"",
"Host": "httpbin.org",
"User-Agent": "curl/7.55.1",
"X-Amzn-Trace-Id": "Root=1-5f1ad7cc-0345d4655f39018e79072992",
"X-Forwarded-Host": "localhost:8080"
},
"origin": "0:0:0:0:0:0:0:1, 58.33.125.63",
"url": "https://localhost:8080/get"
}
Before路由断言工厂 和 Between路由断言工厂 见名知意 给出配置格式,不做测试
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
2.2、Cookie路由断言工厂
有两种配置格式(以下工厂如有相同格式皆可)
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://httpbin.org/
predicates:
- Cookie=chocolate, ch.p
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://httpbin.org/
predicates:
- name: Cookie
args:
name: mycookie
regexp: mycookievalue
将application.yml中的文件改为对cookie_route的调用,然后重启服务测试
curl -H "Cookie:mycookie=mycookievalue" localhost:8080/get
{
"args": {},
"headers": {
"Accept": "*/*",
"Content-Length": "0",
"Cookie": "mycookie=mycookievalue",
"Forwarded": "proto=http;host=\"localhost:8080\";for=\"0:0:0:0:0:0:0:1:64908\"",
"Host": "httpbin.org",
"User-Agent": "curl/7.55.1",
"X-Amzn-Trace-Id": "Root=1-5f1adaf6-b5a5da1473da977e68166041",
"X-Forwarded-Host": "localhost:8080"
},
"origin": "0:0:0:0:0:0:0:1, 58.33.102.47",
"url": "https://localhost:8080/get"
}
2.3 Header路由断言工厂
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
以上是匹配header中有键为X-Request-Id, key为数字的请求
测试:
curl -H "X-Request-Id:111" localhost:8080/get
{
"args": {},
"headers": {
"Accept": "*/*",
"Content-Length": "0",
"Forwarded": "proto=http;host=\"localhost:8080\";for=\"0:0:0:0:0:0:0:1:65454\"",
"Host": "httpbin.org",
"User-Agent": "curl/7.55.1",
"X-Amzn-Trace-Id": "Root=1-5f1ade28-7c4094785381b08095c0b464",
"X-Forwarded-Host": "localhost:8080"
},
"origin": "0:0:0:0:0:0:0:1, 58.33.102.47",
"url": "https://localhost:8080/get"
}
以下断言工厂的应用同理,这边给出配置,不再贴测试结果
2.4 Host路由断言工厂
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://httpbin.org/
predicates:
- Host=**.somehost.org,**.anotherhost.org
2.5 Method路由断言工厂
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://httpbin.org/
predicates:
- Method=GET,POST
2.6 Path路由断言工厂
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://httpbin.org/
predicates:
- Path=/red/{segment},/blue/{segment}
2.7 Query路由断言工厂
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://httpbin.org/
predicates:
- Query=green
2.8 RemoteAddr路由断言工厂
spring:
cloud:
gateway:
routes:
- id: remoteAddr_route
uri: https://httpbin.org/
predicates:
- RemoteAddr=192.168.1.1/24
2.9 Weight路由断言工厂
spring:
cloud:
gateway:
routes:
- id: weight_route
uri: https://httpbin.org/
predicates:
- Weight=192.168.1.1/24
根据作用返回可以分为网关过滤器和全局过滤器
网关过滤器:只作用在所配置的路由上(或者配置作用全局,作用于全局)
全局过滤器:不需要配置,作用在所有的路由上
网关过滤器
3.1 AddRequestHeader网关过滤工厂----添加请求头
以 AddRequestHeader 为例 下面太多,贴出对应配置不进行一一演示
3.1.1 创建 application-add_request_header_route.yml,添加相关配置
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://httpbin.org/
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
filters:
- AddRequestHeader=X-Request-red, blue
3.1.2 应用该配置文件后重启测试
curl localhost:8080/get
{
"args": {},
"headers": {
"Accept": "*/*",
"Content-Length": "0",
"Forwarded": "proto=http;host=\"localhost:8080\";for=\"0:0:0:0:0:0:0:1:50005\"",
"Host": "httpbin.org",
"User-Agent": "curl/7.55.1",
"X-Amzn-Trace-Id": "Root=1-5f1aed6d-98de345042a71ef80a3b4eb8",
"X-Forwarded-Host": "localhost:8080",
"X-Request-Red": "blue"
},
"origin": "0:0:0:0:0:0:0:1, 58.33.125.63",
"url": "https://localhost:8080/get"
}
返回json的请求头中已添加 "X-Request-Red": "blue"
3.2 AddRequestParameter网关过滤工厂----添加请求参数
3.2.1添加固定请求参数
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://httpbin.org/
filters:
- AddRequestParameter=red, blue
3.2.1添加变量请求参数
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://httpbin.org/
predicates:
- Host: {segment}.myhost.org
filters:
- AddRequestParameter=foo, bar-{segment}
两种方式,下同,贴其一。
3.3 AddResponseHeader网关过滤工厂----添加响应头
filters:
- AddResponseHeader=X-Response-Red, Blue
3.4 DedupeResponseHeader网关过滤工厂----剔除重复响应头
filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
3.5 CircuitBreaker网关过滤工厂----熔断(替代之前的hystrix)+ 3.6 FallbackHeaders网关过滤工厂----请求转发
filters:
- name: CircuitBreaker
args:
name: fetchIngredients
fallbackUri: forward:/fallback
- id: ingredients-fallback
uri: http://localhost:9994
predicates:
- Path=/fallback
filters:
- name: FallbackHeaders
args:
executionExceptionTypeHeaderName: Test-Header
3.7 MapRequestHeader
网关过滤工厂----扩充请求头
filters:
- MapRequestHeader=Blue, X-Request-Red
3.8 PrefixPath
网关过滤工厂----添加前缀
filters:
- PrefixPath=/mypath
3.9 PreserveHostHeader
网关过滤工厂----检查请求头
filters:
- PreserveHostHeader
检查是否存在该请求头
3.10 RequestRateLimiter
网关过滤工厂----请求限流
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
配合redis使用做请求限流
使用的算法是令牌桶算法。
该redis-rate-limiter.replenishRate
属性是您希望用户每秒允许多少个请求,而没有任何丢弃的请求。这是令牌桶被填充的速率。
该redis-rate-limiter.burstCapacity
属性是允许用户在一秒钟内执行的最大请求数。这是令牌桶可以容纳的令牌数。将此值设置为零将阻止所有请求。
该redis-rate-limiter.requestedTokens
属性是请求花费多少令牌。这是每个请求从存储桶中提取的令牌数,默认为1
。
3.11 RedirectTo
网关过滤工厂----重定向
filters:
- RedirectTo=302, https://acme.org
3.12 RemoveRequestHeader网关过滤工厂----删除请求头
filters:
- RemoveRequestHeader=X-Request-Foo
3.13 RemoveResponseHeader
网关过滤工厂----删除响应头
filters:
- RemoveResponseHeader=X-Response-Foo
3.14 RemoveRequestParameter
网关过滤工厂----删除请求参数
filters:
- RemoveRequestParameter=red
3.15 RewritePath
网关过滤工厂----重写路径
filters:
- RewritePath=/red(?/?.*), $\{segment}
3.16 RewriteLocationResponseHeader
网关过滤工厂----重写响应头
filters:
- RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,
3.17 RewriteResponseHeader
网关过滤工厂----以正则表达式的方式重写响应头的值
filters:
- RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=***
3.18 SaveSession
网关过滤工厂----保存session
filters:
- SaveSession
3.19 SecureHeaders
网关过滤工厂----添加一系列的安全头
官方参考该博客建议,添加了一下请求头 https://blog.appcanary.com/2017/http-security-headers.html
X-Xss-Protection:1 (mode=block
)
Strict-Transport-Security (max-age=631138519
)
X-Frame-Options (DENY)
X-Content-Type-Options (nosniff)
Referrer-Policy (no-referrer)
Content-Security-Policy (default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline)'
X-Download-Options (noopen)
X-Permitted-Cross-Domain-Policies (none)
要更改默认值,请在spring.cloud.gateway.filter.secure-headers
名称空间中设置适当的属性。可以使用以下属性:
xss-protection-header
strict-transport-security
x-frame-options
x-content-type-options
referrer-policy
content-security-policy
x-download-options
x-permitted-cross-domain-policies
要禁用默认值,请spring.cloud.gateway.filter.secure-headers.disable
用逗号分隔的值设置属性。以下示例显示了如何执行此操作:
spring.cloud.gateway.filter.secure-headers.disable=x-frame-options,strict-transport-security
3.20 SetPath
网关过滤工厂----设置路径
predicates:
- Path=/red/{segment}
filters:
- SetPath=/{segment}
3.21 SetRequestHeader
网关过滤工厂----替换请求头
filters:
- SetRequestHeader=X-Request-Red, Blue
3.22 SetResponseHeader
网关过滤工厂----替换响应头
filters:
- SetResponseHeader=X-Response-Red, Blue
3.23 SetStatus
网关过滤工厂----修改状态
filters:
- SetStatus=401
3.24 StripPrefix
网关过滤工厂----阶段路径
predicates:
- Path=/name/**
filters:
- StripPrefix=2
3.25 Retry
网关过滤工厂----重试
该Retry
GatewayFilter
工厂支持以下参数:
retries
:应尝试的重试次数。
statuses
:应重试的HTTP状态代码,使用表示org.springframework.http.HttpStatus
。
methods
:应重试的HTTP方法,以表示org.springframework.http.HttpMethod
。
series
:要重试的一系列状态代码,用表示org.springframework.http.HttpStatus.Series
。
exceptions
:应重试的引发异常的列表。
backoff
:为重试配置的指数补偿。重试在的退避间隔后执行firstBackoff * (factor ^ n)
,其中n
为迭代。如果maxBackoff
已配置,则应用的最大退避限制为maxBackoff
。如果basedOnPreviousValue
为true,则使用计算退避prevBackoff * factor
。
Retry
如果启用了以下默认过滤器配置:
retries
:3次
series
:5XX系列
methods
:GET方法
exceptions
:IOException
和TimeoutException
backoff
:禁用
以下清单配置了Retry GatewayFilter
:
spring:
cloud:
gateway:
routes:
- id: retry_test
uri: http://localhost:8080/flakey
predicates:
- Host=*.retry.com
filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY
methods: GET,POST
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false
3.26 RequestSize
网关过滤工厂----设置请求数据大小
filters:
- name: RequestSize
args:
maxSize: 5000000
3.27 ModifyRequestBody网关过滤工厂----修改请求体
该过滤器只能使用java代码的方式进行配置
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")
.filters(f -> f.prefixPath("/httpbin")
.modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
(exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))
.build();
}
static class Hello {
String message;
public Hello() { }
public Hello(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
3.28 ModifyResponseBody网关过滤工厂----修改响应体
该过滤器只能使用java代码的方式进行配置
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
.filters(f -> f.prefixPath("/httpbin")
.modifyResponseBody(String.class, String.class,
(exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri)
.build();
}
3.29 default网关过滤工厂----默认的,可以应用于所有的路由
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Red, Default-Blue
- PrefixPath=/httpbin
全局过滤器
使用方式如下:
package com.example.gatewayservice.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
public class TokenFilter implements GlobalFilter, Ordered {
Logger logger = LoggerFactory.getLogger(TokenFilter.class);
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if(token == null || token.isEmpty()){
logger.warn("token is empty...");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -1;
}
}
@Bean
public TokenFilter tokenFilter(){
return new TokenFilter();
}
重启测试:
curl localhost:8080/get
2020-07-24 23:03:14.139 WARN 11940 --- [ctor-http-nio-2] c.e.gatewayservice.filter.TokenFilter : token is empty...
源码地址:https://github.com/houfanGitHub/springcloud.git