开发过微服务应用的童鞋应该都耳濡目染接触过spring cloud gateway和zuul1、zuul2这些微服务网关框架,那么:
Spring Cloud Gateway 与 Netflix Zuul 如何选择呢?
下面就来看下他们之间的对比情况:
1. 内部实现:
gateway对比zuul多依赖了spring-webflux,在spring的支持下,功能更强大,内部实现了限流、负载均衡等,扩展性也更强,但同时也限制了仅适合于Spring Cloud套件。
zuul则可以扩展至其他微服务框架中,其内部没有实现限流、负载均衡等。
2. 是否支持异步:
zuul仅支持同步。
gateway支持异步。理论上gateway则更适合于提高系统吞吐量(但不一定能有更好的性能),最终性能还需要通过严密的压测来决定。
3. 框架设计的角度
gateway是苦等不到zuul2产生的,它吸取了zuul的优点,规避了zuul的一些缺点,具有更好的扩展性,并且其已经发布了2.0.0的RELESE版本,稳定性也是非常好的。
4. 性能
gateway依赖的Spring WebFlux 有一个全新的非堵塞的函数式 Reactive Web 框架,可以用来构建异步的、非堵塞的、事件驱动的服务,在伸缩性方面表现非常好。使用非阻塞API。 WebSocket得到支持,并且由于它与Spring紧密集成,所以在Spring体系中有良好的开发体验。
zuul1是一个基于阻塞io的API Gateway。zuul已经发布了zuul2,基于Netty,也是非阻塞的,支持长连接,但Spring Cloud暂时还没有整合计划,Spring Cloud官方主推的就是gateway,因此如果是Spring Cloud框架的话,还是首选gateway。如果是其他Java框架或者Spring Boot的话,可以考虑用zuul2。
提到Spring Cloud Gateway,那就不得不介绍Spring的一个Web框架 --- Spring WebFlux,下面就看看
Spring WebFlux特性:
Spring WebFlux 是一个异步非阻塞式的 Web 框架,它能够充分利用多核 CPU 的硬件资源去处理大量的并发请求。WebFlux 内部使用的是响应式编程(Reactive Programming),以 Reactor 库为基础, 基于异步和事件驱动,可以让我们在不扩充硬件资源的前提下,提升系统的吞吐量和伸缩性。
WebFlux 并不能使接口的响应时间缩短,它仅仅能够提升吞吐量和伸缩性。
由于Spring WebFlux 是一个异步非阻塞式的 Web 框架,所以它特别适合应用在 IO 密集型的服务中,比如微服务网关这样的应用中。
Spring MVC 的前端控制器是 DispatcherServlet, 而WebFlux 是 DispatcherHandler,它实现了 WebHandler 接口:
在handle中这样处理请求:
1. ServerWebExchange 对象中放置每一次 HTTP 请求响应信息,包括参数等;
2. 判断整个接口映射 mappings 集合是否为空,空则创建一个 Not Found 的错误;
3. 根据具体的请求地址获取对应的 handlerMapping;
4. 调用具体业务方法,也就是我们定义的接口方法;
5. 处理返回的结果;
基于Spring Cloud Gateway的网关实现:
Spring Cloud Gateway是基于Spring Framework 5, Spring WebFlux 和 Spring Boot 2.0 进行构建。
它有如下特性:
1. 动态路由:能够匹配任何请求属性;
2. 集成 Spring Cloud 服务发现功能;
3. 可以对路由指定易于编写的 Predicate(断言)和 Filter(过滤器);
4. 集成Hystrix的断路器功能;
5. 请求限流功能;
6. 支持路径重写。
Spring Cloud Gateway中几个核心的对象:
1. Route(路由):路由是网关的基本构件,它由ID、目标URI、谓词集合和过滤器集合定义。如果聚合谓词为真,则匹配路由。
2. Predicate(谓词):允许开发人员匹配HTTP请求中的任何内容,比如头或参数。和Java8的新特性Predicate概率类似。
3. Filter(过滤器):可以在发送下游请求之前或之后修改请求和响应。
代码示例:
@Bean
public RouteLocator myRoutes (RouteLocatorBuilder builder,
UriConfiguration uriConfiguration) {
return builder.routes()
.route(p -> p.path("/test").uri("http://test.com:80/"))
.route(p -> p.path("/csdn").uri("http://www.csdn.net/"))
.route(p -> p.path("/error").uri("forward:/fallback"))
.build();
}
@RequestMapping("/fallback")
public Mono<String> fallback() {
return Mono.just("fallback");
}