Spring Cloud Gateway应用篇(十三)
一、概述
在微服务架构中,每个服务都是一个可以独立开发和运行的组件,而一个完整的微服务架构由一系列独立运行的微服务组成。其中每个服务都只会完成特定领域的功能,比如订单服务提供与订单业务场景有关的功能、商品服务提供商品展示功能等。各个微服务之间通过轻量级通信机制 REST API 或者 RPC 完成通信。 微服务之后在某些层面会带来一定的影响,比如,一个用户查看一个商品的详情,对于客户端来说,可能需要调用商品服务、评论服务、库存服务、营销服务等多个服务来完成数据的渲染在这个场景中,客户端虽然能通过调用多个服务实现数据的获取,但是会存在一 些问题,比如:
- 客户端需要发起多次请求,增加了网络通信的成本及客户端处理的复杂性。
- 服务的鉴权会分布在每个微服务中处理,客户端对于每个服务的调用都需要重复鉴权。
- 在后端的微服务架构中,可能不同的服务采用的协议不同,比如有 HTTP、RPC 等。客户端如果需要调用多个服务,需要对不同协议进行适配
二、微服务网关的作用
所以,我们可以在微服务之前增加一个前置节点,这个节点就是网关,网关就是一个网络连接到另一个网络的“关口”。也就是网络。而在微服务架构中,它不仅仅只是一个网络互连的一个关口,还有更多的作用,以前面分析的这个场景为例,增加网关之后所有请求的下发都由网关下发;对于商品详情展示的场景来说,增加了 API 网关之后,在 API 网关层可以把后端的多个服务进行整合,然后提供一个唯一的业务接口,客户端只需要调用这个接口即可完成数据的获取及展示。在网关中会再去消费后端的多个微服务进行统一的整合,给客户端返回一个唯一的响应。去消费后端的多个微服务进行统一的整合,给客户端返回一个唯一的响应。
- 针对所有请求进行统一鉴权、限流、熔断、日志。
- 协议转化。针对后端多种不同的协议,在网关层统一处理后以 HTTP 协议对外提供服务。
- 用过 Dubbo 框架的应该知道,针对 Dubbo 服务还需要提供一个 Web 应用来进行协议转化。
- 统一错误码处理。
- 请求转发,并且可以基于网关实现内外网隔离
2.1、网关的作用
- 性能:API高可用,负载均衡,容错机制。
- 安全:权限身份认证、脱敏,流量清洗,后端签名(保证全链路可信调用),黑名单(非法调用的限制)。
- 日志:日志记录(spainid,traceid)一旦涉及分布式,全链路跟踪必不可少。
- 缓存:数据缓存。
- 监控:记录请求响应数据,api耗时分析,性能监控。
- 限流:流量控制,错峰流控,可以定义多种限流规则。
- 灰度:线上灰度部署,可以减小风险。
- 路由:动态路由规则。
三、服务网关的要求
从上面的案例来看,网关成了所有流量的入口,那么对于这样一个角色来说,它需要在某些方面有很高的要求
- 稳定性,
- 安全性,防止恶意请求,以及保障数据传输的安全性
- 高性能、可用性,
- 网关作为所有流量的入口,那么对于性能这块的要求就非常高了,因为一旦网关的性能出现瓶颈,就算后端的服务性能再高,意义也不大
- 网关必须要支持集群部署,这个是分布式架构的基本要求。否则网关服务挂掉就会导致整个系统不可用
- 扩展性,可维护性,对于定制化需求方面,如何实现可扩展;
常见的网关方案
- OpenResty(Nginx+lua)
- Kong,是基于openresty之上的一个封装,提供了更简单的配置方式。 它还提供了付费的商业插件
- Tyk(开源、轻量级),Tyk 是一个开源的、轻量级的、快速可伸缩的 API 网关,支持配额和速度限制,支持认证和数据分析,支持多用户多组织,提供全 RESTful API。它是基于go语言开发的组件。
- Zuul,是spring cloud生态下提供的一个网关服务,性能相对来说不是很高
- Spring Cloud Gateway,是Spring团队开发的高性能网关
网关选型
- 部署和维护成本
- 开源还是闭源
- 是否私有化部署
- 功能是否满足当前需求
- 社区资料的完善以及版本迭代和功能维护
四、Spring Cloud Gateway的核心概念
- Route 路由,它是网关的基础元素,包含ID、目标URI、断言、过滤器组成,当前请求到达网关时,会通过Gateway Handler Mapping,基于断言进行路由匹配,当断言为true时,匹配到路由进行转发
- Predicate,断言,学过java8的应该知道这个函数,它可以允许开发人员去匹配HTTP请求中的元素,一旦匹配为true,则表示匹配到合适的路由进行转发
- Filter,过滤器,可以在请求发出的前后进行一些业务上的处理,比如授权、埋点、限流等。
它的整体工作原理如下。
其中,predicate就是我们的匹配条件;而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了。客户端向 Spring Cloud Gateway 发出请求,如果请求与网关程序定义的路由匹配,则该请求就会被发送到网关 Web 处理程序,此时处理程序运行特定的请求过滤器链。过滤器之间用虚线分开的原因是过滤器可能会在发送代理请求的前后执行逻辑。所有 pre 过滤器逻辑先执行,然后执行代理请求;代理请求完成后,执行 post 过滤器逻辑
五、应用实战
新建一个spring-cloud-gateway服务
在spring-cloud-gateway服务中导入以下包
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
然后在配置文件中配置如下
server: port: 9100 spring: application: name: spring-cloud-gateway cloud: gateway: routes: - predicates: - Path=/service/** filters: #过滤 - StripPrefix=1 uri: http://localhost:8080/ eureka: instance: hostname: localhost client: serviceUrl: defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/ #指向服务注册中心的地址
启动项目通过网关访问业务服务,发现可以直接通过网关的端口发起访问
5.1、断言
https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#the-after-route-predicate-factory
这是官方提供的11种断言方法,有兴趣可以按官网的去配置玩下,cloud整个体系配置相对来说很简单的
下面就写一个关于常用的Cookie配置下;
5.2、自定义断言
如果官网提供的断言不满足自己的要求,官网也支持自定义断言;自定义断言要继承AbstractRoutePredicateFactory这个抽象工厂,我们自定义的断言类名后缀必须是RoutePredicateFactory;自定义也很简单也就是看别人怎么写的然后抄就完了
然后把之前测试工具的Cookies删除,调用成功
5.3、GatewayFilter
Factories
官网有说明这玩意其实就是一个路由过滤器工厂。官网提供了31种,建议大家看下;下面还是和前面一样先抄下源码自定义一个看怎么玩,随便选一个过滤器,例如
Retry GatewayFilter
Factory这个过滤器
类命名要求和以前一样后缀要相同
然后加上我们自定义的过滤器,随便找个地方加上就行
然后访问浏览器
再看控制台,这里我没写过滤逻辑,但进来了说明如果官网的过滤逻辑用的不爽或者公司想定义一个自己的规则,那就可以玩了
前面玩完了GlobalFilter现在就玩下RouteFiler;前面路由是通过HTTP+端口号的方式绑定业务服务的,这种方式是不可取的,第一端口改变了,那网关上配置也跟着改,第二个问题就是,你这么玩不能做负载均衡呀,能不能像zuul像配置服务名就能找到业务服务,还真可以,配置如果下
然后访问服务,如果是多个节点可以发现做负载了
最后再来说下比较常用的另一个东西:限流器;https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#the-requestratelimiter-gatewayfilter-factory操作官方说的很清楚
然后自己写个类写限制规则
因为电脑重装后我虚拟机都清零了,所以redis都没了,今天懒得搞环境了,如果我记得没错的话,在浏览器上访问http://localhost:9100/requestratelimiter/user这个路径过快的话,令牌不够会报429的错,有兴趣的可以自己验证下
5.3、动态路由
https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#actuator-api
在现在的getway中我是把所有路由规则写死在配置文件中,但是在有些业务场景中,需要将路由规则存到配置中心,或者是存在数据库中,这个需求就引出了动态路由;这个在getway中有提供接口,但需要我们自己实现;要想实现这功能也要导入下面包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
然后在网关的配置中加入以下内容,看过前面的应该知道这配置作用这里就不说明了
management: endpoints: web: exposure: include: "*"
搞完后看官网,官网有说怎么看getway的网关路由的元数据信息
里面也有说明创建和删除路由规则的操作说明
那按官网来玩下,按以下配置然后执行下
然后按官网要求刷新下
再访问浏览器,刚刚添加的路由上去了
虽然这样可以添加和删除路由,但现在这操作是存在内存中,如果服务重启的话就都白搞了,所以要想持久化保存就要换个方法;其实我们刚刚所有操作的接口都在GatewayControllerEndpoint类中能找到,看它继承的AbstractGatewayControllerEndpoint类,里面有个触点RouteDefinitionWriter
通过类关系图我们可以找到它的最终实现InMemoryRouteDefinitionRepository类,这个类里面的操作是基于内存的定义,看到这里,灵感应该来了,你竟然能实现基于内存的定义,那我也来继承自己造个轮子来基于数据库、缓存不就完了呗
下面来扩展下RouteDefinitionRepository,下面操作完成后,后面增加的信息就存在缓存服务器里面了