在分布式架构中,服务网关(Service Gateway)的出现有其必然性。为了帮你理解这种必然性,我们先来讨论一下常见的分布式系统组成结构。
一、为什么需要服务网关?
图中各个客户端和后台服务直接交互,我们可以看出三点核心问题。
图 1 分布式系统组成结构
1. 隔离性
首先,客户端与服务之间应该确保隔离性,这是架构设计的一条基本原则。
随着业务需求的变化和时间的演进,各个服务的划分和实现通常都会做相应的调整和升级。但因为现在服务端的访问入口是直接暴露给客户端的,所以如何实现服务端的调整和升级对客户端透明是一个痛点。
图 2 服务端功能演进示意图
2. 访问粒度
更为重要的是,客户端与服务端之间应该具备灵活的访问粒度。
我们知道,每个服务的职责在于提供某一个业务领域的访问入口,而客户端一般都需要整合多项业务数据。在现有的分布式架构中,一个客户端需要和多个服务交互。但是客户端请求和服务提供的访问入口在粒度上往往是不一致的,那么不同场景就需要具备对应的粒度关系。
3. 安全性
最后,在客户端和服务端的访问过程中,也势必涉及安全、性能等一系列非功能性需求。
从设计原则上讲,这些非功能性需求应该是独立开发和演进的,但现状是它们的实现常被夹杂在服务端代码中,与业务功能混淆,维护性很差。这也是我们在开发分布式系统时的一大痛点。
为了解决上述三个问题,确保客户端和服务端交互过程的合理设计和实现,我们需要从系统架构的角度来分析。
在软件设计领域,当我们发现两个组件之间的关系已经不好把控时,经典的做法就是引入分层思想,也就是在这两个组件之间加入一个中间层。而在分布式环境下,我们就可以在客户端和服务端之间架设一层服务网关。
图 3 服务网关示意图
在分布式系统中,架构设计上的一个核心要点就是:要确保所有来自客户端的请求都通过服务网关,再路由转发到后端服务。这样,在网关层我们就可以统一拦截和处理请求,实现对访问粒度的控制、隔离性以及非功能性需求。
那么究竟什么是服务网关?它是如何有效解决这些问题呢?让我们一起来详细看一下服务网关提供的解决方案。
二、服务网关提供了什么样的解决方案?
从概念上讲,所谓的网关(Gateway)是在网络架构中用来实现系统互联的一种组件。而在分布式架构中,服务网关本质上也是一个后台服务,为客户端提供唯一的访问入口。
1. 路由和解耦
在服务网关中,业务路由支持是它的基础功能。针对不同的客户端请求,路由机制能确保将这些请求转发到最合适的服务进行处理。
因此,在网关层,我们可以统一管理各种服务,为客户端请求提供更加简单高效的路由支持。同时,我们也可以制定灵活的路由策略,常见的包括黑白名单、动态规则等。
图 4 服务网关的路由机制
服务路由功能带来的作用就是解耦。对于任何系统交互行为而言,解耦都是必须要考虑的设计原则。而从设计模式上讲,我们也可以把服务网关看做是外观(Facade)模式的一种具体表现形式。外观模式的作用就是把复杂度屏蔽在系统内部,从而降低耦合度。
图 5 网关模式示意图
基于服务网关的这种解耦作用,我们就可以解决客户端和服务端之间的隔离性问题。因为服务网关使得客户端和服务器端在调用关系和部署环境上实现了解耦,向客户端隐藏了应用如何被划分到各个服务的细节。这样,后台服务的演进过程显然就可以不影响到客户端的访问过程。
2. 服务适配和 API 优化
服务网关的第二个核心功能是服务适配,即可以根据客户端请求来动态组合服务端资源,并返回最优的响应结果。
日常开发时,服务网关作为单一入口,通过转换和适配请求,完成与后台服务体系的整合。一个典型的场景就是在多客户端应用中,考虑到不同客户端页面展示的差异性,就算是针对同一个场景的业务请求,需要返回的数据可能也会有所不同。
而服务网关可以向每个客户端提供最优的 API,实现按需获取数据。例如,对于同样的用户信息分页查询功能,PC 端和 APP 端的分页大小就会不一样,我们需要针对不同客户端提供最合适的 API。
图 6 针对客户端的最优 API 示意图
另一方面,我们可以想象通过服务网关,聚合后台服务的调用过程,提供统一面向客户端的聚合服务效果,这样就减少了客户端与服务端的交互次数。
典型的例子就是在电商系统中,原本客户端的一次请求,可能需要同时访问用户服务和订单服务,才能获取想要的订单详细信息,现在我们可以把所有需要的信息统一,在网关层聚合之后再返回给客户端,这也是优化 API 的一个场景的维度。
图 7 使用网关聚合服务调用示意图
基于服务网关的这种优化 API 的作用,我们就可以解决客户端与服务之间的访问粒度问题。服务网关确保了控制访问粒度的高效性和灵活性。
3. 访问控制和切面支持
最后,我们需要强调一下服务网关所具备的访问控制功能,同样体现在多个方面。
例如,在高并发系统中,通常会采用限流和降级的手段来防止服务不可用。这时候在服务网关上,我们就可以设置对应阈值或规则来限制客户端请求流转到后台服务。
图 8 网关的安全性和访问控制功能示意图
再比如说,对于来自客户端的任何请求都需要考虑安全性,而服务网关是统一管理安全性的绝佳场所。实现访问安全性的前提是提供用户认证,我们可以将用于身份认证的部分功能抽取到网关层。而常见的安全性技术如密钥交换、报文加解密等功能也都可以在网关中加以实现。
服务网关的访问控制功能可以说为开发人员提供了强大的切面支持。通过切面,可以把常见的安全性、性能等需求从业务代码中抽离出来,从而解决在分布式系统中如何有效实现非功能性需求的问题。
三、Zuul 和 Spring Cloud Gateway
介绍完服务网关所提供的解决方案之后,我们接下来讨论具体的网关实现工具。
这里,我们选择 Spring 家族中的 Spring Cloud 展开,这是目前最主流的微服务开发框架。在 Spring Cloud 中,实际上提供了两款服务网关实现工具,一款是集成 Netflix 中的 Zuul 网关,一款是自研的 Spring Cloud Gateway。
1. Netflix Zuul
正如我们前面分析的,Zuul 网关为开发人员提供了动态路由、访问监控、安全控制等功能。对于服务网关而言,最重要的功能就是服务路由。在这里,你可以看这个配置示例:
zuul:
prefix: /springsystem
routes:
userservice: /user/**
配置代表了在大型分布式系统中服务网关比较常见的一种应用场景,即在各个服务请求地址上添加一个前缀 prefix 来标识模块和子系统。同时,我们在“routes”配置段中使用"/user"来为 userservice 这个服务名称指定请求根地址。这样,所有通过"/user"地址访问的请求都将被自动路由到该服务中。
2. Spring Cloud Gateway
Spring Cloud Gateway 是 Spring 官方自己开发的一款服务网关,在技术体系上,Spring Cloud Gateway,基于最新的 Spring 5 和 Spring Boot 2 以及用于响应式编程的 Project Reactor 框架,提供响应式、非阻塞式 IO 模型,所以性能上 Spring Cloud Gateway 显然要更胜一筹。作为新一代服务网关,我们接下来重点展开 Spring Cloud Gateway。
Spring Cloud Gateway 的基本结构中包含两个核心组件,一个是过滤器(Filter),一个是谓词(Predicate)。
图 9 Spring Cloud Gateway 基本架构图
过滤器:Spring Cloud Gateway 中的过滤器与传统 Servlet 中的过滤器是同一个概念,在响应 HTTP 请求之前或之后,用于修改请求本身及对应的响应结果。
谓词:而所谓谓词,本质上是一种判断条件,用于将 HTTP 请求与路由进行匹配。Spring Cloud Gateway 内置了大量的谓词组件,可以分别对 HTTP 请求的消息头、请求路径等常见的路由媒介进行自动匹配以便决定路由结果。
我们来看一下一条基于 Spring Cloud Gateway 的完整路由配置的基本结构,如下所示:
spring:
cloud:
gateway:
routes:
- id: userroute
uri: lb://userservice
predicates:
- Path=/user/**
filters:
- PrefixPath=/springsystem
在上述“routes”配置段中,首先我们使用 id 配置项指定了这条路由信息的编号,这个例子中是“userroute”。而 uri 配置项中的“lb”代表负载均衡 LoadBalance,也就是说在访问 url 指定的服务名称时需要集成负载均衡机制。
然后我们使用了谓词来对请求路径进行匹配,这里的“Path=/user/**”代表所有以“/user”开头的请求都将被路由到这条路径中。
最后我们还定义了一个过滤器,这个过滤器的作用是为访问路径添加前缀,这样当请求“/user/”时,最后转发到目标服务的路径将会变为“/springsystem/user/”,而这个路径会被路由到 userservice 这个服务中。
四、总结
可以说,在当下的分布式系统开发过程中,服务网关已经是一个必备组件。很难想象,客户端在访问后台服务时没有通过网关进行统一的路由和访问控制。一旦我们在服务调用过程中添加了一个网关层,那么开发人员就可以集中处理服务路由、服务适配以及常见的安全控制、限流降级等非功能性需求。