一个稍微复杂的微服务集群服务一般都会有认证和鉴权的统一要求,而且子系统的实现由于出自不同团队或者部门,或者基于现实的客观因素考量,可能各自的技术栈都会不同,比如子系统A是java基于SpringBoot技术栈实现,子系统B是基于Python技术栈实现,子系统C也是基于Java开发但是提供的是Dubbo服务。还有就是系统可能跟外部服务集成,这些都是对一个系统的认证,鉴权,服务间调用,负载均衡带来的挑战。业界通用的做法是为微服务集群搭建一套网关服务,网关的实现有多种方式,比如通过Nginx就可以实现网关的功能,还有一些开源的网关项目比如Kong,Zuul,Spring Cloud Gateway等等,这里不对个个项目进行对比。本人调用下来Spring Cloud Gateway比较适合更复杂场景的网关服务实现,感兴趣的朋友可以自行调用对比其他网关项目。下面就讲一下通过Spring Cloud Gateway设计一个网关服务
系统架构
点击可放大
网关接收客户端https)请求或者后端服务间调用请求,在网关内部采用链式处理,每一个链路负责某一块功能,比如,认证链路,黑白名单控制,访问日志链路,请求转发链路,其中请求转发需要根据不同的协议分别实现转发功能。然后统一结果处理和异常处理
应用架构
点击可放大
网关服务架构中功能模块如下
- 动态路由管理
- 统一认证鉴权
- 协议转发
- 负载均衡
- 服务发现
- 用户日志
基于以上功能模块需求的整体架构设计如下
逻辑架构
点击可放大
网关采用分层设计从上到下依次需要实现的功能块分布在视图层,服务层和持久层
视图层:实现网关配置管理的用户交互视图,包括路由配置和多协议服务配置。 路由配置是指配置路由的转发规则,基于配置的路由转发主要服务于https)和websocket协议服务的转发。协议服务配置是指配置非Restful服务的服务接口配置,比如dubbo服务的生产者类,和生产者类方法,或者gRPC服务接口protobuf配置。基于rpc协议的dubbo服务的转发通过自定filter 实现,并通过泛化调用和服务提供方在SDK依赖上解耦
服务层:服务层提供两大功能模块。路由配置管理接口实现和动态路由转发实现。
- 路由配置管理:提供rest接口负责网关路由规则的配置管理,该接口只允许网关管理员访问,通过认证服务进行路由配置接口的认证和鉴权
- 动态路由:动态路由需要同时支持https),websocket,rpc接口的路由转发,其中https) 和websocket服务转发通过动态路由配置实现。rpc接口服务调用通过自定义转发实现,所有的服务转发都要经过以下公共filter
- Black Wither Lister Filter: 验证客户端是否在黑白名单中,黑名单的客户端会被拒绝访问,白名单的客户端会跳过认证和鉴权直接进入转发
- Auth Filter:认证过滤器会验证客户端身份,通过调用认证服务的接口验证客户端身份
- Permission Filter:权限过滤器会验证客户端是否有调用该接口的权限,通过调用认证服务的接口验证客户权限
- Log Filter:日志过滤器会记录客户端访问记录
- Error Filter:当准发异常时会通过Error Filter统一异常返回
除了异常每个路由都要经历的filter外,不同协议的转发还可以定制各自的filter实现转发,例如rpc协议服务的转发通过自定义的filter实现dubbo接口的泛化调用,https) 和 websocket协议的转发通过自定负载均衡过滤器确定转发地址
持久层:网关采用MySql持久化网关配置数据,路由管理服务接口通过Mybatis 框架集成数据库操作。同时网关采用redis作为分布式缓存。当应用启动时从Redis加载所有的路由配置装载路由对象(RouterLocator)。当通过路由管理服务接口使路由规则放生变化时,通过redis通知网关其他服务节点更新RouterLocator对象。
路由配置管理逻辑
点击可放大
路由配置的更改要及时更新到所有网关服务节点,这样匹配到该路由配置的请求在任意网关服务节点都可以进行代理转发,从而使使网关的可用性得到提高。我们可以采用API方式主动通知网关服务节点刷新配置,并且监听刷新配置结果,任意节点刷新失败将会被记录下来以便问题追踪处理
点击可放大
服务发现
网关在转发请求到代理服务之前需要通过负载均衡获取代理服务节点信息,负载均衡需要根据从服务发现结果查询节点信息。因此网关需要启动守护线程每秒主动探知代理服务健康状态保存最新的代理服务节点信息
点击可放大
模型抽象
Spring Cloud Gateway 是基于路由定义来实现定制化请求处理,路由定义(RouteLocator)有一个id,一个目标url,一组断言和一组过滤器定义,我们可以为不同类型的路由转发定义不同的RouteLocator,这些RouteLocator 会共享一些通用的过滤器,比如认证,黑白名单,鉴权。除此以外不同的RouteLocator 需要定义不同的断言和过滤器。以下是对于路由路由,过滤器,断言的抽象
路由
路由是指网关对于请求转发策略的总体定义,网关启动是会初始化所有定义的路由,网关数据模型必须包含以下数据
- 路由id:路由定义的唯一标识
- 路由uri:路由规则转发的目标地址其中不同协议的转发地址根据协议不同定义格式如下
协议类型 |
前缀 |
占位符1 |
占位符2 |
占位符3 |
占位符4 |
https) |
http://或者https:// |
目标服务域名 |
目标服务端口 |
目标服务名 |
目标服务接口地址 |
websocket |
ws:// |
目标服务域名 |
目标服务端口 |
目标服务名 |
目标服务websocket 地址 |
rpc |
dubbo:// |
目标服务域名 |
目标服务dubbo端口 |
目标服务类名 |
- 路由断言:断言决定满足何种情况执行路由规则,每个路由规则必须至少含有一个断言
- 过滤器:过滤器是指路由转发经过的切面行为,每个路由除了执行公共的过滤器之外可定义自己的过滤器
断言
断言是指路由规则执行必须满足的条件,断言规则必须含有以下信息
- 断言名称(name): 断言名称是指路由执行的何种断言,断言名称使用Spring Cloud Gateway内置的断言名称,包含以下类别,多种断言可以叠加作用在同一路由
断言名称 |
解释 |
Path |
通过请求地址匹配转发路径 |
Before |
指定某一时间点之前允许访问 |
After |
指定某一时间点之后可以访问 |
Between |
指定某一段时间允许访问 |
Cookie |
通过验证cookie匹配转发 |
Header |
通过验证header值匹配转发 |
Host |
通过匹配Host地址值匹配转发 |
Method |
通过匹配请求方法匹配转发 |
Query |
通过请求参数匹配转发 |
- 断言内容: 断言内容通过key value键值对保存断言执行内容
过滤器
过滤器是指路由执行经过的切面行为,Ops-Gateway 定位每个路由必须执行以下过滤器
- 黑白名单过滤器(BwListFilter):黑白名单校验,白名单放行,黑名单返回拒绝访问
- 认证过滤器(AuthFilter):对于请求身份校验,校验通过则继续执行下一过滤器,否则返回认证校验错误
- 权限过滤器(PermissionFilter):对于请求身份对于目标接口的权限校验,校验通过则继续执行下一过滤器,否则返回认证校验错误
- 日志过滤器(LogFilter):日志过滤器记录用户访问接口记录和结果
- 异常处理过滤器(ErrorFilter):当请求处理异常时统一处理异常返回
根据以上路由抽象网关在执行不同协议的请求转发时的流程如下:
dubbo服务请求
目标服务是Dubbo服务的请求,网关层需要集成zookeeper实现服务发现和负载均衡,请求dubbo服务的rest请求路径断言,当请求包含服务名称和方法时,请求需要路由到对应dubbo服务。网关服务只需添加对应路由配置和实现泛化调用dubbo服务的过滤器,无需依赖dubbo 服务sdk。dubbu服务转发使用到的断言类型有,因此dubbu服务转发需要实现泛化调用过滤器
点击可放大
Https)和websocket服务转发
目标服务https)web 服务或者websocket服务,请求经过请求路径断言(Path Predicate) 当断言为真时继续执行filer链,其中负载均衡过滤器决定路由请求到到指定服务地址,其中负载均衡断言需要自定义采用加权轮询法实现负载均衡
点击可放大