当前位置: 首页 > 科技观察

SpringCloud,这张架构图帮你梳理所有知识点

时间:2023-03-21 13:32:37 科技观察

思维导图认识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,用于匹配HTTP请求上的数据信息,如请求头信息、请求体信息等。如果一个请求的断言为真,那么它所关联的路由匹配成功,然后将请求交给该路由进行处理。Filter:用于修改某个路由的请求或响应的组件,必须实现SpringCloudGateway中的GatewayFilter接口,需要基于GatewayFilterFactory由具体的实现类构造。理解了以上三个概念,再看上图,可以清楚的看到SpringCloudGateway对客户端请求的处理过程,这有助于我们用好SpringCloudGateway。客户端请求首先通过GatewayHandlerMapping获取,然后根据断言匹配找到对应的路由。找到路由后,完成一组关联请求过滤器的处理方法,向目标URI对应的服务程序发出请求,获得服务响应。网关收到响应后,通过关联的响应过滤器的处理方法,响应也由GatewayHandlerMapping返回给客户端。另外需要注意的是,SpringCloudGateway的过滤器是有序执行的,执行的先后顺序是由顺序值的大小决定的。值越小,优先级越高,最先执行。如何实现API聚合在了解了SpringCloudGateway整体的请求处理流程之后,我们现在就来快速搭建一个基于SpringCloudGateway的API网关,看看在实际应用中需要注意哪些问题。需要注意的是,本文CloudGateway使用的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")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方法中,有两条路由定义了user-service和order-service的ids,并匹配请求的断言和真实的目标请求地址。这里路由的断言采用the路径匹配规则。只要原始请求地址匹配到相应的规则,路由就被匹配上了。不过SpringCloudGate也支持丰富的断言规则,比如主机匹配、请求Body字段匹配、请求数据匹配等,足以满足自定义路由断言的规则。由于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/*使用上面的配置,重启gateway应用也可以完成前面API方法的效果。由于将路由规则转移到配置文件中,因此会得到很大的改善。方便了API的管理,也提供了实现动态路由的可能。当然需要实现动态路由。除了路由配置之外,还需要额外的扩展来实现路由规则的动态刷新,这就涉及到了SpringCloudGateway的更高级的用法。或者参考网上其他实现资料。如何自定义过滤器为了处理API请求或者响应,SpringCloudGateway提供了过滤器组件来实现这个功能,并且内置了很多强大的功能。此外,还有两种类型的过滤器,全局过滤器和网关过滤器。对于全局过滤器,所有匹配路由的请求都会被全局过滤器处理;网关过滤器只有在指定路由上显示时才会起作用。关于。SpringCloudGateway默认的全局过滤器有8个:ForwardRoutingFilterLoadBalancerClientFilter(弃用)ReactiveLoadBalancerClientFilterWebClientHttpRoutingFilterNettyWriteResponseFilterRouteToRequestUrlFilterWebsocketRoutingFilterGatewayMetricsFilter而网关过滤器就更多了,并且由对应工厂类来构造,比如用于熔断的HystrixGatewayFilterFactory,用于RequestRateLimiterGatewayFilterFactoryforcurrentlimiting,ModifyRequestBodyGatewayFilterFactoryformodifyingrequestdata,etc.Ofcourse,developersarealsosupportedtodefinetheirownfilters.Let'sfirstlookathowtocustomizeaglobalfilter.Thecodeimplementationisrelativelysimple:@ComponentpublicclassCustomGlobalFilterimplementsGlobalFilter,Ordered{privateLoggerlog=LoggerFactory.getLogger(MyAuthFilterFactory.class);@OverridepublicMonofilter(ServerWebExchangeexchange,GatewayFilterChainchain){log.info("Executeacustomfilter");returnchain.filter(exchange);}@OverridepublicintgetOrder(){return-1;}}Thiswilladdaglobalfiltertoallroutes.与全局过滤器的定义不同,网关过滤器必须在指定路由上声明才能生效。参考官方内置的网关拦截器,自定义一个简单的网关拦截器工厂进行授权,如下:@ComponentpublicclassMyAuthGatewayFilterFactoryextendsAbstractGatewayFilterFactory;}@OverridepublicGatewayFilterapply(Configconfig){returnnewGatewayFilter(){@OverridepublicMonofilter(ServerWebExchangeexchange,GatewayFilterChainchain){ServerHttpRequestrequest=exchange。getRequest();MultiValueMapqueryParams=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(媒体Type.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=auth}ifto}-服务路由,需要在application.properties配置文件中添加如下配置:spring.cloud.gateway.routes[1].filters[0].name=MyAuth这里的name需要和Spring的MyAuthGatewayFilterFactory类的MyAuth保持一致云网关会自动拼接AuthGatewayFilterFactory来查找相应的网关过滤器。如果没有找到则启动失败并抛出异常:java.lang.IllegalArgumentException:UnabletofindGatewayFilterFactorywithnameMyAuth2配置完成后,重启网关应用程序。这是用原来的方法请求用户服务,一直无法正常访问。只会返回验证授权失败的信息。必须使用http://localhost:8080/user/get?from=system访问才能成功获取到数据,说明定义的授权拦截器已经起作用了。这里我们同时自定义了全局拦截器和网关拦截器。通常,我们会在网关拦截器上展开。自定义,也与内置过滤器结合使用。