SpringCloud --- gateway

    技术2022-07-12  64

    gateway

    介绍搭建环境gateway功能路由配置文件配置代码配置 断言过滤器 总结

    介绍

    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功能

    gateway有个强大的几个功能

    Route(路由):这是网关的基本构建模块。它由一个ID,一个目标url,一组断言和一组过滤器定义。如果断言是真,则路由匹配Predicate(断言):输入类型是一个ServerWebExchange。我们可以使用它来匹配来自Http请求的任务内容,例如Headers或参数Filter(过滤器):Gateway中的filter分为两种类型的Filter,分别是GatewayFilter和Global Filter。这种过滤器Filter将会对请求和响应进行修改处理

    路由

    路由和断言是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/** #断言,路径相匹配的进行路由

    在配置文件中配置的话,就是按照我们原来案例中来配置的

    代码配置

    在代码中配置的话,是利用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(); }

    过滤器

    生命周期 gateway的生命周期比zuul的就简单很多了,跟普通的过滤器相同,两种在 pre 在业务逻辑之前post 在业务逻辑之后 种类 种类为为两种 单一 GatewayFilter全局 GlobaFilter 实现 配置文件实现 spring: application: name: gateway-application cloud: # Spring Cloud Gateway 配置项,对应 GatewayProperties 类 gateway: # 路由配置项,对应 RouteDefinition 数组 routes: - id: user-service # 路由的编号 uri: lb://user-service # 路由到的目标地址,只能到域名部分 https://blog.csdn.net/dear_little_bear predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 - Path=/user/** filters: - StripPrefix=1 - AddRequestHeader=X-Request-red, blue # 请求头的属性和属性值 代码实现 全局拦截器 @Component @Slf4j public class MyLogGateWayFilter implements GatewayFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("*********come in MyLogGateWayFilter: "+new Date()); //从上下文中请求头查询到 String username = exchange.getRequest().getQueryParams().getFirst("username"); if(StringUtils.isEmpty(username)){ log.info("*****用户名为Null 非法用户,(┬_┬)"); //根据上下文回应 exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);//给人家一个回应 return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override //返回拦截器的优先级 public int getOrder() { return 0; } }

    单一拦截器

    @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也是一个不错的选择。

    Processed: 0.019, SQL: 9