12、Spring Cloud gateway
Spring Cloud Netflix Zuul是由Netflix开源的API网关,在微服务架构下,网关作为对外的门户,实现动态路由、监控、授权、安全、调度等功能。
Zuul基于servlet 2.5(使用3.x),使用阻塞API。 它不支持任何长连接,如websockets。而Gateway建立在Spring Framework 5,Project Reactor和Spring Boot 2之上,使用非阻塞API。 比较完美地支持异步非阻塞编程,先前的Spring系大多是同步阻塞的编程模式,使用thread-per-request处理模型。即使在Spring MVC Controller方法上加@Async注解或返回DeferredResult、Callable类型的结果,其实仍只是把方法的同步调用封装成执行任务放到线程池的任务队列中,还是thread-per-request模型。Gateway 中Websockets得到支持,并且由于它与Spring紧密集成,所以将会是一个更好的开发体验。
12.1、快速入门
快速学习gateway,实现之前zuul路由的功能。
步1、添加依赖
删除之前的zuul依赖,添加gateway依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
完整的pom.xml文件依赖如下,至少包含eureka-client,netflix-ribbin,gateway三项:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- gateway网关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
步2、配置application.yml
主要通过添加spring.cloud.gateway的配置选项。需要说明的是:
uri如果配置成http://server:8001即具体地址则为请求一个具体地址,如果 uri配置成lb://serviceid则为实现负载均衡。
server:
port: 3002
#配置本项目的ID
spring:
application:
name: eureka-gateway
#配置gateway
cloud:
gateway:
enabled: true
routes:
- id: eureka-movie
# 如果输入一个固定的地址则为代理一个地址,
# 如果输入lb://spring.application.name则为通过loadbalance实现负载均衡
uri: #http://server1:8001
lb://eureka-movie
predicates:
- Path=/aa/**
filters:
# 在转发后用于删除/aa/这个前缀,
# 如:http://localhost:3002/aa/movie/1转发以后为:http://localhost:8002/movie/1
- StripPrefix=1
#配置注册到服务器
eureka:
client:
enabled: true
service-url:
defaultZone: http://Jack:1234@server1:8761/eureka
截图:

spring.cloud.gateway.routes配置说明:
id:任意名称,唯一。
uri:可以是一个http地址,也可以是lb://serviceid(将实现负载均衡)
predicates : 影射的地址使用Path=..
filters : 过虑器。
启动后,gateway同样也注册到eureka服务器。现在注册到eureka服务器的服务如下:

其中3002为gateway路由服务器,8001和8002为eureka-movie微服务。现在就可以通过3001访问8001和8002上的应用了。
启动类和之前的代码一样:package cn.wangjian.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class SpringCloudEurekaGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaGatewayApplication.class, args);
}
}
步3、访问测试
第一次访问返回8001服务器的信息:

第二次访问返回8002服务器的信息:

附:
spring.cloud.gateway.routes也可以通过代码实现:

完整代码:
package cn.wangjian.springcloud;
import java.util.function.Function;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.Route.AsyncBuilder;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.GatewayFilterSpec;
import org.springframework.cloud.gateway.route.builder.PredicateSpec;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.route.builder.UriSpec;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableEurekaClient
public class SpringCloudEurekaGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaGatewayApplication.class, args);
}
/**以下是使用Java代码配置的routes*/
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder bb = builder.routes();
bb = bb.route("eureka-movie", new Function<PredicateSpec, AsyncBuilder>() {
@Override
public AsyncBuilder apply(PredicateSpec predicateSpec) {
AsyncBuilder rr = predicateSpec.path("/bb/**").filters(new Function<GatewayFilterSpec, UriSpec>() {
@Override
public UriSpec apply(GatewayFilterSpec gatewayFilterSpec) {
return gatewayFilterSpec.stripPrefix(1);// 删除第一个path部分
}
}).uri("lb://eureka-movie");//或输入:http://server1:8001
return rr;
}
});
return bb.build();
}
}
上面使用Java代码,太过冗长,可以使用Java8表达式,可以实现相关的效果:

完整代码:
package cn.wangjian.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableEurekaClient
public class SpringCloudEurekaGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaGatewayApplication.class, args);
}
/**以下是使用Java代码配置的routes*/
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()//
.route("eureka-movie", sp->sp.path("/cc/**").filters(gf->gf.stripPrefix(1)).uri("lb://eureka-movie"))//
.build();
}
}
OK,到此,基本的gateway路由已经实现。





