目录
执行流程
-
客户端请求进入网关
-
进入
RoutePredicateHandlerMapping
,根据路由定义,创建路由对象,进行路由匹配,匹配通过往下执行,反之报错404 -
进入
FilteringWebHandler
,执行过滤器链
时序图
核心类
| class类 | 作用 |
|---|---|
| RoutePredicateHandlerMapping | 路由转发 |
| RouteDefinitionRouteLocator | 根据路由定义,创建路由对象 |
| FilteringWebHandler | 合并全局过滤器、默认过滤器、路由配置过滤器,排序,执行过滤器链 |
| GatewayFilterFactory | 创建过滤器 |
| RoutePredicateFactory | 创建谓词 |
| DefaultGatewayFilterChain | 执行过滤器链 |
路由定义
public class Route implements Ordered {
// 路由唯一标志,
private final String id;
// 路由转发对应微服务的地址
private final URI uri;
// 路由执行顺序编号
private final int order;
//谓词
private final AsyncPredicate<ServerWebExchange> predicate;
//网关过滤器集合
private final List<GatewayFilter> gatewayFilters;
//省略部分源代码
}
配置路由谓词
路径路由谓词工厂(PathRoutePredicateFactory)
支持配置多个路径
-
简写方式
spring:
cloud:
gateway:
routes:
# '-'代表一个对象
- id: path_route
uri: lb://product-service
predicates:
#简写
- Path=/a/get/{segment} # 如果请求路径为/a/get//1或/a/get/bar,则此路由匹配。多个谓词,以‘,’分隔开
-
完整写法
spring: cloud: gateway: routes: - id: path_route uri: lb://product-service predicates: - name: Path # 路由谓词工厂的前缀,如PathRoutePredicateFactory args: # args 下的参数,需要根据每个谓词工厂中的Config内部类的属性定义。 patterns: /a/**,/b/**
-
一个路由配置多个谓词
spring: cloud: gateway: routes: - id: path_route uri: lb://product-service predicates: - name: Path # 路由谓词工厂的前缀,如PathRoutePredicateFactory args: # args 下的参数,需要根据每个谓词工厂中的Config内部类的属性定义。 patterns: /a/**,/b/** - name: Query args: param: user regexp: zhangsan.* ######################简写方式########################################## spring: cloud: gateway: routes: - id: path_route uri: lb://product-service predicates: - Path=/a/**,/b/** - Query=user,zhangsan.*
配置路由过滤器
StripPrefixGatewayFilterFactory
该工厂中有一个
parts
参数,该
parts
参数指示在将请求发送到下游之前要从请求中剥离的路径中的部分数
spring: cloud: gateway: routes: - id: path_route uri: lb://product-service predicates: - Path=/a/**,/b/** filters: - StripPrefix=1 #前端请求地址:http://localhost:8082/a/helloword,往下游转发前,会将原地址中的a去掉,变成http://product-service/helloword,往下转发。
RequestRateLimiterGatewayFilterFactory
用户请求速率限制器过滤器,起到限流作业
spring:
cloud:
gateway:
routes:
- id: path_route
uri: lb://product-service
predicates:
- Path=/a/**,/b/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 5
key-resolver: "#{@myKeyResolver}" # Spel表达式
-
redis-rate-limiter.replenishRate: 1,是您希望用户每秒允许多少个请求,而没有任何丢弃的请求。这是令牌桶被填充的速率。
-
redis-rate-limiter.burstCapacity是允许用户在一秒钟内执行的最大请求数。这是令牌桶可以容纳的令牌数。将此值设置为零将阻止所有请求。
-
key-resolver,表示按照什么策略限流,可以根据ip限流,等等。这个里面配置具体策略。
/**
* @author:shixianqing
* @Date:2021/12/9 11:11
* @Description: 限制请求的标识,可以根据用户名,客户端ip,进行限流
**/
@Component
@Slf4j
public class MyKeyResolver implements KeyResolver {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
String hostAddress = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
log.info("hostAddress:{}",hostAddress);
return Mono.just(hostAddress);
}
}
HystrixGatewayFilterFactory
熔断降级过滤器。
spring: cloud: gateway: routes: - id: path_route uri: lb://product-service predicates: - Path=/a/**,/b/** filters: - name: Hystrix args: name: fallbackcmd # RouteHystrixCommand 组名 fallbackUri: forward:/fallback # 重定向自定义降级接口 # 降级超时配置 hystrix: command: fallbackcmd: execution: isolation: thread: timeoutInMilliseconds: 1000
/**
* 降级接口
*/
@RestController
@Slf4j
public class FallbackController {
@RequestMapping("fallback")
@ResponseStatus
public String fallback(ServerWebExchange exchange) throws Exception {
Exception exception = exchange.getAttribute(ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR);
ServerWebExchange delegate = ((ServerWebExchangeDecorator) exchange).getDelegate();
log.error("接口调用失败,URL={}", delegate.getRequest().getURI(), exception);
if (exception instanceof HystrixTimeoutException) {
log.error("msg {}", "接口调用超时");
throw new BizException("100","接口调用超时");
} else if (exception != null && exception.getMessage() != null) {
log.error("msg {}", "接口调用失败: " + exception.getMessage());
throw new BizException("100","接口调用失败,失败原因:"+exception.getMessage());
} else {
log.error("msg {}", "接口调用失败");
throw new BizException("100","接口调用失败:"+exception.getMessage());
}
}
}
接口调用,出现降级,执行下面方法
@Override
protected Observable<Void> resumeWithFallback() {
//没有配置降级接口,调用父类降级处理
if (this.fallbackUri == null) {
return super.resumeWithFallback();
}
// TODO: copied from RouteToRequestUrlFilter
URI uri = exchange.getRequest().getURI();
// TODO: assume always?
boolean encoded = containsEncodedParts(uri);
URI requestUrl = UriComponentsBuilder.fromUri(uri).host(null).port(null)
.uri(this.fallbackUri).scheme(null).build(encoded).toUri();
//保存降级地址
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
addExceptionDetails();
//重新构造request,请求降级接口
ServerHttpRequest request = this.exchange.getRequest().mutate()
.uri(requestUrl).build();
ServerWebExchange mutated = exchange.mutate().request(request).build();
return RxReactiveStreams.toObservable(getDispatcherHandler().handle(mutated));
}
}
自定义全局过滤器
自定义全局过滤器,可以做身份验证等一些安全功能。每个请求都会经过。
@Slf4j
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//鉴权
List<String> name = exchange.getRequest().getHeaders().get("name");
log.info("name:{}",name);
String path = exchange.getRequest().getURI().getPath();
//path:/a/helloword
log.info("path:{}",path);
String url = exchange.getRequest().getURI().toString();
//url:http://localhost:8082/a/helloword
log.info("url:{}",url);
if (CollectionUtils.isEmpty(name)) {
return Mono.error(new BizException("100","当前未登录"));
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
自定义全局异常处理器
@Component
@Order(-1)
@Slf4j
public class GlobalExceptionHandler implements WebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
//获取原始请求地址
LinkedHashSet<URI> uris = exchange
.getAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
String url;
if (CollectionUtils.isEmpty(uris)) {
url = exchange.getRequest().getURI().toString();
} else {
url = uris.stream().findFirst().get().toString();
}
log.error("url:{},调用失败:",url,ex);
R r = null;
if (ex instanceof TimeoutException) {
r = R.error("接口调用超时,请稍后重试");
} else if (ex instanceof BizException) {
BizException e = (BizException) ex;
r = R.error(e.getMessage());
} else if (ex instanceof ResponseStatusException) {
ResponseStatusException e = (ResponseStatusException) ex;
if (e.getStatus().is4xxClientError()) {
r = R.error("服务不存在,请联系管理员");
} else if (e.getStatus().is5xxServerError()) {
r = R.error("系统异常,请联系管理员");
}
}else {
r = R.error("接口调用失败,请稍后重试");
}
String responseStr = JSONObject.toJSONString(r);
ServerHttpResponse response = exchange.getResponse();
//处理响应乱码
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory factory = response.bufferFactory();
try {
return factory.wrap(responseStr.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return factory.wrap(new byte[0]);
}
}));
}
}
http请求超时配置
全局超时
配置全局 http 超时:
connect-timeout
必须以毫秒为单位指定。
response-timeout
必须指定为 java.time.Duration
spring:
cloud:
gateway:
httpclient:
connect-timeout: 1000
response-timeout: 5s
每个路由超时
要配置每条路由超时:
connect-timeout
必须以毫秒为单位指定。
response-timeout
必须以毫秒为单位指定。
- id: per_route_timeouts
uri: https://example.org
predicates:
- name: Path
args:
pattern: /delay/{timeout}
metadata:
response-timeout: 200
connect-timeout: 200
跨域配置
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
# 允许携带认证信息
# 允许跨域的源(网站域名/ip),设置*为全部
# 允许跨域请求里的head字段,设置*为全部
# 允许跨域的method, 默认为GET和OPTIONS,设置*为全部
# 跨域允许的有效期
allow-credentials: true
allowedOrigins: "*"
allowedHeaders: "*"
allowedMethods: "*"
参考
GlobalCorsProperties
配置
官方网站
参考spring官网