zuul是Netflix开发的一套不错的网关,但是也有着一定的缺陷,目前Netflix 对着zuul2.0开始了开发;但是随着Netflix 开始对旗下很多微服务组件开始停更以后,zuul2.0以后也可能面临着停更的风险;在此基础上 spring推出了比较优秀的一款框架 – gateway;其目标是为了替换zuul,成为新一代的网关;
Spring Cloud Gateway 可以看做是一个 Zuul 1.x 的升级版和代替品,比 Zuul 2 更早的使用 Netty 实现异步 IO,从而实现了一个简单、比 Zuul 1.x 更高效的、与 Spring Cloud 紧密配合的 API 网关。 Spring Cloud Gateway 里明确的区分了 Router 和 Filter,并且一个很大的特点是内置了非常多的开箱即用功能,并且都可以通过 SpringBoot 配置或者手工编码链式调用来使用。
比如内置了 10 种 Router,使得我们可以直接配置一下就可以随心所欲的根据 Header、或者 Path、或者 Host、或者 Query 来做路由。
比如区分了一般的 Filter 和全局 Filter,内置了 20 种 Filter 和 9 种全局 Filter,也都可以直接用。当然自定义 Filter 也非常方便。
创建maven工程
导入pom依赖
<dependencies> <!--gateway依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--eureka依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> 创建启动器 @SpringBootApplication @EnableDiscoveryClient//成为注册中心服务 public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } 修改配置文件 server: port: 9999 spring: application: name: gateway-server # 应用名称,会在Eureka中作为服务的id标识(serviceId) cloud: gateway: routes: - id: user-service #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: lb://user-service #匹配后提供服务的路由地址,lb指的是从注册中心中获取对应服务名称的ip地址 predicates: - Path=/user/** #断言,路径相匹配的进行路由 eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka注:网关可以配置真实的url地址的,但是这样维护比较麻烦,所以一般都是通过注册中心来获取配置信息
测试 这里跟zuul的不同,zuul是在端口号后面加上设置的 后缀,在转发的;这里在端口号中直接加你访问的端口号,之后路由匹配规则后转发的gateway有个强大的几个功能
Route(路由):这是网关的基本构建模块。它由一个ID,一个目标url,一组断言和一组过滤器定义。如果断言是真,则路由匹配Predicate(断言):输入类型是一个ServerWebExchange。我们可以使用它来匹配来自Http请求的任务内容,例如Headers或参数Filter(过滤器):Gateway中的filter分为两种类型的Filter,分别是GatewayFilter和Global Filter。这种过滤器Filter将会对请求和响应进行修改处理路由和断言是gateway的一个特色,路由的转发,会根据匹配到断言设置的,转发,这里gateway的配置有两种,一种是配置文件中配置,一种是代码配置
在配置文件中配置的话,就是按照我们原来案例中来配置的
在代码中配置的话,是利用springboot的配置文件的方法来实现配置
@Configuration public class GateWayConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) { RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes(); //第一个 user-service2 对应使用的id //第二个对应路径 //第三个对应url routes.route("user-service2", r -> r.path("/ace/**").uri("lb://user-service")).build(); return routes.build(); } }在配置路由的时候我们发现了,gateway是根据匹配到的规则来转发的;但是gateway配置的规则不只是有路径,还有其他
配置文件实现
server: port: 10001 spring: application: name: gateway-server # 应用名称,会在Eureka中作为服务的id标识(serviceId) cloud: gateway: routes: - id: user-service #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: lb://user-service #匹配后提供服务的路由地址 order: -1 predicates: - Path=/user/** #断言,路径相匹配的进行路由 - Host=**.foo.org #通过hsot匹配接收一组参数,一组匹配的域名列表,这个模板是一个 ant 分隔的模板,用.号作为分隔符。它通过参数中的主机地址作为匹配规则。 - Cookie=ityouknow, kee #一个是 Cookie name , 一个是正则表达式,路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。--cookie "ityouknow=kee" - Path=/headers #断言,路径相匹配的进行路由 - Method=GET # 通过请求方式匹配 - Header=X-Request-Id, \d+ # 通过请求头中的参数来匹配 第一个是属性 第二个是属性值 - Query=foo, ba. # 通过请求参数属性匹配 包含 keep 属性并且参数值是以 pu 开头的长度为三位的字符串才会进行匹配和路由。 例如localhost:8080?keep=pub - Query=baz # 通过请求参数属性匹配 必须有 含有的参数 - After=2018-01-20T06:06:06+08:00[Asia/Shanghai] # 时间配置 在这个时间之后才会进入 - RemoteAddr=192.168.1.1/24 # 根据id地址匹配注:在代码中实现的话,将r.path()更换为其他的方式匹配即可
@Bean public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) { RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes(); //第一个 user-service2 对应使用的id //第二个对应路径 //第三个对应url routes.route("user-service2", r -> r.path("/ace/**").uri("lb://user-service")).build(); return routes.build(); }单一拦截器
@Component public class CsdnRequestFilter implements GatewayFilter, Ordered { @Value("${gateway.csdn.PaaSID}") private String paaSID; @Value("${gateway.csdn.PaaSToken}") private String paaSToken; @Value("${gateway.csdn.nonce}") private String nonce; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String timeTamp = String.valueOf(System.currentTimeMillis() / 1000); String signature = SecureUtil.sha256(timeTamp + nonce + paaSToken); System.out.print("自定义过滤器:timeTamp:"+timeTamp+";signature:"+signature); //提取应用账户及应用令牌,鉴权 ServerHttpRequest request = exchange.getRequest().mutate() .header("x-tif-nonce", nonce) .header("x-tif-signature", signature) .header("x-tif-paasid", paaSID) .header("x-tif-timestamp", timeTamp) .build(); return chain.filter(exchange.mutate().request(request).build()); } @Override public int getOrder() { return 1; } }这是继承实现后,需要在代码实现的路由器中配置
return builder.routes() .route(r -> r.path("/csdn3/**") .filters(f->f.stripPrefix(1) .filter(csdnRequestFilter)) .uri(csdnHost) .order(3)) .build();Zuul:
使用的是阻塞式的 API,不支持长连接,比如 websockets。
底层是servlet,Zuul处理的是http请求
没有提供异步支持,流控等均由hystrix支持。
依赖包spring-cloud-starter-netflix-zuul。
Gateway:
底层依然是servlet,但使用了webflux,多嵌套了一层框架
依赖spring-boot-starter-webflux和/ spring-cloud-starter-gateway
提供了异步支持,提供了抽象负载均衡,提供了抽象流控,并默认实现了RedisRateLimiter。
相同点: 1、底层都是servlet
2、两者均是web网关,处理的是http请求
不同点:
1、内部实现:
gateway对比zuul多依赖了spring-webflux,在spring的支持下,功能更强大,内部实现了限流、负载均衡等,扩展性也更强,但同时也限制了仅适合于Spring Cloud套件 zuul则可以扩展至其他微服务框架中,其内部没有实现限流、负载均衡等。 2、是否支持异步 zuul仅支持同步 gateway支持异步。理论上gateway则更适合于提高系统吞吐量(但不一定能有更好的性能),最终性能还需要通过严密的压测来决定 3、框架设计的角度 gateway具有更好的扩展性,并且其已经发布了2.0.0的RELESE版本,稳定性也是非常好的 4、性能 WebFlux 模块的名称是 spring-webflux,名称中的 Flux 来源于 Reactor 中的类 Flux。Spring webflux 有一个全新的非堵塞的函数式 Reactive Web 框架,可以用来构建异步的、非堵塞的、事件驱动的服务,在伸缩性方面表现非常好。使用非阻塞API。 Websockets得到支持,并且由于它与Spring紧密集成,所以将会是一个更好的 开发 体验。 Zuul 1.x,是一个基于阻塞io的API Gateway。Zuul已经发布了Zuul 2.x,基于Netty,也是非阻塞的,支持长连接,但Spring Cloud暂时还没有整合计划。
总结 总的来说,在微服务架构,如果使用了Spring Cloud生态的基础组件,则Spring Cloud Gateway相比而言更加具备优势,单从流式编程+支持异步上就足以让开发者选择它了。 对于小型微服务架构或是复杂架构(不仅包括微服务应用还有其他非Spring Cloud服务节点),zuul也是一个不错的选择。