问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

SpringCloudGateway快速上手实现API聚合及自定义过滤器

发布网友 发布时间:2024-10-02 13:08

我来回答

1个回答

热心网友 时间:2024-10-03 20:43

认识SpringCloudGateway

SpringCloudGateway是一款基于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,感兴趣的朋友也可以参考下。

作者:程序员闻人
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
国外人手一个约会软件?真实情况是怎样的? iPhone T版官解教程(实验版2.0) 神舟 战神K550D-i7 D1 14.0和联想小新700电竞版 ISK 15.6好吗? 2022万圣节贺词寄语祝福语_万圣节经典的祝福语大全3篇 孩子们的万圣节祝福语 什么是rec黄金树项目大苗培育?这种REC黄金树培育方法是怎样的? 家用骐达,八年七万多公里,正常保养,最近天气炎热,空调如果晚上开车就... 新骐达没开空调怎么又电子扇的声音 ...发现左边图标的地方有一条很细的竖着的黑线,怎么回事啊? 戴尔电脑在哪更改u盘图标戴尔笔记本怎么设置u盘为第一启动项 玩游戏对学习的影响有多大? 全网最全EdgeMesh Q&amp;A手册 ElasticSearch 学习笔记 初中没有学好文化基础想了解 湖南汽修学校怎么样? I9220开机后变砖了,开机后只要一动屏幕就定在那了,不能动。 三星i9220 运行软件黑屏 有声音怎么回事 干燥综合征患者如何治疗 干燥综合征怎么根治 干燥综合征的早期治疗方法 天猫收单礼金规则是首单有时限、可以和其他券叠加使用、付款再退款是... 天猫赔付红包使用规则? 浦发银行刷卡机最多一次可以转多少钱 MySQLpas数据库简介及应用场景分析mysqlpas数据库 MySQL多实例的灵活应用与管理技巧mysql不同的实例 MySQL三种用户及其特点简介mysql三种用户 MySQL中多用户管理mysql中不同的用户 很多人对腰十分不友好,有哪些伤害腰的错误习惯? 男人为什么喜欢摸女人的腰 现代新工天珠有能量吗? 关于稀有气体 手机游戏对学习的影响? 我用的是网件的NetGear JWNR 2000 路由器,目前发现有时会出现LAN口指示... 微信朋友圈怎么看对方屏蔽了自己吗? 血罗多肉怎么养 血罗多肉养殖方法 为什么网件 JWNR2000 300M 无线宽带路由器在设置连接无线网是只显示下图... 办理公司营业执照需要准备什么资料? 进账单盖什么章 开转帐支票给报关公司,请问填写进帐单后要加盖本公司的章吗_百度... 经费申请报告格式要求及内容 公文写作格式要求及常见错误 网癔症什么叫网络成瘾 ...究竟怎样才算是有网瘾?我有时会一次上五六小时算吗? 征集淘宝店铺名称。主要销售各种果酱(草莓酱、蓝莓酱、芒果酱等)名称要... 淘宝♀草莓小镇♂手绘店怎么样 ...结果交成了城乡居民社会养老保险300元可以退吗?支付宝 金刚砂和环氧地坪漆的区别 泰山币一卷多少个 360娴忚�鍣ㄧ湡镄勮兘鍏岖柅链ㄩ┈鍚楋紵涓嶅紑闅旗�妯% 我的5200可以上移动梦网,但是上别的网就会显示未知回应,请大家帮帮忙... ...没有开任何药,但是这两天耳朵里有点疼,还要到医院吗?