You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

99 lines
9.4 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 47 | 微服务API网关搭建三步曲
你好,我是温铭。
今天这节课OpenResty 专栏就要进入实战的章节了。我会用三节课的内容,来为你介绍如何实现一个微服务 API 网关。在这个过程中,我们不仅会涉及到前面学过的 OpenResty 知识,我也会从行业、产品、技术选型等多个维度,为你展示下,如何从头做一个新的产品和开源项目。
## 微服务 API 网关有什么用?
让我们先来看下微服务 API 网关的作用。下面这张图,是一个简要的说明:
![](https://static001.geekbang.org/resource/image/de/ee/dea3e12608301d1a6b7fffce8bafafee.png)
众所周知API 网关并非一个新兴的概念,在十几年前就已经存在了,它的作用主要是作为流量的入口,统一处理和业务相关的请求,让请求更加安全、快速和准确地得到处理。它有以下几个传统功能:
* 反向代理和负载均衡,这和 Nginx 的定位和功能是一致的;
* 动态上游、动态 SSL 证书和动态限流限速等运行时的动态功能,这是开源版本 Nginx 并不具备的功能;
* 上游的主动和被动健康检查,以及服务熔断功能;
* 在 API 网关的基础上进行扩展,成为全生命周期的 API 管理平台。
在最近几年业务相关的流量不再仅仅由PC客户端和浏览器发起更多的来自手机、IoT 设备等,未来随着 5G 的普及这些流量会越来越多。同时随着微服务架构的结构变迁服务之间的流量也开始爆发性地增长。在这种新的业务场景下自然也催生了API 网关更多、更高级的功能:
1. 云原生友好,架构要变得轻巧,便于容器化;
2. 对接 Prometheus、Zipkin、SkyWalking 等统计、监控组件;
3. 支持 gRPC 代理,以及 HTTP 到 gRPC 之间的协议转换,把用户的 HTTP 请求转为内部服务的 gRPC 请求;
4. 承担 OpenID Relying Party 的角色,对接 Auth0、Okta 等身份认证提供商的服务,把流量安全作为头等大事来对待;
5. 通过运行时动态执行用户函数的方式来实现 Serverless让网关的边缘节点更加灵活
6. 不锁定用户,支持混合云的部署架构;
7. 最后,网关节点要状态无关,可以随意地扩容和缩容。
当一个微服务 API 网关具备了上述十几项功能时,就可以让用户的服务只关心业务本身;而和业务实现无关的功能,比如服务发现、服务熔断、身份认证、限流限速、统计、性能分析等,就可以在独立的网关层面来解决。
从这个角度来看API 网关既可以替代 Nginx 的所有功能,处理南北向的流量;也可以完成 Istio 控制面和 Envoy 数据面的角色,处理东西向的流量。
## 为什么要新造轮子?
正因为微服务 API 网关的地位如此重要,所以它一直处于兵家必争之地,传统的 IT 巨头在这个领域很早就都有布局。根据 2018 年 Gartner 发布的 API 全生命周期报告谷歌、CA、IBM、红帽、Salesforce 都是处于领导地位的厂商,开发者更熟悉的 Kong 则处于远见者的区间内。
那么,问题就来了,为什么我们还要新造一个轮子呢?
简单来说,这是因为当前的微服务 API 网关都不足以满足我们的需求。我们首先来看闭源的商业产品,它们的功能都很完善,覆盖了 API 的设计、多语言 SDK、文档、测试和发布等全生命周期管理并且提供 SaaS 服务,有些还与公有云做了集成,使用起来非常方便。但同时,它们也带来了两个痛点。
第一个痛点平台锁定问题。API 网关是业务流量的入口,它不像图片、视频等 CDN 加速的这种非业务流量可以随意迁移API 网关上会绑定不少业务相关的逻辑。你一旦使用了闭源的方案,就很难平滑和低成本地迁移到其他平台。
第二个痛点,无法二次开发的问题。一般的大中型企业都会有自己独特的需求,需要定制开发,但这时候你只能依靠厂商,而不能自己动手去做二次开发。
这也是为什么开源的 API 网关方案开始流行的一个原因。不过,现有的开源产品也不是万能的,自身也有很多不足。
第一,依赖 PostgreSQL、MySQL 等关系型数据库。这样,在配置发生变化的时候,网关节点只能轮询数据库。这不仅造成配置生效慢,也给代码增加了复杂度,让人难以理解;同时,数据库也会成为系统的单点和性能瓶颈,无法保证整体的高可用。如果你把 API 网关用于 Kubernetes 环境下,关系型数据库会显得更加笨重,不利于快速伸缩。
第二,插件不能热加载。当你新增一个插件或者修改现有插件的代码后,必须要重载服务才能生效,这和修改 Nginx 配置后需要重载是一样的,显然会影响用户的请求。
第三,代码结构复杂, 难以掌握。有些开源项目做了多层面向对象的封装,一些简单的逻辑也变得雾里看花。但其实,对于 API 网关这种场景,直来直去的表达会更加清晰和高效,也更有利于二次开发。
所以,我们需要一个更轻巧、对云原生和开发友好的 API 网关。当然,我们也不能闭门造车,需要先深入了解已有 API 网关各自的特点这时候云原生软件基金会CNCF的全景图就是一个很好的参考
![](https://static001.geekbang.org/resource/image/19/f7/19328e6e516ed8ed6f723dd32fef58f7.png)
这张图筛选出了业界常见的 API 网关,以开源的方案为主,可以为我们下面的技术选型提供不少有价值的内容。
## API 网关的核心组件和概念
当然,在具体实现之前,我们还需要了解 API 网关有哪些核心组件。根据我们前面提到的 API 网关具备的功能点,它至少需要下面几个组件才能开始运行。
首先是路由。它通过定义一些规则来匹配客户端的请求,然后根据匹配结果,加载、执行相应的插件,并把请求转发给到指定的上游。这些路由匹配规则可以由 host、uri、请求头等组成我们熟悉的 Nginx 中的 location就是路由的一种实现。
其次是插件。这是 API 网关的灵魂所在身份认证、限流限速、IP 黑白名单、Prometheus、Zipkin 等这些功能,都是通过插件的方式来实现的。既然是插件,那就需要做到即插即用;并且,插件之间不能互相影响,就像我们搭建乐高积木一样,需要用统一规则的、约定好的开发接口,来和底层进行交互。
接着是schema。既然是处理 API 的网关,那么少不了要对 API 的格式做校验,比如数据类型、允许的字段内容、必须上传的字段等,这时候就需要有一层 schema 来做统一、独立的定义和检查。
最后是存储。它用于存放用户的各种配置,并在有变更时负责推送到所有的网关节点。这是底层非常关键的基础组件,它的选型决定了上层的插件如何编写、系统能否保持高可用和可扩展性等,所以需要我们审慎地决定。
另外,在这些核心组件之上,我们还需要抽象出几个 API 网关的常用概念,它们在不同的 API 网关之间都是通用的。
先来说说Route。路由会包含三部分内容即匹配的条件、绑定的插件和上游如下图所示
![](https://static001.geekbang.org/resource/image/6a/0d/6a86c854ec54b07347ff517114482c0d.png)
我们可以直接在 Route 中完成所有的配置,这样最简单。但在 API 和上游很多的情况下,这样做就会有很多重复的配置。这时候,我们就需要 Service 和 Upstream 这两个概念来做一层抽象。
我们接着来看Service。它是某类 API 的抽象,也可以理解为一组 Route 的抽象它通常与上游服务是一一对应的而Route 与 Service 之间通常是 N:1 的关系。我也用了一张图来表示:
![](https://static001.geekbang.org/resource/image/09/db/0954bddf3828fa26f26a1ba2003c7edb.png)
通过 Service 的这层抽象,我们就可以把重复的插件和上游剥离出来。这样,在插件和上游发生变更的时候,我们只需要修改 Service 就可以了,而不用去修改多个 Route 上绑定的数据。
最后说说Upstream。还是继续上面的示例如果两个 Route 中的上游是一样的,但是绑定的插件各自不同,那么我们就可以把上游单独抽象出来,如下图所示:
![](https://static001.geekbang.org/resource/image/eb/8e/ebedcfafc5aeafb970097e480f663d8e.png)
这样在上游节点发生变更时Route 是完全无感知的,它们都在 Upstream 内部进行了处理。
其实,从这三个主要概念的衍生过程中,我们也可以看到,这几个抽象都基于用户的实际场景,而不是生造出来的。自然,它们适用于所有的 API 网关,和具体的技术方案无关。
## 写在最后
今天这节课,我们介绍了微服务 API 网关的作用、功能、核心组件和抽象概念,它们都是 API 网关的基础。
这里留给你一个思考题:关于传统的南北向流量,和微服务之间的东西向流量,你觉得 API 网关是否都可以处理呢?如果你已经在使用 API 网关了,你也可以写下当初技术选型时的思考。欢迎在留言区和我交流探讨,也欢迎你把这篇文章分享给你的同事、朋友,一起学习和进步。