前面,我们讲了Sidecar和Service Mesh这两种设计模式,它们都是在不侵入业务逻辑的情况下,把控制面(control plane)和数据面(data plane)的处理解耦分离。但是这两种模式都让我们的运维成本变得特别大,因为每个服务都需要一个Sidecar,这让本来就复杂的分布式系统的架构就更为复杂和难以管理了。

在谈Service Mesh的时候,我们提到了Gateway。我个人觉得并不需要为每个服务的实例都配置上一个Sidecar。其实,一个服务集群配上一个Gateway就可以了,或是一组类似的服务配置上一个Gateway。

这样一来,Gateway方式下的架构,可以细到为每一个服务的实例配置一个自己的Gateway,也可以粗到为一组服务配置一个,甚至可以粗到为整个架构配置一个接入的Gateway。于是,整个系统架构的复杂度就会变得简单可控起来。


这张图展示了一个多层Gateway架构,其中有一个总的Gateway接入所有的流量,并分发给不同的子系统,还有第二级Gateway用于做各个子系统的接入Gateway。可以看到,网关所管理的服务粒度可粗可细。通过网关,我们可以把分布式架构组织成一个星型架构,由网络对服务的请求进行路由和分发,也可以架构成像Servcie Mesh那样的网格架构,或者只是为了适配某些服务的Sidecar……

但是,我们也可以看到,这样一来,Sidecar就不再那么轻量了,而且很有可能会变得比较重了。

总的来说,Gateway是一个服务器,也可以说是进入系统的唯一节点。这跟面向对象设计模式中的Facade模式很像。Gateway封装内部系统的架构,并且提供API给各个客户端。它还可能有其他功能,如授权、监控、负载均衡、缓存、熔断、降级、限流、请求分片和管理、静态响应处理,等等。

下面,我们来谈谈一个好的网关应该有哪些设计功能。

网关模式设计

一个网关需要有以下的功能。

当然,网关还可以做更多更有趣的事情,比如:

Gateway、Sidecar和Service Mesh

通过上面的描述,我们可以看到,网关、边车和Service Mesh是非常像的三种设计模式,很容易混淆。因此,我在这里想明确一下这三种设计模式的特点、场景和区别。

首先,Sidecar的方式主要是用来改造已有服务。我们知道,要在一个架构中实施一些架构变更时,需要业务方一起过来进行一些改造。然而业务方的事情比较多,像架构上的变更会低优先级处理,这就导致架构变更的“政治复杂度”太高。而通过Sidecar的方式,我们可以适配应用服务,成为应用服务进出请求的代理。这样,我们就可以干很多对于业务方完全透明的事情了。

当Sidecar在架构中越来越多时,需要我们对Sidecar进行统一的管理。于是,我们为Sidecar增加了一个全局的中心控制器,就出现了我们的Service Mesh。在中心控制器出现以后,我们发现,可以把非业务功能的东西全部实现在Sidecar和Controller中,于是就成了一个网格。业务方只需要把服务往这个网格中一放就好了,与其它服务的通讯、服务的弹力等都不用管了,像一个服务的PaaS平台。

然而,Service Mesh的架构和部署太过于复杂,会让我们运维层面上的复杂度变大。为了简化这个架构的复杂度,我认为Sidecar的粒度应该是可粗可细的,这样更为方便。但我认为,Gateway更为适合,而且Gateway只负责进入的请求,不像Sidecar还需要负责对外的请求。因为Gateway可以把一组服务给聚合起来,所以服务对外的请求可以交给对方服务的Gateway。于是,我们只需要用一个负责进入请求的Gateway来简化需要同时负责进出请求的Sidecar的复杂度。

总而言之,我觉得Gateway的方式比Sidecar和Service Mesh更好。当然,具体问题还要具体分析。

网关的设计重点

第一点是高性能。在技术设计上,网关不应该也不能成为性能的瓶颈。对于高性能,最好使用高性能的编程语言来实现,如C、C++、Go和Java。网关对后端的请求,以及对前端的请求的服务一定要使用异步非阻塞的 I/O 来确保后端延迟不会导致应用程序中出现性能问题。C和C++可以参看Linux下的epoll和Windows的I/O Completion Port的异步IO模型,Java下如Netty、Vert.x、Spring Reactor的NIO框架。当然,我还是更喜欢Go语言的goroutine 加 channel玩法。

第二点是高可用。因为所有的流量或调用经过网关,所以网关必须成为一个高可用的技术组件,它的稳定直接关系到了所有服务的稳定。网关如果没有设计,就会成变一个单点故障。因此,一个好的网关至少要做到以下几点。

第三点是高扩展。因为网关需要承接所有的业务流量和请求,所以一定会有或多或少的业务逻辑。而我们都知道,业务逻辑是多变和不确定的。比如,需要在网关上加入一些和业务相关的东西。因此,一个好的Gateway还需要是可以扩展的,并能进行二次开发的。当然,像Nginx那样通过Module进行二次开发的固然可以。但我还是觉得应该做成像AWS Lambda那样的方式,也就是所谓的Serverless或FaaS(Function as a Service)那样的方式。

另外,在运维方面,网关应该有以下几个设计原则。

在整体的架构方面,有如下一些注意事项。

另外,因为网关是为用户请求和后端服务的桥接装置,所以需要考虑一些安全方面的事宜。具体如下:

小结

好了,我们来总结一下今天分享的主要内容。首先,网关模式能代替边车模式,区别是它将分布在各个服务边上的边车换成了集中式的网关。网关不必管理所有服务节点,而是可以根据需要,为指定的服务集群配上网关,也可以在网关前面加上更高层的网关,从而构造出一个星型的结构。

接着,我列举了网关模式的功能特性。然后,我介绍了网关模式的设计重点。由于网关的功能比较多,因此在设计上要考虑的点也比较多,需要我们仔细思考和斟酌。下篇文章中,我们讲述部署升级策略。希望对你有帮助。

也欢迎你分享一下你接触到的分布式系统有没有用到网关?网关的功能如何?有没有把服务的弹力设计做在里面?

文末给出了《分布式系统设计模式》系列文章的目录,希望你能在这个列表里找到自己感兴趣的内容。