SpringCloud微服务网关gateway
网关简介
大家都都知道在微服务架构中,一个系统会被拆分为很多个微服务。那么作为客户端要如何去调用这么多的微服务呢?
如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去用。
这样的架构,会存在着诸多的问题:
-
每个业务都会需要鉴权、限流、权限校验、跨域等逻辑,如果每个业务都各自为战,自己造轮子实现一遍,会很蛋疼,完全可以抽出来,放到一个统一的地方去做。
-
如果业务量比较简单的话,这种方式前期不会有什么问题,但随着业务越来越复杂,比如淘宝、亚马逊打开一个页面可能会涉及到数百个微服务协同工作,如果每一个微服务都分配一个域名的话,一方面客户端代码会很难维护,涉及到数百个域名,另一方面是连接数的瓶颈,想象一下你打开一个APP,通过抓包发现涉及到了数百个远程调用,这在移动端下会显得非常低效。
-
后期如果需要对微服务进行重构的话,也会变的非常麻烦,需要客户端配合你一起进行改造,比如商品服务,随着业务变的越来越复杂,后期需要进行拆分成多个微服务,这个时候对外提供的服务也需要拆分成多个,同时需要客户端配合你进行改造,非常蛋疼。
上面的这些问题可以借助API网关来解决。
Spring Cloud Gateway
网关作为流量的入口,常用的功能包括路由转发,权限校验,限流等。
Spring Cloud Gateway 是Spring Cloud官方推出的第二代网关框架,定位于取代 Netflix Zuul1.0。相比 Zuul 来说,Spring Cloud Gateway 提供更优秀的性能,更强大的有功能。
Spring Cloud Gateway 是由 WebFlux + Netty + Reactor 实现的响应式的 API 网关。它不能在传统的 servlet 容器中工作,也不能构建成 war 包**。**
Spring Cloud Gateway 旨在为微服务架构提供一种简单且有效的 API 路由的管理方式,并基于 Filter 的方式提供网关的基本功能,例如说安全认证、监控、限流等等。
官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
Spring Cloud Gateway 功能特征
- 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 进行构建;
- 动态路由:能够匹配任何请求属性;
- 支持路径重写;
- 集成 Spring Cloud 服务发现功能(Nacos、Eruka);
- 可集成流控降级功能(Sentinel、Hystrix);
- 可以对路由指定易于编写的 Predicate(断言)和 Filter(过滤器);
1.1 核心概念
- 路由(route)
路由是网关中最基础的部分,路由信息包括一个ID、一个目的URI、一组断言工厂、一组Filter组成。如果断言为真,则说明请求的URL和配置的路由匹配。
- 断言(predicates)
Java8中的断言函数,SpringCloud Gateway中的断言函数类型是Spring5.0框架中的ServerWebExchange。断言函数允许开发者去定义匹配Http request中的任何信息,比如请求头和参数等。
- 过滤器(Filter)
SpringCloud Gateway中的filter分为Gateway FilIer和Global Filter。Filter可以对请求和响应进行处理。
1.2 工作原理
执行流程大体如下:
- Gateway Client向Gateway Server发送请求
- 请求首先会被HttpWebHandlerAdapter进行提取组装成网关上下文
- 然后网关的上下文会传递到DispatcherHandler,它负责将请求分发给RoutePredicateHandlerMapping
- RoutePredicateHandlerMapping负责路由查找,并根据路由断言判断路由是否可用
- 如果过断言成功,由FilteringWebHandler创建过滤器链并调用
- 请求会一次经过PreFilter–微服务–PostFilter的方法,最终返回响应
Spring Cloud Gateway快速开始
依赖
<!-- gateway网关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
注意:会和spring-webmvc的依赖冲突,需要排除spring-webmvc
配置
定义了一个路由,请求进来之后,会匹配路由,就配置而言,假如访问过来的地址是,http://127.0.0.1:8088/sentinel-boot/basic/hello,根据predicates中的断言判断是否匹配当前路由,匹配的话,就经过这个路由设置的过滤器处理请求,这里的是/sentinel-boot/basic/hello 处理后变成/basic/hello,然后拼接转发地址http://127.0.0.1:8099/basic/hello
spring:
application:
name: api-gateway
cloud:
gateway:
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: product_route # 当前路由的标识, 要求唯一
uri: http://127.0.0.1:8099 # 请求要转发到的地址
order: 1 # 路由的优先级,数字越小级别越高
predicates: # 断言(就是路由转发要满足的条件)
- Path=/sentinel-boot/** # 当请求路径满足Path指定的规则时,才进行路由转发
filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 # 转发之前去掉1层路径.
访问测试
一个简单的示例只需要配置和引入依赖即可,测试成功如下
集成nacos
依赖
<!-- nacos服务注册与发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
配置和测试
spring:
cloud:
nacos:
# 需要注意的是客户客户端会在此端口+1000的基础上获取通信端口,具体参考nacos安装中的几个端口
server-addr: 61.171.5.6:30848
discovery:
username: nacos
password: nacos
register-enabled: false #一般会注册成服务提供者和消费者,如果不想成为服务提供者,这里设置false
gateway:
routes:
- id: product_route
uri: lb://nacos-producer # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/nacos-producer/**
filters:
- StripPrefix=1
测试成功,图就不贴了,http://127.0.0.1:8088/nacos-producer/product/123
简略配置
spring:
cloud:
nacos:
# 需要注意的是客户客户端会在此端口+1000的基础上获取通信端口,具体参考nacos安装中的几个端口
server-addr: 61.171.5.6:30848
discovery:
username: nacos
password: nacos
register-enabled: false #一般会注册成服务提供者和消费者,如果不想成为服务提供者,这里设置false
gateway:
discovery:
locator:
enabled: true
这时候,就发现只要按照网关地址/微服务/接口的格式去访问,就可以得到成功响应。
如果服务提供端server.servlet.context-path 配置开启了,你可以参考DiscoveryLocatorProperties添加额外的配置
路由断言工厂(Route Predicate Factories)配置
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories
作用: 当请求gateway的时候, 使用断言对请求进行匹配, 如果匹配成功就路由转发, 如果匹配失败就返回404
注意:多个断言,都返回true才算匹配
类型:内置,自定义
SpringCloud Gateway包括许多内置的断言工厂,所有这些断言都与HTTP请求的不同属性匹配。具体如下:
基于ZoneDatetime类型的断言工厂
此类型的断言根据时间做判断,主要有三个:
AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内
ZonedDateTime.now()
匹配这个时间点之后的任意请求
predicates:
- After=2017-01-20T17:42:47.789-07:00[Asia/Shanghai]
匹配这个时间点之前的任意请求
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
大致是这样,其他的看官方文档吧
基于地址的断言工厂
基于请求中的X-Forwarded-For,一般如果是代理服务器过来的,此地址代表真实ip,总的含义就是ip在这个网段内,就匹配
- XForwardedRemoteAddr=192.168.1.1/24
大致意思同上
- RemoteAddr=192.168.1.1/24
基于Cookie的断言工厂
CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求
cookie是否具有给定名称且值与正则表达式匹配。
这个就是cookie的key是chocolate,value和正则表达式 ch. 匹配
-Cookie=chocolate, ch.
基于Header的断言工厂
HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否具有给定名称且值与正则表达式匹配。
-Header=X-Request-Id, \d+
基于Host的断言工厂
HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
-Host=**.testhost.org
基于Method请求方法的断言工厂
MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
-Method=GET
基于Path请求路径的断言工厂
PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
-Path=/foo/{segment}
基于Query请求参数的断言工厂
QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具有给定名称且值与正则表达式匹配。
-Query=baz, ba.
基于路由权重的断言工厂
WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发
routes:
-id: weight_route1
uri: host1
predicates:
-Path=/product/**
-Weight=group3, 1
-id: weight_route2
uri: host2
predicates:
-Path=/product/**
-Weight= group3, 9
自定义路由断言工厂
自定义路由断言工厂需要继承 AbstractRoutePredicateFactory 类,重写 apply 方法的逻辑。在 apply 方法中可以通过 exchange.getRequest() 拿到 ServerHttpRequest 对象,从而可以获取到请求的参数、请求方式、请求头等信息。
- 必须spring组件 bean
- 类必须加上RoutePredicateFactory作为结尾
- 必须继承AbstractRoutePredicateFactory
- 必须声明静态内部类 声明属性来接收 配置文件中对应的断言的信息
- 需要结合shortcutFieldOrder进行绑定
- 通过apply进行逻辑判断 true就是匹配成功 false匹配失败
注意: 命名需要以 RoutePredicateFactory 结尾
示例
这个示例中只接收一个参数,多个参数一般使用逗号分隔,通过shortcutFieldOrder接收,然后会初始化内置的Config,再然后就是在apply中判断是否通过,更多请参考org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory其他实现类
@Component
public class CheckAuthRoutePredicateFactory
extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {
public CheckAuthRoutePredicateFactory() {
super(CheckAuthRoutePredicateFactory.Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("name");
}
@Override
public Predicate<ServerWebExchange> apply(CheckAuthRoutePredicateFactory.Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
if(config.getName().equals("xushu")){
return true;
}
return false;
}
};
}
// 用于接收配置文件中 断言的信息
@Validated
public static class Config {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
配置
gateway:s
routes:
- id: product_route
uri: lb://nacos-producer # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/nacos-producer/**
- CheckAuth=xushu2 # 对应示例中自定义的断言工厂
filters:
- StripPrefix=1
过滤器工厂( GatewayFilter Factories)配置
Gateway 内置了很多的过滤器工厂,我们通过一些过滤器工厂可以进行一些业务逻辑处理器,比如添加剔除响应头,添加去除参数等
如下图,请求分为转发前处理,和转发后处理
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
过滤器工厂 | 作用 | 参数 |
---|---|---|
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 | 为原始响应删除某个Header | Header名称 |
RewritePath | 重写原始的请求路径 | 原始路径正则表达式以及重写后路径的正则表达式 |
RewriteResponseHeader | 重写原始响应中的某个Header | Header名称,值的正 则表达式,重写后的值 |
SaveSession | 在转发请求之前,强制执行WebSession::save 操作 | 无 |
secureHeaders | 为原始响应添加一系列起安全作用的响应头 | 无,支持修改这些安全响应头的值 |
SetPath | 修改原始的请求路径 | 修改后的路径 |
SetResponseHeader | 修改原始响应中某个Header的值 | Header名称,修改后的值 |
SetStatus | 修改原始响应的状态码 | HTTP 状态码,可以是数字,也可以是字符串 |
StripPrefix | 用于截断原始请求的路径 | 使用数字表示要截断的路径的数量 |
Retry | 针对不同的响应进行重试 | retries、statuses、methods、series |
RequestSize | 设置允许接收最大请求包的大 小。如果请求包大小超过设置的值,则返回 413 Payload TooLarge | 请求包大小,单位为字节,默认值为5M |
ModifyRequestBody | 在转发请求之前修改原始请求体内容 | 修改后的请求体内容 |
ModifyResponseBody | 修改原始响应体的内容 | 修改后的响应体内容 |
参数、请求头,请求路径过滤器工厂
去除第一层路径,添加请求头,请求参数,再添加前缀路径
filters:
- StripPrefix=1
- AddRequestHeader=X-Request-color, red #添加请求头
- AddRequestParameter=color, blue # 添加请求参数
- PrefixPath=/nacos-producer # 添加前缀 对应微服务需要配置context-path
测试代码
http://127.0.0.1:8088/nacos-producer/product/test
@RestController
@RequestMapping("/product")
@Slf4j
public class ProductController {
@Value("${server.servlet.context-path}")
String context_path;
@RequestMapping("/test")
public String get(HttpServletRequest request,@RequestParam("color") String color) {
// Thread.sleep(4000);
// 测试添加请求头
log.info(request.getHeader("X-Request-color"));
// 测试添加请求参数
log.info(color);
// 测试添加前缀地址
log.info(context_path);
return "查询商品成功";
}
}
日志
2022-11-17 14:34:59.833 - INFO --- [nio-8005-exec-6] c.s.p.controller.ProductController : red
2022-11-17 14:35:00.935 - INFO --- [nio-8005-exec-6] c.s.p.controller.ProductController : blue
2022-11-17 14:35:01.384 - INFO --- [nio-8005-exec-6] c.s.p.controller.ProductController : /nacos-producer
重定向操作
- RedirectTo=302, https://www.baidu.com/ #重定向到百度,请求最终没有到提供者端
自定义过滤工厂
同样的你可以参考官方的其他实现类
继承AbstractNameValueGatewayFilterFactory且我们的自定义名称必须要以GatewayFilterFactory结尾并交给spring管理。
@Component
public class CheckAuthGatewayFilterFactory
extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {
public CheckAuthGatewayFilterFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("value");
}
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String name=exchange.getRequest().getQueryParams().getFirst("name");
if(StringUtils.isNotBlank(name)){
if(config.getValue().equals(name)){
return chain.filter(exchange);
}
else {
// 返回404
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
return exchange.getResponse().setComplete();
}
}
// 正常请求
return chain.filter(exchange);
}
};
}
public static class Config {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
- CheckAuth=xushu
全局过滤器
文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters
局部过滤器和全局过滤器区别:
局部:局部针对某个路由, 需要在路由中进行配置
全局:针对所有路由请求, 一旦定义就会投入使用
GlobalFilter 接口和 GatewayFilter 有一样的接口定义,只不过, GlobalFilter 会作用于所有路由。
LoadBalancerClientFilter
LoadBalancerClientFilter 会查看exchange的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值(一个URI),如果该值的scheme是 lb,比如:lb://myservice ,它将会使用Spring Cloud的LoadBalancerClient 来将 myservice 解析成实际的host和port,并替换掉 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的内容。
spring:
cloud:
gateway:
routes:
- id: order_route
uri: lb://mall-order
predicates:
- Path=/order/**
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
return chain.filter(exchange);
}
throw NotFoundException.create(use404, "Unable to find instance for " + url.getHost());
}
自定义全局过滤器
@Component
public class LogFilter implements GlobalFilter {
Logger log= LoggerFactory.getLogger(this.getClass());
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info(exchange.getRequest().getPath().value());
return chain.filter(exchange);
}
}
允许跨域访问
官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#cors-configuration
首先跨域是浏览器限制的,一般跨域是通过前端的标签 <link>
元素<script>
元素<img>
元素<iframe>
元素,点击后从一个网站跳转到另一个网站,或者jsonP跨域,域名或ip端口完全相同就不算跨域,当然这需要对应的网站是支持跨域的,这个一般需要在请求返回浏览器的响应头里告诉浏览器我是允许跨域的。
一般这个是一个全局配置,如果是secrity或者shiro一般是在单独的过滤器中设置,当然不集成授权框架,spring本身也支持有这样的全局配置,百度吧
httpServletResponse.setHeader("Access-control-Allow-Origin", "*");
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
gateway直接可以在配置中配置跨域相关的配置,如下
允许https://docs.spring.io跨域过来的get请求
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "https://docs.spring.io"
allowedMethods:
- GET
允许所有来源的跨域
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- DELETE
- PUT
- OPTION
测试代码
在idea中点击浏览器图标打开这个文件即可
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://apps.bdimg.com/libs/jquery/1.9.1/jquery.min.js"></script>
</head>
<body>
<input type="button" value="点击跨域" onclick="getData()">
<script>
function getData() {
$.get('http://127.0.0.1:8088/nacos-producer/product/test',function(data){
alert(data)
});
}
</script>
</body>
</html>
springboot默认的方式实现跨域
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*"); // 允许的method
config.addAllowedOrigin("*"); // 允许的来源
config.addAllowedHeader("*"); // 允许的请求头参数
// 运行访问的资源, 这个代码是由于gateway是通过webflux提供服务的,此处需要添加这样的配置
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
访问日志记录
文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#reactor-netty-access-logs
要启用 Reactor Netty 访问日志,请设置-Dreactor.netty.http.server.accessLogEnabled=true
,似乎不支持在配置文件中开启
总之gateway基于webflux提供访问,webflux又基于netty,这个配置就是开启请求日志的打印
如果需要记录到文件中,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>
日志大致上打印了请求时间,请求路径,响应码等信息,
超时时间配置
文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#http-timeouts-configuration
全局的超时时间
spring:
cloud:
gateway:
httpclient:
connect-timeout: 1000
response-timeout: 5s
路由的超时时间
- id: per_route_timeouts
uri: https://example.org
predicates:
- name: Path
args:
pattern: /delay/{timeout}
metadata:
response-timeout: 200
connect-timeout: 200
TLS和SSL支持
文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#tls-and-ssl
gateway整合sentinel流控降级
网关作为内部系统外的一层屏障, 对内起到一定的保护作用, 限流便是其中之一. 网关层的限流可以简单地针对不同路由进行限流, 也可针对业务的接口进行限流,或者根据接口的特征分组限流。
https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81
基础示例
依赖
注意这里和端方文档引入的不一样,spring-cloud-alibaba-sentinel-gateway中包含了官方要求的sentinel-spring-cloud-gateway-adapter
SentinelGatewayFilter
实例以及 SentinelGatewayBlockExceptionHandler
实例的注入这个依赖SentinelSCGAutoConfiguration类中已经完成了,我们不需要重复注入
<!--sentinel整合gateway 以前的版本adapter-->
<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>
配置
spring:
cloud:
nacos:
# 需要注意的是客户客户端会在此端口+1000的基础上获取通信端口,具体参考nacos安装中的几个端口
server-addr: 61.171.5.6:30848
discovery:
username: nacos
password: nacos
# register-enabled: false #一般会注册成服务提供者和消费者,如果不想成为服务提供者,这里设置false
sentinel:
transport:
dashboard: 127.0.0.1:8858
# 指定sentinel控制台获取应用数据的端口,被占用会端口号默认 +1,设置后还是会自动+1
port: 8719
gateway:
enabled: true
discovery:
locator:
lower-case-service-id: true
routes:
- id: product_route
uri: lb://nacos-producer # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/nacos-producer/**
- CheckAuth=xushu2
#- After=2000-12-31T23:59:59.789+08:00[Asia/Shanghai]
#- Header=X-Request-Id,\d+
#- Method=GET
#- Query=name,xushu|zhuge
#- CheckAuth=xushu2
#配置过滤器工厂
filters:
- StripPrefix=1
- AddRequestHeader=X-Request-color, red #添加请求头
- AddRequestParameter=color, blue # 添加请求参数
- PrefixPath=/nacos-producer # 添加前缀 对应微服务需要配置context-path
# - RedirectTo=302, https://www.baidu.com/ #重定向到百度,请求最终没有到提供者端
- CheckAuth=xushu
# Add your routes here.
- id: product_route # 无对应服务
uri: lb://product
predicates:
- Path=/product/**
- id: httpbin_route # 无对应服务
uri: https://httpbin.org
predicates:
- Path=/httpbin/**
filters:
- RewritePath=/httpbin/(?<segment>.*), /$\{segment} # 效果待定
测试
这里会以路由id作为资源名称,同样的需要这个资源被访问了才能够显示在控制台
网关的sentinel界面,控制台菜单和网关应用是有区别的
被限流后显示
修改默认的限流返回
基础示例中默认的返回是
{
"code": 429,
"message": "Blocked by Sentinel: ParamFlowException"
}
这个是来自com.alibaba.csp.sentinel.adapter.gateway.sc.callback.DefaultBlockRequestHandler处理器,和之前普通应用的处理器DefaultBlockExceptionHandler不同,由于网关是webflux的,这个处理器也是针对webflux
如果我们要自定义返回
配置
sentinel:
transport:
dashboard: 127.0.0.1:8858
# 指定sentinel控制台获取应用数据的端口,被占用会端口号默认 +1,设置后还是会自动+1
port: 8719
scg:
fallback:
mode: response # 选择“重定向”或“响应”。 `redirect` or `response`.
response-body: '{"code":403,"mes":"限流了"}'
成功后如下
实现自定义处理器
@Configuration
public class GatewayConfig {
@PostConstruct
public void init(){
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {
System.out.println(t);
HashMap<String,String> map=new HashMap<>();
map.put("code",HttpStatus.TOO_MANY_REQUESTS.toString());
map.put("message","限流了2");
// 自定义异常处理
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}
本地设置流控规则
大致和我sentinel基础示例中流控规则的初始化方式一致,只不过规则类变了
@PostConstruct
public void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
//resource:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。
//count:限流阈值
//intervalSec:统计时间窗口,单位是秒,默认是 1 秒。
rules.add(new GatewayFlowRule("order_route")
.setCount(2)
.setIntervalSec(1)
);
rules.add(new GatewayFlowRule("user_service_api")
.setCount(2)
.setIntervalSec(1)
);
// 加载网关规则
GatewayRuleManager.loadRules(rules);
}
网关限流配置项
官方文档:https://sentinelguard.io/zh-cn/docs/api-gateway-flow-control.html
其中网关限流规则 GatewayFlowRule
的字段解释如下:
-
resource
:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。 -
resourceMode
:规则是针对 API Gateway 的 route(RESOURCE_MODE_ROUTE_ID
)还是用户在 Sentinel 中定义的 API 分组(RESOURCE_MODE_CUSTOM_API_NAME
),默认是 route。 -
grade
:限流指标维度,同限流规则的grade
字段。 -
count
:限流阈值 -
intervalSec
:统计时间窗口,单位是秒,默认是 1 秒。 -
controlBehavior
:流量整形的控制效果,同限流规则的controlBehavior
字段,目前支持快速失败和匀速排队两种模式,默认是快速失败。 -
burst
:应对突发请求时额外允许的请求数目。 -
maxQueueingTimeoutMs
:匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。 -
paramItem
:参数限流配置。若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规则;否则会转换成热点规则。其中的字段:
parseStrategy
:从请求中提取参数的策略,目前支持提取来源 IP(PARAM_PARSE_STRATEGY_CLIENT_IP
)、Host(PARAM_PARSE_STRATEGY_HOST
)、任意 Header(PARAM_PARSE_STRATEGY_HEADER
)和任意 URL 参数(PARAM_PARSE_STRATEGY_URL_PARAM
)四种模式。fieldName
:若提取策略选择 Header 模式或 URL 参数模式,则需要指定对应的 header 名称或 URL 参数名称。pattern
:参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控;若为空则统计该请求属性的所有值。(1.6.2 版本开始支持)matchStrategy
:参数值的匹配策略,目前支持精确匹配(PARAM_MATCH_STRATEGY_EXACT
)、子串匹配(PARAM_MATCH_STRATEGY_CONTAINS
)和正则匹配(PARAM_MATCH_STRATEGY_REGEX
)。(1.6.2 版本开始支持)
用户可以通过 GatewayRuleManager.loadRules(rules)
手动加载网关规则,或通过 GatewayRuleManager.register2Property(property)
注册动态规则源动态推送(推荐方式)。
api分组限流
就是先配置那些请求路径属于哪个分组,然后根据分组配置规则
其他配置项说明
网关高可用
为了保证 Gateway 的高可用性,可以同时启动多个 Gateway 实例进行负载,在 Gateway 的上游使用 Nginx 或者 F5 进行负载转发以达到高可用。配置就不粘贴了,大致是这个意思
关联信息
- 关联的主题:
- 上一篇:
- 下一篇:
- image: 20221021/1
- 转载自: