1. gateway过滤器
Gateway中Filter的生命周期只有两个:pre 和 post:
PRE: 这种过滤器在请求被路由之前调用,可实现身份验证。
POST:这种过滤器在路由到微服务以后执行,可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
Gateway 的Filter从作用范围可分为两种: GatewayFilter与GlobalFilter。 GatewayFilter:应用到单个路由或者一个分组的路由上,GlobalFilter:应用到所有的路由上。
内置过滤器:
过滤器工厂 | 作用 | 使用 |
AddRequestHeader | 为原始请求添加Header | - AddRequestHeader=X-Request-red, blue |
AddRequestParameter | 为原始请求添加请求参数 | - AddRequestParameter=color, blue |
AddResponseHeader | 为原始响应添加Header | - AddResponseHeader=X-Response-Red, Blue |
DedupeResponseHeader | 剔除响应头中重复的值 | - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin |
CircuitBreaker | spring cloud gateway 熔断器,支持resilience4j | |
FallbackHeaders | 为fallbackUri的请求头中添加具 体的异常信息 | |
MapRequestHeader | 从已有的请求头中获取值,并赋值给一个新的请求头 | - MapRequestHeader=Blue, X-Request-Red |
PrefixPath | 为原始请求路径添加前缀 | - PrefixPath=/mypath |
PreserveHostHeader | 为请求添加一个 preserveHostHeader=true的属 性,路由过滤器会检查该属性以 决定是否要发送原始的Host | - PreserveHostHeader |
RequestRateLimiter | 用于对请求限流,限流算法为令牌桶 | |
RedirectTo | 将原始请求重定向到指定的URL | |
RemoveHopByHopHeadersFilter | 删除header | - RemoveResponseHeader=X-Response-Foo |
RemoveRequestParameter | 删除参数 | - RemoveRequestParameter=red |
RequestHeaderSize | 限制请求头大小,超过限制大小将会返回431 | - RequestHeaderSize=1000B |
RewritePath | 重写原始的请求路径 | - RewritePath=/red/?(?.*), /${segment} |
RewriteResponseHeader | 重写原始响应中的某个Header | |
SaveSession | 在转发请求之前,强制执行 | - SaveSession |
SecureHeaders | 为原始响应添加一系列起安全作用的响应头 | spring.cloud.gateway.filter.secure-headers.disable=x-frame-options,strict-transport-security |
SetPath | 修改原始的请求路径 | |
SetResponseHeader | 修改原始响应中某个Header的值 | - SetRequestHeader=X-Request-Red, Blue |
SetRequestHeader | 修改请求头的值 | - SetRequestHeader=X-Request-Red, Blue |
SetStatus | 修改原始响应的状态码 | - SetStatus=401 |
StripPrefix | 用于截断原始请求的路径,使用数字表示要截断的路径的数量 | - StripPrefix=2 |
Retry | 针对不同的响应进行重试 | |
RequestSize | 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回413,请求包大小,单位为字 节,默认值为5M | |
SetRequestHostHeader | 设置请求host | |
ModifyRequestBody | 在转发请求之前修改原始请求体 | |
内容 | ||
ModifyResponseBody | 修改响应内容 |
2 自定义过滤器
自定义一个过滤器打印请求日志
在配置文件中配置:
# true/false控制日志是否开启
- MyLog=true
自定义一个过滤器工厂:
/**
* 自定义过滤器
*/
@Component
public class MyLogGatewayFilterFactory extends AbstractGatewayFilterFactory<MyLogGatewayFilterFactory.MyLogConfig> {
public MyLogGatewayFilterFactory() {
super(MyLogConfig.class);
}
/**
* 读取配置文件中的参数 赋值到 配置类中
* @return
*/
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("showLog");
}
@Override
public GatewayFilter apply(MyLogConfig config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (config.showLog) {
System.out.println("请求路径:" + exchange.getRequest().getPath());
}
return chain.filter(exchange);
}
};
}
/**
* 配置类接收配置的参数
*/
public static class MyLogConfig {
private boolean showLog;
public boolean isShowLog() {
return showLog;
}
public void setShowLog(boolean showLog) {
this.showLog = showLog;
}
}
}
然后启动测试:
3 自定义全局过滤器
全局过滤器作用于所有路由, 无需配置。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能。开发一个全局过滤器实现接口的统一鉴权,大概逻辑为:当客户端第一次请求服务时,服务端对用户进行信息认证(登录) ,如果认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证; 以后每次请求,客户端都携带认证的token 服务端对token进行解密,判断是否有效。自定义全局过滤器需要实现GlobalFilter和Ordered接口。
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("token");
if (StringUtils.hasText(token)) {
// TODO 处理鉴权逻辑
System.out.println("auth: 鉴权成功");
return chain.filter(exchange);
} else {
System.out.println("auth: token丢失!!");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}
/**
* 数值越小越先执行
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
4 网关限流
网关是所有请求的公共入口,所以可以在网关进行限流,而且限流的方式也很多。Sentinel支持对SpringCloud Gateway、Zuul等主流网关进 行限流。从1.6.0版本开始,Sentinel提供了SpringCloud Gateway的适配模块,可以提供两种资源维度的限流: route维度,即在Spring配置文件中配置的路由条目,资源名为对应的routeId;自定义API维度,用户可以利用Sentinel提供的API来自定义一些API分组。
使用sentinel实现网关限流需要先引入依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
限流配置:
package com.tdt.platformcloud.gateway.config;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.*;
/**
* 请求限流配置
*/
@Configuration
public class RequestLimitConfig {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public RequestLimitConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
/**
* 初始化限流过滤器
* @return
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
/**
* 配置初始化的限流参数
*/
@PostConstruct
public void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("product_route") // 资源名称,对应路由ID
.setCount(1) // 阈值
.setIntervalSec(1)); // 统计时间窗口,单位是秒,默认1s
GatewayRuleManager.loadRules(rules);
}
/**
* 限流异常处理器
* @return
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
/**
* 自定义限流异常页面
*/
@PostConstruct
public void initBlockHandlers() {
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map<String, Object> result = new HashMap<>();
result.put("code", 0);
result.put("message", "接口被限流了,稍后再试");
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(result));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}
然后测试一下:
还可以自定义api分组进行更细粒度的限流。