Spring Cloud Gateway是Spring Cloud官方推出的网关框架,用来取代Zuul网关(指Zuul 1.x,是一个基于阻塞I/O的API Gateway)。Spring Cloud Gateway建立在Spring5、Project Reactor和SpringBoot2之上,使用非阻塞API。Spring Cloud Gateway还支持WebSocket,并且与Spring紧密集成,拥有更好的开发体验。网关常见的功能除了路由转发之外,还有权限校验、限流控制等功能。
在Spring Cloud Gateway中内置了很多Predicate和Filter类型。
我这里专门创建了一个gateway-server服务,用来搭建网关服务。
主要是添加spring-cloud-starter-gateway依赖,这是搭建网关需要的依赖,同时引入了spring-cloud-starter-netflix-eureka-client依赖,主要实现服务的注册和发现(注册中心使用之前搭建的即可)。
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-gateway
这里除了增加了应用名称、端口号、Eureka服务注册外,主要还添加了两个服务的路由配置,具体如下:
spring.application.name=gateway-server
server.port=8100
#设置与Eureka Server交互的地址。
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8000/eureka/
spring.cloud.gateway.routes[0].id=gateway-clientA
spring.cloud.gateway.routes[0].uri=lb://gateway-clientA
spring.cloud.gateway.routes[0].predicates[0]= Path=/clientA/**
spring.cloud.gateway.routes[0].filters[0]= StripPrefix=1
spring.cloud.gateway.routes[1].id=gateway-clientB
spring.cloud.gateway.routes[1].uri=lb://gateway-clientB
spring.cloud.gateway.routes[1].predicates[0]= Path=/clientB/**
spring.cloud.gateway.routes[1].filters[0]= StripPrefix=2
其中,
启动类,是一个普通的SpringBoot启动类,网关服务也不需要额外增加启动网关服务的注解,这里只需要增加了服务注册发现的注解@EnableDiscoveryClient即可。
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayServerApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayServerApplication.class, args);
}
}
这里为了演示API网关服务的使用,我们准备了两个应用gateway-clientA和gateway-clientB来进行测试。这两个服务是普通的SpringBoot应用,分别提供了一个测试接口,具体可以参考源码。
分别启动网关服务和两个测试服务,访问gateway-clientA服务时,可以通过http://localhost:8100/clientA/provider 进行访问,这里访问了网关服务的,然后由服务网关进行路由转发到gateway-clientA应用实例上。访问gateway-clientB服务时,可以通过http://localhost:8100/clientB/gateway-clientB/provider 进行访问,和访问gateway-clientA的区别是在访问路径上增加了服务名称,这个主要是因为StripPrefix参数决定的。
如果我们有非常多的微服务模块,每个服务模块都通过前面的这种方式进行配置会非常的麻烦,有什么方式可以避免这种配置呢?我们可以基于注册服务,启用自动的路由配置。
其实这种方法也很简单,因为前面我们的实例已经引入了Eureka注册中心,所以这里我们只需要修改网关服务的配置文件即可,如下所示:
spring.application.name=gateway-server
server.port=8100
#设置与Eureka Server交互的地址。
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8000/eureka/
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
其中,
修改配置后,重新启动网关服务,这个时候再分别访问http://localhost:8100/gateway-clienta/provider 和 http://localhost:8100/gateway-clientb/provider 就可以进行相关api接口的访问了。
Spring Cloud Gateway支持Hystrix过滤器的应用,这里选择集成Hystrix实现服务的熔断降级。
首先,添加Hystrix依赖,如下:
org.springframework.cloud
spring-cloud-starter-hystrix
1.4.7.RELEASE
然后,修改配置如下:
spring.cloud.gateway.routes[1].id=gateway-clientB
spring.cloud.gateway.routes[1].uri=lb://gateway-clientB
spring.cloud.gateway.routes[1].predicates[0]= Path=/clientB/**
spring.cloud.gateway.routes[1].filters[0]= StripPrefix=2
#熔断降级
spring.cloud.gateway.routes[1].filters[1].name=Hystrix
spring.cloud.gateway.routes[1].filters[1].args.name=fallback
spring.cloud.gateway.routes[1].filters[1].args.fallbackUri=forward:/fallback
&esmp;其中,
最后,增加一个回调方法,如下:
@RestController
public class HystrixController {
@GetMapping("fallback")
public String fallback(){
return "Hystrix限流,被降级!";
}
}
根据请求失败类型,可以为服务设置重机制,如下:
spring.cloud.gateway.routes[0].id=gateway-clientA
spring.cloud.gateway.routes[0].uri=lb://gateway-clientA
spring.cloud.gateway.routes[0].predicates[0]= Path=/clientA/**
spring.cloud.gateway.routes[0].filters[0]= StripPrefix=1
#重试
spring.cloud.gateway.routes[0].filters[1].name=Retry
spring.cloud.gateway.routes[0].filters[1].args.retries=3
spring.cloud.gateway.routes[0].filters[1].args.statuses=BAD_GATEWAY
Spring Cloud Gateway 提供了基于 Redis 的限流方案。为了实现限流功能,我们首先需要添加对应的依赖包spring-boot-starter-data-redis-reactive。如下所示:
org.springframework.boot
spring-boot-starter-data-redis-reactive
2.0.4.RELEASE
然后,修改配置文件,如下:
#redis
spring.redis.host=localhost
spring.redis.password=123456
spring.redis.port=6379
spring.cloud.gateway.routes[0].id=gateway-clientA
spring.cloud.gateway.routes[0].uri=lb://gateway-clientA
spring.cloud.gateway.routes[0].predicates[0]= Path=/clientA/**
spring.cloud.gateway.routes[0].filters[0]= StripPrefix=1
#限流
spring.cloud.gateway.routes[0].filters[2].name=RequestRateLimiter
spring.cloud.gateway.routes[0].filters[2].args.redis-rate-limiter.replenishRate=20
spring.cloud.gateway.routes[0].filters[2].args.redis-rate-limiter.burstCapacity=30
spring.cloud.gateway.routes[0].filters[2].args.key-resolver=#{@ipKeyResolver}
其中,
定义KeyResolver,实现如下:
@Configuration
public class RatelimitConfig {
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
}
该Bean会被注册到Spring容器中,然后通过配置文件,可以被限流过滤器读取并使用。