发布网友 发布时间:2024-10-02 13:08
共1个回答
热心网友 时间:2024-10-03 20:43
认识SpringCloudGatewaySpringCloudGateway是一款基于Spring5,ProjectReactor以及SpringBoot2构建的API网关,是SpringCloud微服务生态的主要组件之一。SpringCloudGateway主要负责接口请求的路由分发,并且支持对请求的安全验证,流量监控和流量控制等扩展操作。另外值得一提的点是,SpringCloudGateway默认采用了非阻塞I/O模型实现请求路由的分发。对于处理一些I/O耗时长的请求上,相比其他一样用Java编写采用的同步阻塞I/O模型的网关性能更高,处理的并发数也更高,避免了因为I/O阻塞(网络调用,数据库操作等)导致线程空闲下来,仍能继续处理响应其他请求。
SpringCloudGateway适用场景作为API网关,SpringCloudGateway所提供的功能也很强大,集成了对负载均衡,动态路由,访问控制,限流熔断,埋点监控等功能的支持。如果现有的微服务体系是以Java生态甚至Spring生态为基础的,那么就十分适合使用SpringCloudGateway作为API应用网关了,让聚合管理多个微服务API,对外进行统一的输出。
同时秉承Spring家族的传统,SpringCloudGateway也旨在提供一个简单,且高效的方式来进行API路由和请求关注点的扩展,对于已经熟悉Spring或者SpringBoot的开发者来说,SpringCloudGateway学习成本并不高,利用底层框架所带的注解驱动和自动化配置等特性,使用和扩展起来难度都不算高。
快速上手SpringCloudGateway利用SpringCloudGateway能快速搭建一个API网关,但在这之前,先介绍一下使用SpringCloudGateway框架所涉及的一些专用概念,来加深对SpringCloudGateway的认识,方便后面的使用。
路由:是SpringCloudGateway中基础的组件,通常由一个id标识,目标URI,以及一系列断言(Predicate)和过滤器组成。
断言(Predicate):是Java8函数库的Predicate对象,具体类型为Predicate<ServerWebExchange>,用于匹配HTTP请求上数据信息,如请求头信息,请求体信息。如果对于某个请求的断言为true,那么它所关联的路由就算匹配成功,然后将请求给这个路由处理。
过滤器:用于某一个路由的请求或者响应进行修改的组件,在SpringCloudGateway都要实现GatewayFilter接口,并且需要由基于GatewayFilterFactory具体实现类构造。
认识上面三个概念之后,再看上图所示,就能清楚看出SpringCloudGateway对客户端请求的处理过程了,这帮助我们用好SpringCloudGateway帮助很大。
客户端请求首先被GatewayHandlerMapping获取,然后根据断言匹配找到对应的路由
找到路由后,走完所关联的一组请求过滤器的处理方法,请求到目标URI所对应的服务程序,获得服务响应。
网关收到响应后,通过关联的响应过滤器的处理方法后,同样由GatewayHandlerMapping返回响应给客户端。
额外需要注意的是SpringCloudGateway的过滤器是有序执行的,统一以order值的大小决定执行顺序,值越小优先级越高,就越先执行。
如何实现API聚合认识SpringCloudGateway整体处理请求过程之后,我们现在就快速构建一个基于SpringCloudGateway的API网关,看看在实际应用中还需要注意的哪些地方,需要注意的是本文所使用的SpringCloudGateway属于最新的里程碑版本2.2.3,对应SpringBoot版本为2.3.1,并且SpringCloud版本为Hoxton.SR6。利用SpringInitializr,选择对应的版本和依赖后快速新建一个项目spring-cloud-gateway-quick-start,并且为了实现请求的路由,表现网关的效果,再分别新建用户服务应用demo-userservice和订单服务应用demo-orderservice,各自提供一个可调用API接口。
用户服务暴露8071端口,提供/user/get接口:
//demo-userservice项目@RestController@RequestMapping("/user")publicclassUserServiceController{@RequestMapping("/get")publicUserget(){returnUser.mock();}}类似,订单服务暴露8061端口,提供/order/get接口:
//demo-orderservice项目@RestController@RequestMapping("/order")publicclassOrderServiceController{@RequestMapping("/get")publicOrderget(){returnOrder.mock();}}接下来要通过SpringCloudGateway将两个服务接口聚合在spring-cloud-gateway-quick-start项目中,首先来看下利用SpringCloudGatewayAPI方式的实现:
@SpringBootApplicationpublicclassDemogatewayApplication{publicstaticvoidmain(String[]args){SpringApplication.run(DemogatewayApplication.class,args);}@BeanpublicRouteLocatorcustomRouteLocator(RouteLocatorBuilderbuilder){returnbuilder.routes().route("user-service",r->r.path("/user/*").uri("http://localhost:8071")).route("order-service",r->r.path("/order/*").uri("http://localhost:8061")).build();}}接下来要通过SpringCloudGateway将两个服务接口聚合在spring-cloud-gateway-quick-start项目中,首先来看下利用SpringCloudGatewayAPI方式的实现:
上述代码就已经实现API路由的功能,是不是很快速,同时启动spring-cloud-gateway-quick-start和其他服务应用,就可以统一通过网关应用访问用户服务和订单服务了:
one@192~%curlhttp://localhost:8080/user/get{"id":4720186416534735290,"token":"86b6118d-7dc6-4d30-a5f3-3d5fc6348f9a"}one@192~%curlhttp://localhost:8080/order/get{"id":5832646761962425508,"title":"MyOrder"}回到API实现的代码,DemogatewayApplication#customRouteLocator方法中定义了两个id分别为user-service和order-service的路由,并且设置了匹配请求的断言,以及真正目标请求地址。这里路由的断言采用了路径匹配的规则,只要原始请求地址符合对应的规则就算匹配到此路由,但SpringCloudGate还支持丰富的断言规则,如主机匹配,请求体字段匹配,请求数据匹配等等,足以满足定制路由断言的规则了。
由于使用API就是硬编码方式将路由规则定义在程序里了,这样做扩展性很差,也不好维护。于是更推荐另外一种实现方式:配置化。来看下要实现相同功能,在application.properties里该如何配置:
spring.cloud.gateway.routes[0].id=order-servicespring.cloud.gateway.routes[0].uri=http://localhost:8061spring.cloud.gateway.routes[0].predicates[0].name=Pathspring.cloud.gateway.routes[0].predicates[0].args[pattern]=/order/*spring.cloud.gateway.routes[1].id=user-servicespring.cloud.gateway.routes[1].uri=http://localhost:8071spring.cloud.gateway.routes[1].predicates[0].name=Pathspring.cloud.gateway.routes[1].predicates[0].args[pattern]=/user/*使用上面的配置,重启网关应用,同样能完成之前API方式的效果,由于路由规则转移到了配置文件中,就大大方便对API的管理,为实现动态路由也提供了可能。当然需要实现动态路由,除了路由配置,还需要进行额外的扩展实现路由规则的动态刷新,涉及SpringCloudGateway更高级的用法,本文就不再详细赘述了,可以等后续进阶使用和分析的文章或者参考网上其他实现资料。
如何自定义过滤器为了能对API的请求或者响应处理,SpringCloudGateway提供过滤器组件来实现这一功能,并且内置了很多功能强大。另外过滤器分两类,全局过滤器和网关过滤器,对于全局过滤器,所有匹配到路由的请求处理时都会经过全局过滤器处理;而网关过滤器只有显示在指定路由上时才会起到左右。
SpringCloudGateway默认的全局过滤器有8个:
ForwardRoutingFilter
LoadBalancerClientFilter(弃用)
ReactiveLoadBalancerClientFilter
WebClientHttpRoutingFilter
NettyWriteResponseFilter
RouteToRequestUrlFilter
WebsocketRoutingFilter
GatewayMetricsFilter
而网关过滤器就更多了,并且由对应工厂类来构造,比如用于熔断的HystrixGatewayFilterFactory,用于限流的RequestRateLimiterGatewayFilterFactory,用于修改请求数据的ModifyRequestBodyGatewayFilterFactory等等,当然也支持开发者进行定义自己的过滤器。
首先来看下如何自定义一个全局过滤器,代码实现比较简单:
@ComponentpublicclassCustomGlobalFilterimplementsGlobalFilter,Ordered{privateLoggerlog=LoggerFactory.getLogger(MyAuthFilterFactory.class);@OverridepublicMono<Void>filter(ServerWebExchangeexchange,GatewayFilterChainchain){log.info("执行自定过滤器");returnchain.filter(exchange);}@OverridepublicintgetOrder(){return-1;}}这样就可以为所有路由添加一个全局的过滤器了。不同于全局过滤器的定义,网关过滤器必须在指定路由上进行申明才能生效,参考官方内置的网关*,自定义一个用于授权的简易网关*工厂如下:
@ComponentpublicclassMyAuthGatewayFilterFactoryextendsAbstractGatewayFilterFactory<MyAuthGatewayFilterFactory.Config>{privateLoggerlogger=LoggerFactory.getLogger(MyAuthGatewayFilterFactory.class);publicMyAuthGatewayFilterFactory(){super(Config.class);}@OverridepublicGatewayFilterapply(Configconfig){returnnewGatewayFilter(){@OverridepublicMono<Void>filter(ServerWebExchangeexchange,GatewayFilterChainchain){ServerHttpRequestrequest=exchange.getRequest();MultiValueMap<String,String>queryParams=request.getQueryParams();Stringfrom=queryParams.getFirst(config.getAuthKey());ServerHttpResponseresponse=exchange.getResponse();logger.warn("校验授权开始");if(config.getAuthValue().equals(from)){logger.warn("校验授权成功");returnchain.filter(exchange);}else{logger.warn("校验授权失败");response.setStatusCode(HttpStatus.OK);response.getHeaders().setContentType(MediaType.valueOf("text/html;charset=utf-8"));DataBufferwrap=response.bufferFactory().wrap(config.getAuthFailMsg().getBytes(Charset.forName("UTF-8")));returnresponse.writeWith(Flux.just(wrap));}}};}publicstaticclassConfig{privateStringauthKey="from";privateStringauthValue="system";privateStringauthFailMsg="授权失败";publicStringgetAuthKey(){returnauthKey;}publicvoidsetAuthKey(StringauthKey){this.authKey=authKey;}publicStringgetAuthValue(){returnauthValue;}publicvoidsetAuthValue(StringauthValue){this.authValue=authValue;}publicStringgetAuthFailMsg(){returnauthFailMsg;}publicvoidsetAuthFailMsg(StringauthFailMsg){this.authFailMsg=authFailMsg;}}}如果要在user-service路由下使用,需要在application.properties配置文件添加如下配置:
spring.cloud.gateway.routes[1].filters[0].name=MyAuth这里的名称就需要跟MyAuthGatewayFilterFactory类的MyAuth保持一致,SpringCloudGateway会自动拼接上AuthGatewayFilterFactory去查找对应的网关过滤器,没有找到就会导致启动失败,抛出异常:
java.lang.IllegalArgumentException:UnabletofindGatewayFilterFactorywithnameMyAuth2配置完对网关应用进行重启,这是使用原来的方式去请求用户服务,已经无法正常访问,只会返回校验授权失败的信息,必须以http://localhost:8080/user/get?from=system方式访问才能成功获取到数据,说明定义的授权*已经起了作用。
这里我们就将全局*和网关*都实现了自定义,通常情况我们都会在网关*上进行扩展定制,也结合内置的过滤器使用。
最后将完整的实现代码上传到Gitlab:https://github.com/wrcj12138aaa/spring-cloud-gateway-quick-start,感兴趣的朋友也可以参考下。
作者:程序员闻人