在《微服务网关——需求篇》中,我们讨论了微服务网关的需求,本文将对微服务网关进行设计。考虑到实际情况的差异,这里实际给出的是设计选项,最终设计基于实际场景来确定。
一般情况下,服务对外提供的是RESTful接口,所以一般路由模块根据请求的host, url等规则转发到指定的服务。
考虑到路由规则需要频繁的修改发布,为了发布的便利性,考虑针对规则实现热发布。有几种实现方式:
基于数据库
即将路由规则配置到数据库中,当网关收到请求后,从数据库中查询规则进行规则匹配。根据匹配到的规则进行路由。
考虑到性能,可以缓存规则,例如缓存到redis中。当修改配置后,需要将修改的数据刷到缓存中。
此方式需要实现数据库与缓存的同步逻辑,提供操作界面,需要一定的开发量。
基于配置文件
即将路由规则配置到配置文件中,网关启动时直接加载即可。普通的配置文件方式无法动态处理配置,每次修改后都需要启动网关,比较麻烦。对于微服务架构来说,一般会有配置服务器,可以基于配置服务器来实现配置的实时生效。
相对于前一种方法,可以基于微服务基础设施来实现,降低了一定的开发量。
一般负载均衡算法有:
对于微服务场景来说,优先选择源地址hash:
聚合服务有两种方案:
考虑到GraphQL的学习成本,以及聚合服务的量不是很多,优先考虑在网关中直接进行编码的方式。
目前大部分系统采用的都是基于RBAC的认证授权。RBAC模型是目前主流权限控制的理论基础。
RBAC(Role-Based Access Control)即:基于角色的权限控制。通过角色关联用户,用户关联权限的方式间接赋予用户权限。如下图:
RBAC模型可分为:RBAC0、RBAC1、RBAC2、RBAC3四种。其中RBAC0是基础,也是最简单的,相当于底层逻辑,RBAC1、RBAC2、RBAC3都是以RBAC0为基础的升级。具体内容请自行Google。
考虑互联网项目对用户角色的区分没有特别的严格(相对后台管理系统),RBAC0模型就可以满足常规的权限管理系统的需求,所以选择基于RBAC0来实现认证与鉴权。
对于Java来说,主流的认证与鉴权框架是SpringSecurity和Shiro,考虑集成的便利性,选择SpringSecurity作为认证鉴权框架。
一般的流量控制模式有:
对于微服务场景来说,控制速率是比较合适的流量控制方案。通常情况下,使用令牌桶算法来实现访问速率的控制,常用的令牌桶算法有两种:
流量控制算法在确定后也是基本不需要变化的,所以对于热部署的需求不是必要的。
另外流量控制可以前置,放到接入层来处理,一般的网络接入服务,如nginx是支持流量控制的。如果前期对流量控制没有太多的定制化需求,可以考虑基于nginx来进行处理。
服务熔断的实现思路:
考虑到有较成熟的开源项目,推荐直接使用开源项目来处理。
一种服务升降级的方案可以基于阻塞队列来实现:
考虑到有较成熟的开源项目,推荐直接使用开源项目来处理。
考虑到网关是集群化部署,所以优先使用集中式缓存方式,即网关中所有需要缓存的数据都集中进行缓存。使用常用的分布式缓存中间件即可,例如redis。
基于缓存的网关工作步骤:
此处需要注意缓存常见问题:缓存雪崩、缓存击穿、缓存穿透,需要针对性的做好处理。
对于服务重试至少需要提供两个功能:
对于配置来说,需要配置请求的超时时间、单次请求的超时时间、重试次数,注意单次请求的超时时间*重试次数要小于请求的超时时间,否则会影响服务重试逻辑。同时,也需要考虑配置的动态生效,以保障网关的稳定性。
对于执行来说,根据配置的次数来进行处理即可。
逻辑实现并不复杂,不过考虑到有较成熟的开源项目,推荐直接使用开源项目来处理。
应用日志记录遵循项目日志规范。对于访问日志来说,前期可以考虑在接入层实现,例如通过nginx的访问日志来实现对访问请求的记录。待后期有特定需求后再进行定制化。
对于管理功能,由于是非核心需求,前期可以暂不考虑。
传统的基于线程的并发模型(Thread-based concurrency),为每一个请求分配一个线程或进程。这种模型编程简单,可以将处理一个完整请求的代码编写在一个代码路径中。这种模型的弊端是,随着线程(进程)数的上升,操作系统在这些线程(进程)之间的频繁切换,将急剧降低系统的性能。
网关作为整个系统的入口,需要处理大量的请求,故基于线程的并发模型并不适用。需要使用Reactor模型来进行处理。
关于Reactor模型请参考《EDA风格与Reactor模式 》
目前常用的IO框架Netty可通过配置实现上述Reactor模型,如自行开发网关,可基于Netty进行开发。
高可用包含了前面所说的流量控制、熔断和服务升降级。除了这些功能外,还需要提供服务的优雅上下线功能以及自身的优雅下线功能。
对于使用Java开发的项目来说,由于JVM的特性,一般需要一个预热的过程,即服务启动后,需要访问一段时间后,服务才会达到最佳状态。如果服务刚启动就接收高强度的请求,可能会导致响应时间过长、服务负载过高的问题,严重时可能导致服务被瞬间压垮。为了避免这种情况,网关可以考虑支持Slow Start特性。即经过一段时间,逐渐把请求压力增加到预设的值。
另外,当一个服务下线时,不能直接关闭服务,需要先关闭该服务的对外接口,当该服务处理完所有正在处理的请求并返回后,方可关闭服务。
对于网关自身也类似,当网关需要关闭时,不是直接结束网关进程,而是先关闭监听套接字,但是继续为当前连接的客户提供服务,当所有客户端的服务都完成后,再把进程关闭。
网关对请求的处理,可以分为:
对于此类请求的扩展,主要是基于过滤器/拦截器来实现。
一般拦截器可以分为两大类:
一般来说,先执行全局拦截器,再执行为了业务逻辑编写的拦截器。不过,为了灵活性,网关最好能提供一种机制,可以较容易地调整拦截器的执行顺序。最简单的一种方法,就是给每个拦截器定义一个优先级,网关按优先级顺序依次调用各拦截器。
同时,网关也需要能方便的动态配置拦截器,即动态配置拦截器的开启与关闭、以及配置哪些拦截器针对哪些请求生效。可以通过两种方式来处理:
网关层为保证高可用,易于伸缩,快速启动,需要设计成无状态的(微服务里的绝大部分服务都需要设计为无状态的)。但是,由于网关需要处理用户的认证与鉴权,势必与用户状态有关系,此处需要解耦用户状态关系。目前一般做法是基于token来进行处理:
通过此方式,保证了网关的无状态,继而保证网关的快速扩容。
对于微服务监控目前市面上有较完善的项目,例如SkyWalking,Pinpoint。可以基于这些项目快速搭建一个服务监控系统。对于定制化需求,可以进行二次开发。
同时可以基于ELK对日志进行收集分析,方便快速的定位问题。
出处:https://www.toutiao.com/i6901247551744557576