官方文档
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.2.RELEASE/reference/html/
在之前也介绍过,Greenwich版本时就已经停止了对spring-cloud-netflix-zuul的更新,而spring cloud官方也推荐了使用gateway来代替zuul组件。不仅如此,gateway的功能甚至比zuul的功能更加强大。
下面是Spring Cloud Gateway启动器的依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
gateway底层通讯是基于netty,属于异步模组,所以它不能和其他一些同步库一起使用(例如Spring Data和Spring Security),下面是官网的描述。
如果引入了gateway依赖,但是不想使用gateway可以配置如下属性
spring.cloud.gateway.enabled=false
接着就可以像启动普通springboot项目一样启动gateway项目就可以了,创建一个springboot启动类
gateway项目的主要功能还是路由,而断言的使用就是对路由的限制,满足predicates后的条件就会路由到uri后的路径
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
此路由匹配具有名称为chocolate,内容为ch.p正则表达式匹配的cookie的请求就会使用路由
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
除此之外还有需要断言条件的类型,可以参考官网中对于断言的描述
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.2.RELEASE/reference/html/#gateway-request-predicates-factories
注意这些断言是可以存在多个条件的,下面就是同时存在时间和cookie条件,同时满足才会被路由
predicates:
- Cookie=chocolate, ch.p
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
gateway也可以去路由到eureka中的微服务,前提是gateway项目也已经注册进了eureka中,这种路由方式是默认支持负载均衡的,而且默认的策略是轮询。如下图所示,在uri: lb://
后面就可以填上需要路由的微服务名。
spring:
cloud:
gateway:
routes:
- id: myRoute
uri: lb://service
predicates:
- Path=/service/**
gateway中存在很多过滤器(这个过滤器顾名思义就是在请求之前需要执行的过滤器的过滤方法),我们如果需要使用则只要在配置文件中配置即可,当然如果gateway提供的过滤器还不满足我们的需求,我们还可以自己实现自定义的过滤器。
如下配置所示,下面配置的过滤器会将相匹配的请求的请求头中添加X-Request-red=blue
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-red, blue
除此之外还有许多可以配置的过滤器,具体的使用可以参考下面的官方文档
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.2.RELEASE/reference/html/#gatewayfilter-factories
在gateway过滤器中我们可以整合Hystrix一起使用,这样就可以使用Hystrix的功能,实现服务降级、服务熔断等功能。
首先我们如果想要整合Hystrix,必须得引入Hystrix得依赖,就是spring-cloud-starter-netflix-hystrix
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>
下面将演示Hystrix过滤器简单的使用,首先有如下的配置,这里的gateway已经配置了eureka客户端,也已经注册进了eureka中,所以eureka相关的配置没有写出。
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: user
uri: lb://SERVICE-USER
predicates:
- Query=name,1
- Query=age
- Path=/**
filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
hystrix:
command:
fallbackcmd:
execution:
isolation:
thread:
timeoutInMilliseconds: 1000
断言和微服务路由前面已经介绍了,就不再说明了,重点是下面的配置。
filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
hystrix:
command:
fallbackcmd:
execution:
isolation:
thread:
timeoutInMilliseconds: 1000
需要关注filters中args里的name和fallbackUri属性,其中name配置的我们CommandName,对应的关系如下图所示,代表的其实就是它的Hystrix配置(Hystrix配置可以参考《Hystrix断路器组件(Finchley版)》)。
至于fallbackUri则代表降级方法的路径forward:
则表示项目内跳转,后面就是路径,降级方法如下
只需要实现GlobalFilter和Ordered就可以创建自定义的过滤器,下面是官网的描述,地址如下
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.2.RELEASE/reference/html/#gateway-combined-global-filter-and-gatewayfilter-ordering
这里我们通过自定义过滤器解决单点登陆的问题,拦截未登录的请求,思路简单来说就是在路由之前通过过滤器将请求拦截,然后拿到本地缓存或数据库中的cookie检验用户是否登陆,如果cookie存在则请求继续,如果不存在则拦截请求。
过滤器的具体实现如下,最后只要加上@Component注解将过滤器注入spring容器中就会生效。
@Component
public class LoginFilter implements GlobalFilter, Ordered {
//判断是否携带 login cookie
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, HttpCookie> cookies = request.getCookies();
// HttpCookie httpCookie = null;
for (Map.Entry<String, List<HttpCookie>> cookie : cookies.entrySet()) {
if (cookie.getKey().equals("login1")) {
System.out.println(1);
return chain.filter(exchange);
}
}
System.out.println(2);
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
@Override
public int getOrder() {
return -1;
}
}
对于大型大型的互联网项目,限流是必不可少的,特别是微服务项目,在了解了系统的负荷之后,如果访问流量超过负荷那么将导致服务整体被压垮,为保证服务可用预防突发流量,在流量到达设置指标时允许摒弃部分以保证服务运行正常。
对于spring cloud项目,使用的是Redis RateLimiter完成的服务限流,它是基于redis和令牌桶算法实现而来的。
官网描述如下
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.2.RELEASE/reference/html/#the-redis-ratelimiter
首先需要加入spring-boot-starter-data-redis-reactive依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redis-reactiveartifactId>
dependency>
令牌桶算法的大致原理如下
下面是简单的配置示例,首先需要配置redis,再就是配置过滤器和令牌桶,因为限流也是基于过滤器实现的
spring:
redis:
host: 127.0.0.1
port: 6379
timeout: 5000
application:
name: service-gateway #此实例注册到eureka服务端的name
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: user
uri: lb://SERVICE-USER
predicates:
- Query=name,1
- Query=age
- Path=/**
filters:
- name: RequestRateLimiter
KeyResolver: userKey
args:
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 2
下面就是限流的配置,也可以看作是令牌桶的配置,首先name是固定的,KeyResolver配置的是令牌的bean的name,redis-rate-limiter.replenishRate配置的是每秒钟生成的令牌数量,redis-rate-limiter.burstCapacity配置的是令牌桶的最大数量。
filters:
- name: RequestRateLimiter
KeyResolver: userKey
args:
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 2
下面配置的就是令牌的bean对象了,具体的实现类就是KeyResolver,不过我们可以配置bean的name,上面配置的也就是bean的name。
创建KeyResolver对象重写resolve方法,其中返回的对象就是真正的令牌,这里是将用户登陆的cookie作为令牌。这样如果用户想要访问页面就必须登陆,而且也实现了想要的限流。
@Bean
public KeyResolver userKey(){
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest();
HttpCookie userCookie = null;
for (Map.Entry<String, List<HttpCookie>> cookies : request.getCookies().entrySet()) {
if (cookies.getKey().equals("login1")) {
userCookie = cookies.getValue().get(0);
}
}
return Mono.just(userCookie.getValue()); //
}
};
}