客户端访问服务器资源时, 通过网关访问服务器资源, 网关主要有过滤, 鉴权,日志记录, 路由,限流等的功能, 其中过滤器的作用与Spring过滤器功能相通.
Spring Cloud Gateway是spring cloud团队基于Spring5.0, SpringBoot2.0等技术开发的, 本身也是一个微服务, 需要注册到Eureka中心.
![](https://img.haomeiwen.com/i5808899/5aa700437b1e42a7.png)
使用网关的优点:
- 安全, 只有网关对外暴露, 微服务可以隐藏于内网, 通过防火墙进行保护
- 易于监控, 可以在网关收集监控数据并将其推送到外部系统进行分析
- 易于认证, 可以在网关进行认证后再转发到微服务器, 无需再微服务中认证
- 减少了客户端和微服务之间的交互次数
- 易于统一授权
搭建网关微服务, 实现路由分发:
- 创建SpringBoot工程gateway_server, 选择Eureka客户端和gateway两个starter
![](https://img.haomeiwen.com/i5808899/6d9ec6b8727f4ad3.png)
- 在application.yml进行配置, 主要进行路由的配置,
这里采用lb协议, 通过服务的应用名称在Eureka注册中心获取到地址和端口号, 并且自动启用负载均衡进行访问
#声明注册中心地址
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka
spring:
#配置应用名称
application:
name: gateway-server
#配置路由分发
cloud:
gateway:
# 路由si(集合)
routes:
# id唯一标识
- id: user-service-route
# 路由服务地址 采用lb协议, 实现动态地址获取, 采用lb协议时会自动使用负载均衡访问
uri: lb://provider-service
# 断言, 对于/user/*路径的地址会路由到provider-service服务中去
predicates:
- Path=/user/**
filter:
# 配置真正地址中转时路由地址去掉断言的一个参数
- StripPrefix= 1
- 增加过滤器: gateway自带几十种过滤器, 具体可查看官网gatewayfilter_factories
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://example.org
filters:
- AddRequestHeader=X-Request-Foo, Bar
- AddRequestParameter=foo, bar
request的header会增加X-Request-Foo:Bar字段
request的请求字段会增加foo=bar参数
过滤器的类型有两种, 以上为局部过滤器, 还有全局过滤器, 作用在所有路由上
spring:
cloud:
gateway:
# 配置全局默认过滤器
default-filters:
# 往响应过滤器中加入信息
- AddResponseHeader=i-info, Foo
配置网关的跨域问题:
spring:
application:
name: sysgateway
cloud:
gateway:
#解决跨域问题
globalcors:
cors-configurations:
'[/**]': # 匹配所有请求
allowedOrigins: "*" #跨域处理 允许所有的域
allowedMethods: # 支持的方法
- GET
- POST
- PUT
- DELETE
![](https://img.haomeiwen.com/i5808899/b17665b692963835.png)
- 在gate-server自定义全局过滤器, 实现GlobalFilter接口, Ordered接口用来指定filter的执行顺序. 重启服务器即可生效. 注意这个类要用component注解CIA会被spring容器加载.
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
/**
* 设置全局的filter, 比如进行身份的认证
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//这里假装进行身份验证
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (StringUtil.isNullOrEmpty(token)){
//拦截
return exchange.getResponse().setComplete();
}
//放行
return chain.filter(exchange);
}
/**
* 设置filter的执行顺讯
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
网关的限流:
我们之前说过,网关可以做很多的事情,比如,限流,当我们的系统 被频繁的请求的时候,就有可能 将系统压垮,所以 为了解决这个问题,需要在每一个微服务中做限流操作,但是如果有了网关,那么就可以在网关系统做限流,因为所有的请求都需要先通过网关系统才能路由到微服务中。
令牌桶算法
令牌桶算法是常见的限流算法之一, 大致描述如下:
- 所有的请求在处理之前都需要拿到一个令牌才能做被处理, 业务处理完后会将令牌删除
- 根据限流的大小, 按照一定的速率往桶里添加令牌
- 桶设置最大的令牌限额, 当达到最大值时, 新到的令牌会被丢弃或者拒绝
- 桶有最低令牌限额, 当达到最小值时, 业务处理完成后的令牌不会被删除, 以此保证足够的令牌
使用redis中的Ratelimter令牌桶算法实现网关的限流
需求:每个ip地址1秒内只能发送1次请求,多出来的请求返回429错误。
代码实现:
- 引入redis的RateLimter限流算法来实现
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
- 在GatewayApplication中定义KeyResolver对象, 用于指定使用id作为限流条件
@Bean
public KeyResolver ipKeyResolver(){
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getRemoteAddress().getHostString());
}
};
}
- 在application.yml中设置限流规则和redis服务器地址
filters:
- StripPrefix= 1
- name: RequestRateLimiter #请求数限流 名字不能随便写
args:
key-resolver: "#{@ipKeyResolver}"
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 1
redis:
host: 192.168.225.128
port: 6379
解释:
- burstCapacity:令牌桶总容量。
- replenishRate:令牌桶每秒填充平均速率。
- key-resolver:用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
如上配置表示: 一秒内允许一个请求通过, 令牌桶的速度也是一秒内添加一个令牌
通过在replenishRate
和中设置相同的值来实现稳定的速率burstCapacity
。设置burstCapacity
高于时,可以允许临时突发replenishRate
。在这种情况下,需要在突发之间允许速率限制器一段时间(根据replenishRate
),因为2次连续突发将导致请求被丢弃(HTTP 429 - Too Many Requests
)
网友评论