文章目录
实战
环境
Nacos
Nacos Version
:2.0.3
Sentinel
Sentinel Version
:1.8.2
Demo Application:
System
: macOS 10.14.6
JDK
: 1.8.0_251
Spring Boot
:2.2.13.RELEASE
Spring Cloud
:Hoxton.SR12
Spring Cloud Alibaba
: 2.2.5.RELEASE
核心概念
Route: 网关的基础组成部分。包含唯一标示id
,路由目标uri
,一系列断言
及过滤器
集合。当所有断言集合为true时会匹配到该路由。
Predicate: 是Java 8 函数式断言。输入参数为ServerWebExchange
,通过该参数匹配http请求参数。
Filter: 特定工厂类创建的一系列GatewayFilter
实例。在发送到下游服务之前对请求或响应进行裁剪修改。
基础使用
配置路由
pom添加gateway依赖
<!-- spring cloud gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
注意: Gateway其实是Spring Cloud下的生态组件。
properties添加路由配置
#spring.cloud.gateway.discovery.locator.enabled=true
## routes路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
## 当前路由的标识, 要求唯一
spring.cloud.gateway.routes[0].id=good-service
## 请求要转发到的地址
spring.cloud.gateway.routes[0].uri=http://localhost:10012
## 路由的优先级,数字越小级别越高
spring.cloud.gateway.routes[0].order=1
## predicates: 断言(就是路由转发要满足的条件)
## 当请求路径满足Path指定的规则时,才进行路由转发
spring.cloud.gateway.routes[0].predicates[0]=Path=/good-apig/**
## 转发之前,删除第一层路径 即http://localhost:10016/good-apig/good/1 => http://localhost:10012/good/1
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1
以上代表请求http://localhost:10016/good-apig/good/1
时,若满足断言Path=/good-apig/**
,网关会裁剪第一级路径/good-apig
将请求代理到http://localhost:10012/good/1
结合Nacos
将路由配置放Nacos,结合Nacos服务发现及Ribbon的负责均衡进行服务调用。将配置从本地迁移到Nacos配置中心,可以动态配置路由信息。
Bootstrap添加Nacos配置
# nacos discovery
spring.cloud.nacos.discovery.server-addr=ip:port
# nacos config
spring.cloud.nacos.config.server-addr=ip:port
修改路由配置
## 结合nacos及ribbon负载均衡
spring.cloud.gateway.routes[0].uri=lb://good-service
自动配置微服务
让gateway可以发现nacos中的微服务,并做通用路由配置。
添加配置
spring.cloud.gateway.discovery.locator.enabled=true
默认访问路径
http://localhost:10016/good-service/good/1
结合Sentinel
Gateway整合Sentinel进行网关限流。限流维度有如下两种:
Route ID
:网关中定义的路由id,以路由维度进行限流,即该路由下请求都进行流控。API 分组
:自定义Api分组,可以自定义多个匹配路径同属一个分组,对分组下的所有匹配路径都进行流控。
添加依赖
<!--sentinel integration gateway-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!--sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
添加配置
# sentinel config
spring.cloud.sentinel.transport.dashboard=localhost:10000
spring.cloud.sentinel.web-context-unify=false
Route ID限流
Sentinel配置流控规则
Burst size
:突发请求额外允许的请求数⽬。在快速失败模式下,当请求达到阈值,若该值为0立即失败。若该值为5,则达到阈值继续处理5个请求后才开始失败。
API 分组
定义分组
分组usergood下包含两个匹配路径/user-apig/**
和/good-apig/**
。
对分组usergood设置限流规则
验证规则是否生效
配置断言
Spring Cloud Gateway 将路由匹配为 Spring WebFlux HandlerMapping 基础结构的一部分。 Spring Cloud Gateway 包含许多内置的路由断言工厂。这些断言匹配 HTTP 请求的不同属性。可以按照逻辑自由组合。
内置断言
断言工厂 | 功能说明 | 配置 |
---|---|---|
AfterRoutePredicateFactory | 接收一个时间参数,判断请求时间是否晚于指定时间(ZonedDateTime) | - After=2017-01-20T17:42:47.789-07:00[America/Denver] |
BeforeRoutePredicateFactory | 接收一个时间参数,判断请求时间是否早于指定时间(ZonedDateTime) | - Before=2017-01-20T17:42:47.789-07:00[America/Denver] |
BetweenRoutePredicateFactory | 接收两个时间参数,判断请求时间是否在指定时间段内(ZonedDateTime) | - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver] |
CookieRoutePredicateFactory | 接收两个参数,判断请求cookie是否具有给定名称且值与正则表达式匹配 | - Cookie=chocolate, ch.p |
HeaderRoutePredicateFactory | 接收两个参数,判断请求Header是否具有给定名称且值与正则表达式匹配 | - Header=X-Request-Id, \d+ |
HostRoutePredicateFactory | 接收一个参数,判断请求的Host是否满足匹配规则 | - Host=.somehost.org,.anotherhost.org |
MethodRoutePredicateFactory | 接收一个参数,判断请求类型是否跟指定的类型匹配 | - Method=GET,POST |
PathRoutePredicateFactory | 接收一个参数,判断请求的URI部分是否满足路径规则 | - Path=/red/{segment},/blue/{segment} |
QueryRoutePredicateFactory | 接收两个参数,判断请求参数是否具有给定名称且值与正则表达式匹配 | - Query=color, gree. |
RemoteAddrRoutePredicateFactory | 接收一个IP地址段,判断请求主机地址是否在地址段中 | - RemoteAddr=192.168.1.1/24 |
WeightRoutePredicateFactory | 接受两个参数分组和权重,对于同一个组内的路由按照权重转发 | - Weight=group1, 8 - Weight=group1, 2 |
自定义断言
必须按照xxxRoutePredicateFactory
格式命名断言工厂。
自定义评分断言工厂类ScoreRoutePredicateFactory
@Component
public class ScoreRoutePredicateFactory extends AbstractRoutePredicateFactory<ScoreRoutePredicateFactory.Config> {
public ScoreRoutePredicateFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("minScore", "maxScore");
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
// TODO: 获取请求中的 score 参数,并判断是否满足 minScore<score<=maxScore
return false;
};
}
@Data
public static class Config {
private Integer minScore;
private Integer maxScore;
}
}
配置过滤器
网关过滤器
内置网关过滤器
过滤器工厂 | 作用 | 参数 |
---|---|---|
AddRequestHeader | 为原始请求添加Header | Header的名称及值 |
AddRequestParameter | 为原始请求添加请求参数 | 参数名称及值 |
AddResponseHeader | 为原始响应添加Header | Header的名称及值 |
DedupeResponseHeader | 剔除响应头中重复的值 | 需要去重的Header名称及去重策略 |
Hystrix | 为路由引入Hystrix的断路器保护 | HystrixCommand 的名称 |
FallbackHeaders | 为fallbackUri的请求头中添加具体的异常信息 | Header的名称 |
PrefixPath | 为原始请求路径添加前缀 | 前缀路径 |
PreserveHostHeader | 为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host | 无 |
RequestRateLimiter | 用于对请求限流,限流算法为令牌桶 | keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus |
RedirectTo | 将原始请求重定向到指定的URL | http状态码及重定向的url |
RemoveHopByHopHeadersFilter | 为原始请求删除IETF组织规定的一系列Header | 默认就会启用,可以通过配置指定仅删除哪些Header |
RemoveRequestHeader | 为原始请求删除某个Header | Header名称RemoveResponseHeader |
RewritePath | 重写原始的请求路径 | 原始路径正则表达式以及重写后路径的正则表达式RewriteResponseHeader |
SaveSession | 在转发请求之前,强制执行WebSession::save操作 | 无 |
SecureHeaders | 为原始响应添加一系列起安全作用的响应头 | 无,支持修改这些安全响应头的值 |
SetPath | 修改原始的请求路径 | 修改后的路径 |
SetResponseHeader | 修改原始响应中某个Header的值 | Header名称,修改后的值 |
SetStatus | 修改原始响应的状态码 | HTTP 状态码,可以是数字,也可以是字符串 |
StripPrefix | 用于截断原始请求的路径 | 使用数字表示要截断的路径的数量 |
Retry | 针对不同的响应进行重试 | retries、statuses、methods、series |
RequestSize | 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large | 请求包大小,单位为字节,默认值为5M |
ModifyRequestBody | 在转发请求之前修改原始请求体内容 | 修改后的请求体内容ModifyResponseBody |
自定义网关过滤器
必须以xxxGatewayFilterFactory
格式命名。
@Slf4j
@Component
public class CustomTimeGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomTimeGatewayFilterFactory.Config>{
public CustomTimeGatewayFilterFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("enabled");
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
if (!config.enabled) {
return chain.filter(exchange);
}
// pre阶段开启统计
AtomicReference<StopWatch> sw = new AtomicReference<>(new StopWatch(exchange.getRequest().getURI().toString()));
sw.get().start();
return chain.filter(exchange).then(Mono.fromRunnable(()->{
// post阶段结束统计
sw.get().stop();
log.info("CustomTimeGatewayFilterFactory - 执行完毕, \n请求uri[{}], \n请求耗时统计[{}ms]",exchange.getRequest().getURI(), sw.get().getTotalTimeMillis());
}));
};
}
@Getter
@Setter
public static class Config {
// 默认开启耗时统计
private boolean enabled = true;
}
}
全局过滤器
内置全局过滤器
过滤器 | 作用 |
---|---|
ReactiveLoadBalancerClientFilter | 将lb形式url解析为真实的ip:port |
ForwardRoutingFilter | 根据 forward:// 前缀( Scheme )过滤处理,将请求转发到当前网关实例本地接口 |
NettyRoutingFilter | 根据 http:// 或 https:// 前缀( Scheme )过滤处理,使用基于 Netty 实现的 HttpClient 请求后端 Http 服务 |
NettyWriteResponseFilter | 如果后端返回的是Netty HttpClientResponse,将响应代理返回给客户端 |
RouteToRequestUrlFilter | 根据匹配的 Route ,计算请求的地址。注意,这里的地址指的是 URL ,而不是 URI |
WebsocketRoutingFilter | 根据 ws:// / wss:// 前缀( Scheme )过滤处理,代理后端 Websocket 服务,提供给客户端连接 |
GatewayMetricsFilter | 开启网关指标,添加一个名为 gateway.requests 的计时器指标。可整合Prometheus |
自定义全局过滤器
自定义全局鉴权过滤器:CumtomAuthGlobalFilter
@Slf4j
@Component
public class CumtomAuthGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String accessToken = exchange.getRequest() == null || exchange.getRequest().getHeaders().get(HttpHeaders.AUTHORIZATION) == null ? StringUtils.EMPTY : exchange.getRequest().getHeaders().get(HttpHeaders.AUTHORIZATION).get(0);
if (!check(accessToken)) {
log.error("请求路径:[{}],token:[{}] 鉴权失败", exchange.getRequest().getURI(), accessToken);
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
private boolean check(String token) {
if (StringUtils.isBlank(token)) {
return false;
}
// 调用鉴权服务鉴权
return true;
}
@Override
public int getOrder() {
return 0;
}
}
HttpHeaders过滤器
过滤器 | 作用 |
---|---|
ForwardedHeadersFilter | 在请求发往下游前,创建一个Forwarded header。如果已经存在Forwarded header,则把当前请求的host,port,scheme添加到Forwarded header中 |
RemoveHopByHopHeadersFilter | 默认删除转发的请求中header:Connection、Keep-Alive、Proxy-Authenticate、Proxy-Authorization、TE、Trailer、Transfer-Encoding、Upgrade。可通过修改属性spring.cloud.gateway.filter.remove-non-proxy-headers.headers 指定要删除的header |
XForwardedHeadersFilter | 在发向下游前可以创建一系列的X-Forwarded-* headers。它会使用当前请求的Host header,scheme,port和path去创建对应的headers |
其他配置
自定义异常返回
配置类GatewayConfiguration
添加方法:
@PostConstruct
public void initBlockHandlers() {
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
private final String FLOW_ERROR_MESSAGE ="触发限流";
private final Integer FLOW_ERROR_CODE =106;
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable throwable) {
Map map = new HashMap<>();
map.put("code", FLOW_ERROR_CODE);
map.put("msg", FLOW_ERROR_MESSAGE);
log.error("请求uri: [{}], 异常编码: [{}],异常描述: [{}]", exchange.getRequest().getURI(), FLOW_ERROR_CODE, FLOW_ERROR_MESSAGE);
return ServerResponse.status(HttpStatus.OK).
contentType(MediaType.APPLICATION_JSON).
body(BodyInserters.fromValue(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}