目录
- 什么是Gateway
- 为什么引入网关
- Gateway的组成
- First try
- Predicate
- Filter
- 1. AddRequestHeader
- 2. AddRequestParameter
- 3. AddResponseHeader
- 4. DedupeResponseHeader
- 5. HystrixGatewayFilter
- 6. Spring Cloud CircuitBreakerFilter
- 7. FallbackHeaders
- 8. MapRequestHeader
- 9. PrefixPath
- 10. PreserveHostHeader
- 11. RequestRateLimiter
- 12. RedirectTo
- 13. RemoveRequestHeader
- 14. RemoveResponseHeader
- 15. RemoveRequestParameter
- 16. RewritePath
- 17. RewriteLocationResponseHeader
- 18. RewriteResponseHeader
- 19. RewriteResponseHeader
- 20. SecureHeaders
- 21. SetPath
- 22. SetRequestHeader
- 23. SetResponseHeader
- 24. SetStatus
- 25. StripPrefix
- 26. Retry
- 27. RequestSize
- 28. SetRequestHost
- 29. ModifyRequestBody
- 30. ModifyResponseBody
- 31. Default-filters
- 动态路由
什么是Gateway
Gateway是微服务的API网关。它的主要作用是网关的管理,另外还提供协议适配、协议转发、安全、监控、弹性等功能。
为什么引入网关
没有网关之前:
使用方需要记住需要调用哪些服务,一旦后端的服务发生变化,需要该很多的配置。
有了网关之后:
清晰明了。
当某些应用较多时,甚至可以是gateway of gateway。
Gateway的组成
由Netty写的上游服务端和下游客户端。协议的适配和转发就是由两端的Netty来完成的。中间是许多Route配置规则,每个Route包含Predicate和Filter。
每个Route放大之后:
Predicate进行规则的匹配,匹配上了就交由对应的Filter去逐级处理,在请求到达下游服务之前和收到响应之后都可以进行流水线式处理加工信息。
Predicate:
- Path
- Host
- Date/Time
- Method
- Headers
- Query Params
- Cookies
- Read Body
- …
Pre-Filter:
- Headers
- Path
- Rate Limiting
- Cookies
- Hystrix
- Modifying Body
- …
Global-Filter:
- Netty Router
- Web Sockets
- Load Balancers
- Metrics
Post-Filter:
- Set status
- Headers
- Cookies
- …
First try
为使用Gateway只需要引入以下的依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
当然,为了加入spring-boot的监控,可引入以下jar包:
在这里插入代码片
代码示例:
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes().route(r -> r.path("/csdn").uri("https://blog.csdn.net/qq_39722475").id("my_blog")).build();
}
Predicate
通过gateway的启动日志可以看到Predicate的种类:
1. After
表示在某一时间之后,才允许访问。我们启动一个8081的服务,用于接收gateway发来的请求。单独访问打印Hello!
Gateway的after断言处理,
@Bean
public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
ZonedDateTime datetime = LocalDateTime.now().minusHours(1).atZone(ZoneId.systemDefault());
return builder.routes().route("after_route", r -> r.after(datetime).uri("http://127.0.0.1:8081/")).build();
}
使用配置文件
spring:
cloud:
gateway:
routes:
- id: after_route
uri: http://127.0.0.1:8081/
predicates:
- After=2020-05-31T21:48:45.091+08:00[Asia/Shanghai]
如果更改代码,允许一个小时后访问:
@Bean
public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
ZonedDateTime datetime = LocalDateTime.now().plusHours(1).atZone(ZoneId.systemDefault());
return builder.routes().route("after_route", r -> r.after(datetime).uri("http://127.0.0.1:8081/")).build();
}
2. Before
表示在某一时间之前,才允许访问。java代码:
@Bean
public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
ZonedDateTime datetime = LocalDateTime.now().plusHours(1).atZone(ZoneId.systemDefault());
return builder.routes().route("before_route", r -> r.before(datetime).uri("http://127.0.0.1:8081/")).build();
}
配置文件方式:
spring:
cloud:
gateway:
routes:
- id: before_route
uri: http://127.0.0.1:8081/
predicates:
- Before=2020-05-31T21:48:45.091+08:00[Asia/Shanghai]
3. Between
表示在某一时间之前,才允许访问。java代码:
@Bean
public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
ZonedDateTime datetime1 = LocalDateTime.now().minusHours(1).atZone(ZoneId.systemDefault());
ZonedDateTime datetime2 = LocalDateTime.now().plusHours(1).atZone(ZoneId.systemDefault());
return builder.routes().route("between_route", r -> r.between(datetime1, datetime2).uri("http://127.0.0.1:8081/")).build();
}
配置文件方式:
spring:
cloud:
gateway:
routes:
- id: between_route
uri: http://127.0.0.1:8081/
predicates:
- Between=2020-05-31T20:48:45.091+08:00[Asia/Shanghai], 2020-05-31T22:48:45.091+08:00[Asia/Shanghai]
4. Cookie
@Bean
public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
return builder.routes().route("cookie_predicate_route", r -> r.cookie("name", "xiaoming").uri("http://127.0.0.1:8081/")).build();
}
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://127.0.0.1:8081/
predicates:
- Cookie=name, xiaoming
访问的截图:
5. Header
表示请求的Header有相应的字段,满足给定的正则表达式,才允许访问。java代码:
@Bean
public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
return builder.routes().route("header_route", r -> r.header("X-Request-Id", "\\d+").uri("http://127.0.0.1:8081/")).build();
}
配置文件方式:
spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://127.0.0.1:8081/
predicates:
- Header=X-Request-Id, \d+
访问的截图:
6. Host
参数是一系列的域名。可以使用Ant式的匹配风格,如**.sunq.com匹配www.sunq.com、vip.sunq.com等。也可以是{sub}.sunq.com,这个sub取自ServerWebExchange.getAttributes()的key是ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE时的value。java代码:
@Bean
public RouteLocator customRouteLocator4(RouteLocatorBuilder builder) {
ZonedDateTime datetime = LocalDateTime.now().plusHours(1).atZone(ZoneId.systemDefault());
return builder.routes().route("host_predicate_route", r -> r.host("sunq.com:8080").uri("http://127.0.0.1:8081/")).build();
}
配置文件方式:
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://127.0.0.1:8081/
predicates:
- Host=**.sunq.com, **.buns.com
更改本地的host:
结果示例:
此时再用localhost访问不到了:
7. Method
请求的方法与之匹配,可支持配置多个,以逗号隔开。java代码:
@Bean
public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
return builder.routes().route("method_route", r -> r.method(HttpMethod.GET).uri("http://127.0.0.1:8081/")).build();
}
配置文件方式:
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://127.0.0.1:8081/
predicates:
- Method=GET
8. Path
请求的路径与之匹配,可支持配置多个,以逗号隔开。java代码:
@Bean
public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
return builder.routes().route("method_route", r -> r.method(HttpMethod.GET).uri("http://127.0.0.1:8081/")).build();
}
配置文件方式:
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://127.0.0.1:8081/
predicates:
- Path=/test/{segment}
Filter
1. AddRequestHeader
给请求加上头信息:
@Bean
public RouteLocator customRouteLocator3(RouteLocatorBuilder builder) {
return builder.routes().route("head_filter", r -> r.path("/test/head").filters(f -> f.addRequestHeader("X-Request-Id", "hello!")).uri("http://localhost:8081/test/head")).build();
}
配置文件方式:
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://127.0.0.1:8081/
filters:
- AddRequestHeader=X-Request-red, hello!
8081的代码:
@GetMapping("/test/head")
public String testGatewayHead(HttpServletRequest request, HttpServletResponse response){
String head=request.getHeader("X-Request-Id");
return "return head info:"+head;
}
由于到达Filter已经知晓了uri,因此可以在配置上使用路径或域名的字段:
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- AddRequestHeader=X-Request-Red, Blue-{segment}
以上的配置可以访问https://example.org/red/1,就会在头加上X-Request-Red=Blue-1。
2. AddRequestParameter
添加请求参数。
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
filters:
- AddRequestParameter=red, blue
3. AddResponseHeader
添加回执头信息。
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
filters:
- AddResponseHeader=X-Response-Red, Blue
4. DedupeResponseHeader
对回执头信息去重。比如,我们在Gateway以及微服务上都设置了CORS(解决跨域)header,如果不做任何配置,请求 -> 网关 -> 微服务,获得的响应就是这样的:
Access-Control-Allow-Credentials: true, true
Access-Control-Allow-Origin: https://musk.mars, https://musk.mars
也就是Header重复了。要想把这两个Header去重,只需设置成如下即可1。
spring:
cloud:
gateway:
routes:
- id: dedupe_response_header_route
uri: https://example.org
filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_FIRST
5. HystrixGatewayFilter
Hystrix在网关引入断路器,防止服务调用的级联失败。需要引入spring-cloud-starter-netflix-hystrix的依赖jar。配置示例:
spring:
cloud:
gateway:
routes:
- id: hystrix_route
uri: https://example.org
filters:
- Hystrix=myCommandName
因为官方的推荐是Resilience4J,对此不过多说明(自身也不熟)。
6. Spring Cloud CircuitBreakerFilter
Spring Cloud CircuitBreaker是对Hystirx和Resilience4J的包装。配置的方式:
spring:
cloud:
gateway:
routes:
- id: circuitbreaker_route
uri: https://example.org
filters:
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/inCaseOfFailureUseThis
7. FallbackHeaders
当发生熔断时,此过滤器允许我们把异常信息放到转发的请求头信息中。配置的方式:
spring:
cloud:
gateway:
routes:
- id: ingredients
uri: lb://ingredients
predicates:
- Path=//ingredients/**
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
8. MapRequestHeader
把请求的头信息赋值给回执的头信息:
spring:
cloud:
gateway:
routes:
- id: map_request_header_route
uri: https://example.org
filters:
- MapRequestHeader=Blue, X-Request-Red
这会把Blue的value赋值给X-Request-Red。
9. PrefixPath
对所有的请求路径添加前缀:
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- PrefixPath=/mypath
访问/hello的请求被发送到/mypath/hello。
10. PreserveHostHeader
表示在Spring Cloud Gateway转发请求的时候,保持客户端的Host信息不变,会将这些信息携带到后面的服务实例上面。要不然后面的实例服务接收到的Host信息就是新的Http Client的Host信息,而不是真实的客户端的Host信息了。配置如下所示,这个配置没有参数:
spring:
cloud:
gateway:
routes:
- id: preserve_host_route
uri: https://example.org
filters:
- PreserveHostHeader
11. RequestRateLimiter
限流使用RateLimiter的实现类来决定当前的请求是否处理,不处理就返回429(太多请求)。目前的一个实现是ReidsRateLimiter,需要引入spring-boot-starter-data-redis-reactive的依赖。此限流的策略是令牌桶,有3个对应的参数:
- redis-rate-limiter.replenishRate:令牌桶允许的填充速率。
- redis-rate-limiter.burstCapacity:令牌桶的最大容量。
- redis-rate-limiter.requestedTokens:每个请求消耗的令牌数目。
另外有个可选参数key-resolver,对应的接口是KeyResolver,现在默认的实现是PrincipalNameKeyResolver。用于从请求抽取key,如果找不到会直接拒绝请求。可以调整以下配置spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key (true or false) 和 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code。
配置如下所示:
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1000
redis-rate-limiter.burstCapacity: 2000
redis-rate-limiter.requestedTokens: 1
12. RedirectTo
重定向,配置包含重定向的返回码和地址:
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- RedirectTo=302, https://acme.org
13. RemoveRequestHeader
去掉某个请求头信息:
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: https://example.org
filters:
- RemoveRequestHeader=X-Request-Foo
14. RemoveResponseHeader
去掉某个回执头信息:
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: https://example.org
filters:
- RemoveResponseHeader=X-Request-Foo
15. RemoveRequestParameter
去掉某个请求参数信息:
spring:
cloud:
gateway:
routes:
- id: removerequestparameter_route
uri: https://example.org
filters:
- RemoveRequestParameter=red
16. RewritePath
改写路径:
@Bean
public RouteLocator customRouteLocator6(RouteLocatorBuilder builder) {
return builder.routes().route("rewrite_filter", r -> r.path("/where/**").filters(f -> f.rewritePath("/where/*", "/test/")).uri("http://localhost:8081/test/head")).build();
}
或使用配置:
spring:
cloud:
gateway:
routes:
- id: rewrite_filter
uri: http://localhost:8081
predicates:
- Path=/test/**
filters:
- RewritePath=/where(?<segment>/?.*), /test(?<segment>/?.*)
路径中的where改为了test。
17. RewriteLocationResponseHeader
改写回执的版本信息,改写回执的host:port等。
spring:
cloud:
gateway:
routes:
- id: rewritelocationresponseheader_route
uri: http://example.org
filters:
- RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,
18. RewriteResponseHeader
改写回执的头信息。
spring:
cloud:
gateway:
routes:
- id: rewriteresponseheader_route
uri: https://example.org
filters:
- RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=***
/42?user=ford&password=omg!what&flag=true被改写为/42?user=ford&password=***&flag=true。
19. RewriteResponseHeader
强制WebSession::save操作。
spring:
cloud:
gateway:
routes:
- id: save_session
uri: https://example.org
predicates:
- Path=/foo/**
filters:
- SaveSession
20. SecureHeaders
添加安全相关头信息:
- 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)
21. SetPath
设置请求路径,与RewritePath类似。
spring:
cloud:
gateway:
routes:
- id: setpath_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- SetPath=/{segment}
如/red/blue的请求被转发到/blue。
22. SetRequestHeader
设置请求头信息。
spring:
cloud:
gateway:
routes:
- id: setrequestheader_route
uri: https://example.org
filters:
- SetRequestHeader=X-Request-Red, Blue
23. SetResponseHeader
设置回执头信息。
spring:
cloud:
gateway:
routes:
- id: setrequestheader_route
uri: https://example.org
filters:
- SetResponseHeader=X-Request-Red, Blue
24. SetStatus
设置回执状态码。
spring:
cloud:
gateway:
routes:
- id: setstatusint_route
uri: https://example.org
filters:
- SetStatus=401
25. StripPrefix
跳过指定路径。
spring:
cloud:
gateway:
routes:
- id: nameRoot
uri: https://nameservice
predicates:
- Path=/name/**
filters:
- StripPrefix=2
请求/name/blue/red会转发到/red。
26. Retry
重试。
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
27. RequestSize
请求大小。
spring:
cloud:
gateway:
routes:
- id: request_size_route
uri: http://localhost:8080/upload
predicates:
- Path=/upload
filters:
- name: RequestSize
args:
maxSize: 5000000
超过5M的请求会返回413错误。
28. SetRequestHost
设置请求的host。
spring:
cloud:
gateway:
routes:
- id: set_request_host_header_route
uri: http://localhost:8080/headers
predicates:
- Path=/headers
filters:
- name: SetRequestHost
args:
host: example.org
29. ModifyRequestBody
更改请求体。
@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;
}
}
30. ModifyResponseBody
更改回执体。
@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();
}
31. Default-filters
对所有请求添加过滤器。
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Red, Default-Blue
- PrefixPath=/httpbin
动态路由
通过不停机,更新路由规则。
public class GatewayRouteDefinition {
// 路由的id
private String id;
// 路由断言集合配置
private List<GatewayPredicateDefinition> predicates = new ArrayList<>();
// 路由过滤器集合配置
private List<GatewayFilterDefinition> filters = new ArrayList<>();
// 珞由规则转发的目标uri
private String uri;
// 路由执行的顺序
private int order= O;
// 此处省略get和set方法
}
public class GatewayFilterDefinition {
//Filter Name
private String name;
// 对应的路由规则
private Map<String, String> args =口ew LinkedHashMap<>();
// 此处省略Get和Set方法
}
public class GatewayPredicateDefinition {
// 断言对应的Name
private String name;
// 配置的断言规则
private Map<String, String> args = new LinkedHashMap<> ();
// 此处省略Get和Set方法
}
@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
private ApplicationEventPublisher publisher;
// 增加路由
public String add(RouteDefinitiondefinition definition) {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
}
// 更新路由
public String update(RouteDefinition definition) {
try {
this.routeDefinitionWriter.delete(Mono.just(definition.getld()));
} catch (Exception e) {
return "update fail,not find route routeld:" + definition.getid();
}
try {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
} catch (Exception e) {
return "update route fail";
}
}
// 删除路由
public Mono<ResponseEntity<Object> delete(Stringid) {
return this.routeDefinitionWriter.delete(Mono.just(id))
.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
.onErrorResume(t -> t instanceof NotFoundException, t -> Mono.
just(ResponseEntity.notFound().build()));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
}
@RestController
@RequestMapping("/route")
public class RouteController {
@Autowired
private DynamicRouteServiceimpl dynamicRouteService;
// 增加路由
@PostMapping("/add")
public String add(@RequestBody GatewayRouteDefinition gwdefinition) {
try {
RouteDefinition definition = assembleRouteDefinition(gwdefinition);
return this.dynamicRouteService.add(definition);
} catch (Exception e) {
e.printStackTrace();
}
return "succss";
}
// 删除路由
@DeleteMapping("/routes/{id}")
public Mono<ResponseEntity<Object> delete(@PathVariable String id) {
return this.dynamicRouteService.delete(id);
}
// 更新路由
@PostMapping("/update")
public String update(@RequestBody GatewayRouteDefinition gwdefinition) {
RouteDefinition definition = assembleRouteDefinition(gwdefinition);
return this.dynamicRouteService.update(definition);
}
}
参考https://www.imooc.com/article/290816 ↩︎