GateWay
路由规则
即满足某种规则之后即向指定路径路由,否则应为404。
#参数合并写法
spring:
cloud:
gateway:
routes:
- id: after_route #路由规则id,唯一
uri: https://example.org #路由目标路径
predicates: #断言,判断是否路由
- Cookie=mycookie,mycookievalue #当cookie中包含该键值对的时候才路由
#参数展开写法
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- name: Cookie
args:
name: mycookie
regexp: mycookievalue
路由规则还有时间,请求头,域名,请求方式等,详情参见官方文档
动态路由(服务名称转发)
spring:
cloud:
gateway:
routes:
- id: after_route #路由规则id,唯一
uri: lb://product-service #lb://根据服务名称从注册中心获取服务请求地址
predicates: #断言,判断是否路由
- Cookie=mycookie,mycookievalue #当cookie中包含该键值对的时候才路由
使用spring约定配置完成自动路由
spring:
cloud:
gateway:
discovery:
localtor: #路由规则id,唯一
enable: true #与服务发现组件结合,访问路径之前应该添加上接口所在服务的名称,比如:product-service/user/get
lower-case-service-id:true #是否将服务名称转为小写
predicates: #断言,判断是否路由
- Cookie=mycookie,mycookievalue #当cookie中包含该键值对的时候才路由
网关过滤器
路由过滤器允许以某种方式修改传入HTTP请求或传出HTTP响应。路由筛选器的作用域为特定路由。SpringCloudGateway包括许多内置的GatewayFilter工厂。
内置过滤器使用
配置方式:
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- AddRequestHeader=X-Request-Red, Blue-{segment} #为请求添加请求头
GateWay内置过滤器工厂速查
更多过滤器或者路由规则的使用样例可参见官方文档或者此处
自定义网关过滤器
当gateway提供的网关过滤器和全局过滤器不满足我们的使用时可以自定义过滤器
/**
* 自定义网关过滤器
* 需要实现GatewayFilter, Ordered两个接口
*/
@Component
public class MyGatewayFilter implements GatewayFilter, Ordered {
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange);//返回此值则通过此过滤器
}
/**
* 过滤器执行顺序,order越小,优先级越高
*
* @return
*/
@Override public int getOrder() {
return 0;
}
}
定义完网关过滤器之后需要将其注册到路由规则中
/**
* 配置Gateway路由规则和过滤器的配置类
*/
@Configuration
public class GatewayRoutesConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes().route(r -> r
//断言(判断条件)
.path("/product/**")
//目标URI,路由到微服务的地址
.uri("lb://product-service")
// ====注册自定义网关过滤器====
.filters(new MyGatewayFilter())
//路由ID(唯一)
.id("product-service"))
.build();
}
}
自定义全局过滤器
全局过滤器会在所有路由中生效 ,一般可用于统一鉴权。
/**
* 自定义全局过滤器
* 需要实现GlobalFilter, Ordered两个接口
*/
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String paramValue = exchange.getRequest()//获取请求对象
.getQueryParams()
.getFirst("paramName");//获取请求参数
return chain.filter(exchange);//通过过滤器
}
/**
* 过滤器执行顺序,order越小,优先级越高
*
* @return
*/
@Override public int getOrder() {
return 0;
}
}
//直接在配置类使用函数表达式的方式
@Bean
public GlobalFilter customGlobalFilter() {
return (exchange, chain) -> exchange.getPrincipal()
.map(Principal::getName)
.defaultIfEmpty("Default User")
.map(userName -> {
//adds header to proxied request
exchange.getRequest().mutate().header("CUSTOM-REQUEST-HEADER", userName).build();
return exchange;
})
.flatMap(chain::filter);
}
@Bean
public GlobalFilter customGlobalPostFilter() {
return (exchange, chain) -> chain.filter(exchange)
.then(Mono.just(exchange))
.map(serverWebExchange -> {
//adds header to response
serverWebExchange.getResponse().getHeaders().set("CUSTOM-RESPONSE-HEADER",
HttpStatus.OK.equals(serverWebExchange.getResponse().getStatusCode()) ? "It worked": "It did not work");
return serverWebExchange;
})
.then();
}
网关限流
Spring Cloud Gateway官方提供了RequestRateLimiterGatewayFilterFactor过滤器工厂,使用Redis和Lua脚本
实现了令牌桶的方式。
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
限流配置
#路由id
spring:
redis:
timeout: 10000
host: localhost
port: 6379
lettuce:
pool:
max-active: 1024 #最大连接数
max-wait: 10000 #最大连接阻塞等待时间
max-idle: 200 #最大空闲连接
min-idle: 5 #最小空闲连接
cloud:
gateway:
routes:
- id: provider-service
#动态路由
uri: lb://service-provider
predicates:
- Path=/**
filters:
- name: RequestRateLimiter #限流过滤器
args:
redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充速率
redis-rate-limiter.burstCapacity: 2 #令牌桶总容量
key-resolver: "#{@pathKeyResolver}" #使用SpEL表达式按名称引用限流策略bean,根据url、param或者ip限流
配置Gateway限流策略配置类
/**
* Gateway限流策略配置类
* 注:容器中同时只能有一种限流策略
*/
@Configuration
public class KeyResolverConfiguration {
/**
* 根据url进行限流,单个url限流超过限制则限流
* @return
*/
@Bean
public KeyResolver pathKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
}
/**
* 根据参数进行限流
* @return
*/
// @Bean
public KeyResolver parameterKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}
/**
* 根据ip进行限流,单个ip流量达到限制后限流
* @return
*/
// @Bean
public KeyResolver ipKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
}
实现熔断降级
server.port: 8082
spring:
application:
name: gateway
redis:
host: localhost
port: 6379
password: 123456
cloud:
gateway:
routes:
- id: rateLimit_route
uri: http://localhost:8000
order: 0
predicates:
- Path=/test/**
filters:
- StripPrefix=1
- name: Hystrix
args:
name: fallbackCmdA
fallbackUri: forward:/fallbackA
hystrix.command.fallbackCmdA.execution.isolation.thread.timeoutInMilliseconds: 5000
这里的配置,使用了两个过滤器:
(1)过滤器StripPrefix,作用是去掉请求路径的最前面n个部分截取掉。
StripPrefix=1就代表截取路径的个数为1,比如前端过来请求/test/good/1/view,匹配成功后,路由到后端的请求路径就会变成http://localhost:8888/good/1/view。
(2)过滤器Hystrix,作用是通过Hystrix进行熔断降级
当上游的请求,进入了Hystrix熔断降级机制时,就会调用fallbackUri配置的降级地址。需要注意的是,还需要单独设置Hystrix的commandKey的超时时间
fallbackUri配置的降级地址的代码如下:
@RestController
public class FallbackController {
@GetMapping("/fallbackA")
public Response fallbackA() {
Response response = new Response();
response.setCode("100");
response.setMessage("服务暂时不可用");
return response;
}
}
动态配置和使用路由规则
Gateway的路由规则映射的实例是RouteDefinitionLocator,一个RouteDefinitionLocator实例代表一个路由规则.如果我们使用默认的配置方式,配置文件的内容可以自动映射到这个实例中.
// 下面两种路由规则的配置方式是等效的
spring:
cloud:
gateway:
routes:
- id: setstatus_route
uri: https://example.org
filters:
- name: SetStatus
args:
status: 401
- id: setstatusshortcut_route
uri: https://example.org
filters:
- SetStatus=401
要将 Spring Cloud Gateway 的路由规则保存在数据库或 Redis 中,可以使用 Spring Cloud Gateway 提供的 RouteDefinitionRepository 接口。这个接口提供了从外部存储中获取、保存和更新路由规则的方法,可以通过实现这个接口来实现将路由规则保存在数据库或 Redis 中的功能。
具体实现步骤如下:
- 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.实现 RouteDefinitionRepository 接口
@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
private static final String KEY = "gateway_routes";
private final RedisTemplate<String, Object> redisTemplate;
public RedisRouteDefinitionRepository(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
List<RouteDefinition> routeDefinitions = redisTemplate.opsForHash()
.values(KEY).stream()
.map(o -> JSON.parseObject(o.toString(), RouteDefinition.class))
.collect(Collectors.toList());
return Flux.fromIterable(routeDefinitions);
}
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap(r -> {
redisTemplate.opsForHash().put(KEY, r.getId(), JSON.toJSONString(r));
return Mono.empty();
});
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(id -> {
redisTemplate.opsForHash().delete(KEY, id);
return Mono.empty();
});
}
}
3.在配置文件中开启配置
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
4.在启动类中注入 RedisRouteDefinitionRepository 实例
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
@Bean
public RouteDefinitionRepository routeDefinitionRepository(RedisTemplate<String, Object> redisTemplate) {
return new RedisRouteDefinitionRepository(redisTemplate);
}
}
通过上述步骤,就可以实现将 Spring Cloud Gateway 的路由规则保存在 Redis 中,并且能够在更新路由规则之后,不需要重启 Gateway 应用程序即可动态加载最新的路由规则。
开启路由规则监控
要启用RouteDefinition指标,需要将spring-boot-starter-actuator添加为项目依赖项。然后,默认情况下,只要将属性spring.cloud.gateway.metrics.enabled设置为true,指标就会可用。将添加一个名为spring.cloud.gateway.routes.count的计量指标,其值为RouteDefinitions的数量。该指标将在/actuator/metrics/spring.cloud.gateway.routes.count路径下可用。
这样可以方便地监控和了解当前定义的路由数量,以便在需要时进行调整和管理。可以通过访问上述路径来获取该指标的值,并结合其他监控工具进行监控和分析。
增加路由元数据参数
可以通过使用元数据来为每个路由配置附加参数,具体如下所示:
spring:
cloud:
gateway:
routes:
- id: route_with_metadata
uri: https://example.org
metadata:
optionName: "OptionValue"
compositeObject:
name: "value"
iAmNumber: 1
然后从exchange中取用自定义元参数
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
// get all metadata properties
route.getMetadata();
// get a single metadata property
route.getMetadata(someKey);
配置路由超时时间
HTTP超时(响应超时和连接超时)可以为所有路由配置默认值,并且可以针对每个具体的路由进行覆盖配置。
要配置所有路由的默认超时时间,您可以在应用程序的配置文件中添加以下属性:
连接超时时间必须以毫秒为单位进行指定。
响应超时时间必须以java.time.Duration格式进行指定。
spring:
cloud:
gateway:
httpclient:
response-timeout: <default-response-timeout>
connect-timeout: <default-connect-timeout>
要为特定的路由进行超时配置,您可以在路由定义中使用元数据来覆盖默认超时时间。例如:
连接超时时间必须以毫秒为单位进行指定。
响应超时时间必须以毫秒为单位进行指定。
spring:
cloud:
gateway:
routes:
- id: myRoute
uri: http://example.org
metadata:
response-timeout: <route-response-timeout>
connect-timeout: <route-connect-timeout>
Reactor Netty访问日志
注意这是Java 系统配置,而非SpringBoot配置
-Dreactor.netty.http.server.accessLogEnabled=true
可以配置日志系统以创建一个单独的访问日志文件。以下示例创建了一个 Logback 配置:
<appender name="accessLog" class="ch.qos.logback.core.FileAppender">
<file>access_log.log</file>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="accessLog" />
</appender>
<logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
<appender-ref ref="async"/>
</logger>
在上面的配置中,我们创建了一个名为 ACCESS_LOG_FILE 的文件 appender,将日志写入 /path/to/access.log 文件中。然后,我们定义了一个名为 reactor.netty.http.server.AccessLog 的 logger,将日志级别设置为 INFO,并将 appender ACCESS_LOG_FILE 关联到该 logger。
你需要将上述配置保存为 logback.xml 文件,并确保它位于类路径下。启动应用程序时,Logback 将加载该配置,并将访问日志写入指定的文件中。
Gateway 配置跨域
全局跨域配置
spring.cloud.gateway.globalcors.corsConfigurations.[/*/*/threads/**].allowedOriginPatterns=*
spring.cloud.gateway.globalcors.corsConfigurations.[/*/*/threads/**].allowedHeaders=*
spring.cloud.gateway.globalcors.corsConfigurations.[/*/*/threads/**].allowedMethods=GET,POST,PUT,DELETE
spring.cloud.gateway.globalcors.corsConfigurations.[/*/*/threads/**].allowCredentials=true
spring.cloud.gateway.globalcors.corsConfigurations.[/*/*/threads/**].maxAge=1728000
单个路由跨域配置
在Spring Cloud Gateway中,“route”配置允许直接将CORS配置应用于特定的路由,使用的是名为“cors”的元数据键。与全局配置类似,CORS配置的属性属于Spring Framework的
spring:
cloud:
gateway:
routes:
- id: cors_route
uri: https://example.org
predicates:
- Path=/service/**
metadata:
cors
allowedOrigins: '*'
allowedMethods:
- GET
- POST
allowedHeaders: '*'
maxAge: 30
Actuator API
/gateway端点允许您监视和与Spring Cloud Gateway应用程序进行交互。为了远程访问,该端点必须在应用程序属性中启用并通过HTTP或JMX进行公开。以下示例显示了如何进行配置:
management.endpoint.gateway.enabled=true # default value
management.endpoints.web.exposure.include=gateway
一旦启用了/gateway端点,您可以通过发送HTTP请求或使用JMX工具与其进行交互。该端点提供了一组RESTful API,用于获取有关Gateway的信息,例如已定义的路由、过滤器链、请求计数等。您可以使用这些信息来监视和诊断您的Gateway应用程序。
请注意,对/gateway端点的访问通常需要适当的权限和身份验证,以确保安全性。根据您的需求和环境,您可以配置适当的访问控制和安全设置。
获取路由规则
GET /actuator/gateway/routes:
示例结果:
[{
"route_id": "first_route",
"route_object": {
"predicate": "org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$432/1736826640@1e9d7e7d",
"filters": [
"OrderedGatewayFilter{delegate=org.springframework.cloud.gateway.filter.factory.PreserveHostHeaderGatewayFilterFactory$$Lambda$436/674480275@6631ef72, order=0}"
]
},
"order": 0
},
{
"route_id": "second_route",
"route_object": {
"predicate": "org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$432/1736826640@cd8d298",
"filters": []
},
"order": 0
}]
获取过滤器
GET /actuator/gateway/globalfilters
{
"org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter@77856cc5": 10100,
"org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@4f6fd101": 10000,
"org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@32d22650": -1,
"org.springframework.cloud.gateway.filter.ForwardRoutingFilter@106459d9": 2147483647,
"org.springframework.cloud.gateway.filter.NettyRoutingFilter@1fbd5e0": 2147483647,
"org.springframework.cloud.gateway.filter.ForwardPathFilter@33a71d23": 0,
"org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@135064ea": 2147483637,
"org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@23c05889": 2147483646
}
GET /actuator/gateway/routefilters
{
"[AddRequestHeaderGatewayFilterFactory@570ed9c configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[SecureHeadersGatewayFilterFactory@fceab5d configClass = Object]": null,
"[SaveSessionGatewayFilterFactory@4449b273 configClass = Object]": null
}
清空路由规则缓存
POST /actuator/gateway/refresh
这个请求只会返回200,没有响应体
如果需要指定删除带有某个元参数的路由需要使用:
/actuator/gateway/refresh?metadata=group:group-1
这将仅删除group元数据为group-1的路由
获取单个路由规则
GET /actuator/gateway/routes/{id}
(比如/actuator/gateway/routes/first_route)
创建路由规则和删除路由规则
增加单个路由规则:
POST /gateway/routes/{id_route_to_create}(需要在请求体中指定路由规则详情)
示例curl
curl --location 'http://a1-hsb.easemob.com/management/gateway/routes/im-roster' \
--header 'Content-Type: application/json' \
--data '{
"id": "im-roster",
"predicates": [
{
"name": "Path",
"args": {
"rosters": "/{org}/{app}/users/{username}/rosters/**",
"internal-rosters": "/internal/rest/{org}/{app}/users/{username}/rosters/**",
"contacts": "/{org}/{app}/users/{username}/contacts/**",
"internal-contacts": "/internal/rest/{org}/{app}/users/{username}/contacts/**"
}
},
{
"name": "Method",
"args": {
"http-method": "POST",
"http-method-put": "PUT",
"http-method-delete": "DELETE"
}
}
],
"filters": [
{
"name": "Auth",
"args": {
"isPermitted": "true"
}
},
{
"name": "Retry",
"args": {
"retries": "1",
"methods": "GET,POST,PUT,DELETE",
"series": "",
"statuses": "BAD_GATEWAY"
}
}
],
"uri": "lb://im-roster-doublewrite",
"metadata": {
"connect-timeout": 2000,
"response-timeout": 30000
},
"order": 20
}'
删除单个路由规则:
DELETE /gateway/routes/{id_route_to_delete}
创建多个路由规则
要在单个请求中创建多个路由定义,请向/gateway/routes发出POST请求,并提供一个JSON请求体,其中指定了路由的字段,包括路由的ID(参见检索有关特定路由的信息)。
如果在创建路由的过程中出现任何错误,所有路由定义将被丢弃。
endpoints汇总
端点 | 描述 |
---|---|
/actuator/gateway/routes | 显示配置在网关中的路由。 |
/actuator/gateway/filters | 列出应用于路由的过滤器。 |
/actuator/gateway/health | 显示网关的健康状态。 |
/actuator/gateway/metrics | 提供网关的指标信息。 |
/actuator/gateway/trace | 显示网关的最近跟踪信息。 |
/actuator/gateway/info | 显示网关的常规信息。 |
/actuator/gateway/env | 显示网关的环境属性。 |
/actuator/gateway/loggers | 列出网关的日志记录级别。 |
/actuator/gateway/heapdump | 生成并下载网关的堆转储文件。 |
/actuator/gateway/threaddump | 生成网关的线程转储信息。 |
/actuator/gateway/auditevents | 列出网关的审计事件。 |
/actuator/gateway/mappings | 显示网关的请求映射信息。 |
/actuator/gateway/scheduledtasks | 列出网关的定时任务。 |
/actuator/gateway/httptrace | 提供网关的HTTP请求/响应跟踪信息。 |
/actuator/gateway/caches | 列出网关的缓存统计信息。 |
/actuator/gateway/logfile | 检索网关的日志文件。 |
/actuator/gateway/threaddump | 生成网关的线程转储信息。 |
/actuator/gateway/shutdown | 启动网关的优雅关闭。 |
Gateway日志级别
配置日志级别
以下记录器(Logger)在DEBUG和TRACE级别下可能包含有价值的故障排除信息:
- org.springframework.cloud.gateway
- org.springframework.http.server.reactive
- org.springframework.web.reactive
- org.springframework.boot.autoconfigure.web
- reactor.netty
- redisratelimiter
通过将这些记录器的日志级别设置为DEBUG或TRACE,您可以获得更详细的日志信息,以便在故障排除过程中进行分析和调试。请注意,在生产环境中,建议仅将关键记录器设置为DEBUG级别,以避免日志量过大。
比如将redisratelimiter设置为DEBUG级别,需要配置:
logging.level.redisratelimiter=DEBUG
抓包功能
Reactor Netty HttpClient和HttpServer可以启用抓包功能。当结合将reactor.netty日志级别设置为DEBUG或TRACE时,它可以记录通过网络发送和接收的信息,例如头部和主体。要启用抓包功能,分别设置spring.cloud.gateway.httpserver.wiretap=true或spring.cloud.gateway.httpclient.wiretap=true来针对HttpServer和HttpClient。
开发指南
命名规范
为自定义过滤器和引用进行命名
自定义过滤器的类名应以GatewayFilterFactory结尾。
例如,要在配置文件中引用名为Something的过滤器,过滤器类的名称必须为SomethingGatewayFilterFactory。
自定义路由断言工厂
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
public MyRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
// 从Config对象获取配置
return exchange -> {
//获取请求
ServerHttpRequest request = exchange.getRequest();
//从请求中获取信息,看看它是否匹配配置。
return matches(config, request);
};
}
public static class Config {
//将断言的配置属性放在此处
}
}
自定义过滤器工厂
前置过滤器:
@Component
public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory<PreGatewayFilterFactory.Config> {
public PreGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
// 从Config对象获取配置
return (exchange, chain) -> {
// 构建前置过滤器,需要在调用chain.filter之前处理请求
ServerHttpRequest request = exchange.getRequest().mutate().build();
//接下来可以对这个请求做预处理,例如添加请求头,修改请求路径等,做完所需修改后,调用chain.filter方法继续执行过滤器链
return chain.filter(exchange.mutate().request(request).build());
};
}
public static class Config {
//Put the configuration properties for your filter here
}
}
后置过滤器
@Component
public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory<PostGatewayFilterFactory.Config> {
public PostGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
// grab configuration from Config object
return (exchange, chain) -> {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
// 修改响应内容
}));
};
}
public static class Config {
//Put the configuration properties for your filter here
}
}