饿了么高稳定、高性能、高可用、高容错API架构实践!

简介: 先简单介绍一下 API,就是相当于前端比如 Web 访问到后端的服务接口,这中间有一个隔离,适配给外部各端进行访问,隔离是起到安全性的考虑,还有一个协议转换的考虑。

43e1a5000a88d330cc683c10c184f2b3fe7d85cb

什么是 API Everything?

e314041e1a833337a5b0abe3e3b016bc8faf35ed

先简单介绍一下 API,就是相当于前端比如 Web 访问到后端的服务接口,这中间有一个隔离,适配给外部各端进行访问,隔离是起到安全性的考虑,还有一个协议转换的考虑。

当然,基于这一块我们还有很多其他的考虑。在饿了么初期发展阶段,我们的很多 Web API 层都是手写的,即多数应用服务后端,都自己写 Web API,单独部署,提供给前端 HTTP API 调用。

98fb2487e8446965455f7aa2d22d030878787cdb

当时业务高速发展,为了快速应对,有一些业务逻辑会放在 Web API 层,甚至在 Web API 层也会访问数据库,进行数据库操作。

在 API 层直接访问数据库会导致安全性的一些问题,这个是不允许的。前端访问后端,这个 HTTP API 接口是什么风格?

有 Restful ,还有 JSON-RPC ,需要统一考虑,不然对前端开发的体验不一致。

另外 HTTP API 文档存在过时,不能反映代码实现的变化,比如代码改了,文档没有改。

最后,前端同学和后端同学同时开发,但开发的节奏可能不太一样。比如前端在进行开发,但后端可能会被插入更加紧急的项目,就不能及时完成当前项目的 API。

这样可能会延误前端的开发,产生前后端开发不同步,导致前端资源和后端资源有个相互等待,会导致开发体验的不顺畅,效率也不高。

需求调研-API Everything

基于这些情况,公司考虑是否可以统一形成一个 API 框架,于是我们调研了各个部门,发现他们都有这个需求,希望有一个统一的 API 框架开发,目前也有一些独立写的 Web API 层。

如下图,是调研的一些需求:

8c35ea3516506fc9880f250220d161af5d7e83c5

从图中,我们能看到一个很重要的功能就是 HTTP 到 SOA 的服务映射,还有认证与鉴权,比如公司的 SSO,包括用户、饿了么的一些鉴权,API 部署与运行。

API Orchestration,这个相当于是 API 的拼接和剪裁的概念,可以调用多个 API,取各个 API 返回结果的一部分,重新组合成新的结果,返回给前端。

这个应用在一个前端 API 调用,可以实际调用多个后端 API,组装一个返回结果给前端,减少前端调用后端多次,提高前端用户的体验。

另外,因为后端已经有了很多基础服务的接口,新业务开发不需要后端再提供接口,只需要在这些接口上进行组合、裁剪就可以了。

此外还有 API 文档、API 测试、Mock API、限流、反爬(就是说接口暴露在公网,存在爬虫爬取这些接口的情况,我们怎么保护接口等等)以及灰度。

我们的 API Everything 做这些事情时,把后端的 SOA 服务通过 API 的方式,安全可靠地提供给各种端(Web/APP)来使用。

产品技术方案原则

调研出来以后,需要考虑以什么原则去考虑产品和系统设计。

998e139e85b8eb0c264502c68afb72608629b3f0

API Everything 是一个基础框架,我们首先考虑到基础框架的稳定性是第一位。

哪怕你不能满足所有功能需求,但你很稳定,接入 API Everything 框架的各个应用系统就不会歇菜,不会出问题,服务才能在稳定的基础上提升,才有可能增加新的功能和特性,也就是说稳定压倒一切。

然后是性能,包括吞吐量(响应速度)、性能高了,就会节省硬件资源;高可用性,避免出现单点故障。还有容错性,对外各种依赖,要考虑外部依赖歇菜怎么办,怎么降级。

还有,API Everything 接了后端的应用系统,外部流量进来,不能冲击到后端应用系统。如何让这个系统更健壮,怎么保护自己,怎么保护接入的应用系统等等。

其次,在这个基础之上考虑 DevOps 怎么弄,提供接入方自助 DevOps,还有各种指标查看、监控告警、排错手段、查看 log/trace/exception、自助扩容等。

最终希望这一块做到对接入方是透明的,可以自动扩容。API Everything 框架引起的问题,由我们解决,接入应用方出现的问题,由接入方解决,要有非常清晰的边界界定。

一般问题可以自动解决,现场日志自动保留,尽可能自愈,并且接入方知道发生了什么情况。

另外,怎么让我们的研发或者应用开发端更“懒”?就是把经常要做的事情自动化。

比如接入一个应用方,要进行各种配置,比较繁琐也容易出错,那我们是不是可以进行自动化接入、自动化配置呢?

刚才也谈到代码和文档不同步怎么办,所以我们不写文档,在代码里面写文档。

比如:在 Java 代码里写 Java doc 注释,我们就把这些注释抽出来,作为 API 文档的一部分;另外,我们也提供一些标注,帮助完成文档。

用户体验也是考虑的一大因素,因为技术产品基本上由工程师直接进行开发,追求完成功能,多数没有考虑用户体验,导致用起来操作别扭。

所以在这方面要充分吸取教训,把使用者的体验考虑起来,能点一下就不用点两下,不能把技术复杂性暴露给用户去理解、去操作,让用户用起来很爽,简单不去操作是一个目标。

这个框架涉及到很多配置,散放在不同系统,我们的想法是在一个配置里面全把它搞定,不要让用户理解这个是 API Everything 框架的哪一部分管理的,要到哪个系统去操作。

另外就是满足不同的功能需求,比如接入不同的协议等,这是我们对整个产品方案在原则上的一些考虑。

生命周期

5ae711f1503e61e70c9296f8b6274253793c1847

从 API 这边出发,可以看到 API 的生命周期是从 API 开发开始的,这个过程中会有文档、Mock,开发完了是管理,也就是授权谁能访问,有些是不是可以灰度。

API 管理之后是 API 网关服务,就是运行态服务,对 API 进行协议转换,比如将 HTTP 协议转换成 SOA,调用后台的 SOA 服务,最后进入 API 运维,就是对 API 进行监控管理和部署扩容。

产品规划

根据产品系统设计原则,结合 API 的生命周期,我们规划了下面几个产品,如下图:

6cac22f7f06bc693a5a3f921731d50463f8f5e56

比如说开发支持这一块,就是 API Portal,运行支持这块就是 Stargate Cluster。还有质量保证,就是 API Robot,通过自动化回归测试来保证 API 的质量。

另外要考虑到在开发的过程中,怎么让前后端同步,这里面很重要的一点是怎么样 Mock API 的数据,让前后端分离开发?这就产生了 Mock Server,于是根据规划形成了这四个产品

但这四个产品之间是什么样的关系呢?这里从系统上的交互来考虑。

64f4dfe86df73b16aad974b1dfe56298736fac17

如上图,我们从底下看,前端的应用(比如到达圈前端),通过 HTTP 访问,到达 Nginx Cluster,然后转向 SOA 服务。

灰色的路径就是到达圈管理后台应用,灰色再上去就到达圈服务,另外还有红色这条路径,前端 URL 可以通过加入 query string 访问 Mock Server。

Stargate Cluster 收到这个 querystring,就不会发给后端 SOA 服务,会路由到指定的 Mock Server。前端会通过 Mock 的方式完成前端的开发。

API Portal 负责 API 文档这块,文档对应的是部署到哪个环境,在 API Portal 里有显示。

在饿了么有如下几个环境:

  • 开发的 Alpha 测试环境,这个是提供给开发使用的。
     
  • Beta 环境,这个是提供给测试来验证是否可以上线的环境,只有通过了 Beta 测试,才能上线。这个 Beta 环境也用来和其他团队进行联调。
     
  • Prod 环境,用于线上生产。在 API Portal 上就能知道当前部署的应用、对外提供的 API 文档具体是什么。这是 API Portal 通过访问 Stargate Cluster 部署信息获得的。
     

API Robot 从 API Portal 中获取 API 定义,通过定义发测试请求给 Stargate Cluster。

饿了么内部服务是 SOA 架构,服务间有相互依赖的情况,需要进行测试、联调。

有时候发现我们 SOA 服务依赖对方 SOA 服务,对方还没有开发完成,我们想测试自己开发 的 SOA 服务怎么办?

这时我们就可以用 Mock Server,Mock 对方的 SOA 服务,写一下 Mock Case,完成我们自己的开发测试。

0d2be162078e67d9996d4f57372277fe1b437e7c

刚才说代码即文档,所以可能要规范一下怎样写代码,注释和标注写完了,就可以自动化从这些注释和标注中抽出文档,形成 API 文档。

Web API 这一块不需要手工写,目前我们自动生成对应 Web API 的代码,然后自动再部署。

部署就是监听 SOA 服务部署消息,收到了部署消息,就自动生成的 Web API 代码并且自动部署。

而 Mock,我们也是自动生成的,创建 Mock Case 的时候,就自动生成相应的数据,这些数据可以自己改。还有 API 的监控告警,每个应用接入,自动进行全链路监控。

Stargate Cluster 技术架构

1387723884f86effc4355a6edb436acb4e1c0dd0

如上图,这是其中一个产品 Stargate Cluster 的技术架构。

从上面看,ELESS 是我们构建系统,我们会监听它的构建消息,当有构建过来的时候,调用 base.stargate_core 服务。

该服务实现非常简单,就是把 Stargate Cluster 需要的信息保存到 MaxQ 里面,MaxQ 是饿了么自己开发的一个 MQ 产品,目前在大量应用。

最后 Stargate Cluster 运营管理服务是从 MaxQ 取消息进行处理。

为什么会有这样的考虑?因为之前我们在 Stargate Cluster 运营管理服务里经常有迭代开发,经常增加一些功能(比如异地多活),这些功能不断地迭代、增加,需要经常部署,处于一个不太稳定的情况。

我们想任何这种构建和部署消息不能丢,于是就有了 stargate_core 这个服务,这个服务非常简单,不进行功能上的迭代,保持不变,这样就比较可靠,作用就是把构建和部署消息放在 MaxQ 里头。

而 stargate 运营管理不断开发、然后重启,这个过程中至少 MaxQ 里的数据不会丢,重启完了也可以消费,继续进行部署。我们是基于变化和不变化的隔离去考虑系统可靠性的结果。

Stargate Cluster 基于 Docker 部署

13fe302c19e694fa70ee93c27ba4b0d2f8c98a57

谈谈为什么能自动部署?其实挺有意思的,我们这边用的是 Docker 环境。

从底下开始看,可以看到,比如 SOA 服务部署了,通过 ELESS(饿了么发布系统 ),我们获得部署消息,放到 MaxQ 里。

然后我们从 MaxQ 拿出消息,调用 AppOS(饿了么自研的 Docker 平台),启动相应的 Docker 实例。

实例上运行着这个 SOA 服务对应的 Web API 代码,Docker 实例启动时,调用 Navigator(饿了么自研的 Nginx 管理平台),将该 Docker 实例的 IP 注册到 Nginx 上,这样外部流量就打到这些 Docker 上了。

在新 Docker 成功启动之后,Stargate Cluster 就会调用 AppOS 将之前版本的 Docker 实例给销毁掉,通过 Navigator 将对应在 Nginx 上的 IP 也删除掉,这就是完成自动部署。

a96cc4db1b92a22c5b9d0d7887549cec3d917d97

这是我们自动部署的一些信息,从图中可以看到左上角基本上是 SOA 服务部署的 Push Seq,下面是 PushSeq Used by Client。

这两个 Push Seq 一致,就说明 SOA 服务部署时,Stargate Cluster 将其对应的 Web API 端也自动部署了,两边用的版本(Push Seq)是一致的。同时,包括部署的一些版本信息我们也都会保存下来。

API Portal – 自动化文档

58ec7a3f98e8d53b0b5e511c0c4de8b82a833bd4

讲完 Stargate Cluster,我们再来看看 API Portal 这一块。

API Portal 在系统部署时,获取部署的源文件,自动解析这些代码,然后根据代码里面的注释和标注自动生成 API 文档,这个文档以 Swagger 方式存储。

刚开始我们以 Swagger 的原生界面去展示这个界面,前端开发不太习惯这个界面,于是我们就用前端喜欢的,即作为各种表格进行展示来给他们使用。上面的表格展示方式,就是因为这个开发出来的。

那 Swagger 原生的界面还需要吗?API Portal 上有一个功能, Try it Out(就是试一试),具体就是后端开发采用这个功能看看后端 API 吐的数据长得怎么样,不对就修改后端 API,直到满意为止。

前端也使用这个功能,看看没有实际数据又是怎么样的。Try it Out 功能就采用了 Swagger 原生的界面,后端反而比较喜欢这个页面,因此保留下来。这样前后端的用户体验,也都能满足,大家各取所需。

下面是一个 Swagger 文档界面:

2fda40537847823884a986c0e241176ab690c258

Mock Server 流程

735e3e9ce302625447a32fec61a66b8c3dfa2b67

Mock Server 是什么?

看看图中这个场景,SOA Client 要对 Service Provider 进行测试。

Service Provider (就是被测试的对象 Server Under Test )依赖外部的服务,比如 Server Cluster 1,但是外部的服务因为其他情况不能用,我们就用 Mock Server 来模拟 Server Cluster 1。

这样依赖问题解决了,SOA Client 对 Service Provider 就能正常进行测试了。

使用 Mock Server 还可以解决,依赖服务需要返回特定的场景,但又不好操作,这样通过 Mock Server,写不同的 Mock Case 进行返回,就比较方便。

实际情况下 SOA 环境里面有很多依赖关系,但对方的接口没有确认或者环境不好怎么办?

我们通过 Mock Server 把这些服务全部屏蔽掉,这样只需要测试我们要测试的服务就好,这对 SOA 环境解决依赖问题是挺有效的。

Mock Server—自动解析

ee9c6422ea33738082487213a9a48ebd954fb6f1

饿了么有 Maven 私服,各个 SOA 服务之间通过 Maven 私服上的 API 进行调用。

上图是我们的 Mock Server 界面,从图中可以看到左边是输入依赖服务的接口 Maven 依赖,基本上操作就是延续之前 SOA 调用的一些流程,把外部依赖填到上面的框里头,在 Mock Server 指定依赖的接口。

于是 Mock Server 就能从 Maven 私服去拉这些依赖,自动分析它到底有哪些类,分析好这个类,包括这个类里面有哪些方法都全部显示出来。

上图右边的方法就是自动分析,黄色标识的那个方法,就是想进行 Mock,点击下右边的加号,就创建了 Mock Case。这个 Mock Case 名字就是测试任意参数。

自动生成 Mock Case

7ad0e3cb88161f94cad77957bcd83ae78678c2dd

Mock Case 是说当 Mock Server 接收的数据,即请求的参数和 Mock Case 里设定的 Input 匹配,那么这个 Mock Case 里设定的 Output 就作为响应返回。

刚才创建了一个 Mock Case 叫做测试任意参数, Mock Case 的值是根据分析的 Model(数据定义),自动生成。

比如 Input "type" : Integer,我们没有加入 enum[234567] 的时候,就意味着只要是请求里包含任何整数,该 Mock Case 就会被命中,返回 Output 的内容。

如果加入了 enum [234567],那么只有请求参数里是 234567,该 Mock Case 才会命中。

Output 支持函数,上面 Preview 就可以看到执行 Output 的表达式之后是什么结果,当该 Mock Case 被命中时,这个 Preview 的结果就作为响应返回。

前后端开发分离

f6094fd48ec2aac015b9c4deb1007e4509c82258

后端开发根据 PRD,通过在代码接口里添加标注和注释,就完成了 API 定义。

把代码 check in,构建系统知道这个变化后,然后把这个变化通知到 API Portal,就自动生成了 API 文档,后端在 API Portal 上通知前端,双方通过API Portal 讨论确认 API 文档。

前端根据这个文档,通过 API Portal 上提供的 Mock,完成前端的开发,更复杂的交互需要构造后端 API 产生不同的数据。

前端通过构造不同的 Mock Case,完成这些复杂的开发。开发完成之后,就去进行其他事情,在后端完成 API 开发之后,通知前端一起进行联调。

在以前,前后端不分离的话就会相互等待,前端开发等后端实现 API,吐出数据之后再进行,对前端开发体验不好,现在这样前端就可以一气呵成把它开发完成,不用等候后端了。

现在,后端这边也开发完了,说一个时间大家联调一下就 OK 了,这就是整个前后端开发分离的流程。

应用实践

463dfb8675a5065bf71b1afd720c42d1a9ece762

这个流程在我们配送范围的项目里已经应用起来了,这里我们给出了一个迭代的统计,这个迭代可以在开发的过程中就看到使用的情况。

比如原来估计工时 5 天,现在 3 天就做完了,后端也节省一些时间,以前他们说,不然就等前端调用后告诉他们什么样子,或者他自己写一些测试脚本去看是什么样的数据。

这方面他就写了 API,通过 API Portal 就能看到后端 API 吐的数据是否正确,这样的话后端开发也减少了开发时间。

还有,后端以前要写 Web API 代码,还要进行部署,现在也不用写了,也不用部署了。

以前联调时间都比较长,是两天时间,因为那时联调是包括开发时间,他不确定数据对不对。

所以有些处理还没有写,等他看到数据以后再去开发出来,时间就稍微长一些。

联调的时间,只需半个小时,看这个事情通不通就好了,整个开发体验还是不错的,总体来看,整个开发时间减少了 50% 左右。

问题真的解决了吗?

11bcaa9b08a041185e2c5a8336b86680e83b5638

有了这些产品,我们看最早提到的那些问题是不是都解决了。假如今天写业务,我们是通过统一的方式介入,把它下沉到 SOA,因此业务也不会分散到各个地方。

我们在定义 API 时,也给了一个缺省的 API 生成方式,就是使用 Json RPC,自动将方法签名等作为 URL。

另外也提供了 Mock 服务,帮助前后端开发分离,还有就是代码即是文档这一块。然而这些问题都解决了,真的解决了吗?

其实还远远没有,还有更多事情要解决:

  • 全链路环节比较多, 出现问题时如何快速定位?
     
  • 故障发生时: 能够自动把现场保留下来吗?能够执行基本分析,把分析的结果保存下来吗?能够自愈吗?
     
  • 能够快照后台服务及数据, 通过 Docker 环境,通过之前记录的 traffic,自动化完成回归测试吗?
     
  • 采用 Async Web, 提高性能?采用 GO?
     
  • 当有一些业务需求: 现有 API 相互组合就可以完成这个需求,还是需要开发?需要智能分析所有 API 业务属性吗?需要面向业务开发提供搜索和推荐?
     
关于团队思考

我们一直在想,面向开发、测试、运维角色,如何提供一个更好的服务?也就是说怎么样更懒、更自动化。

比如在 API 的运维上,我们能不能做到对于接入用户方完全无感知?还有就是我们的 API Everything 团队,面对多个接入方同时进行,我们应怎样处理,自动化?

其实我们不希望太多重复的事情需要重复去做,而是考虑怎么通过工具把我们自己给解放出来,去做更多自动化的事情,首先解放自己,才能拯救用户。

最后我们也希望从根上去思考问题,去解决问题。

目录
相关文章
|
10天前
|
监控 JavaScript 安全
构建微服务架构下的API网关
【4月更文挑战第15天】在微服务架构中,API网关扮演着至关重要的角色。它作为系统的唯一入口,不仅负责请求的路由、负载均衡和认证授权,还涉及到监控、日志记录和服务熔断等关键功能。本文将探讨如何构建一个高效且可靠的API网关,涵盖其设计原则、核心组件以及实现策略,旨在为后端开发人员提供一套实用的指导方案。
24 4
|
26天前
|
XML JSON 安全
谈谈你对RESTful API设计的理解和实践。
RESTful API是基于HTTP协议的接口设计,通过URI标识资源,利用GET、POST、PUT、DELETE等方法操作资源。设计注重无状态、一致性、分层、错误处理、版本控制、文档、安全和测试,确保易用、可扩展和安全。例如,`/users/{id}`用于用户管理,使用JSON或XML交换数据,提升系统互操作性和可维护性。
18 4
|
1月前
|
消息中间件 缓存 API
微服务架构下的API网关性能优化实践
在现代的软件开发中,微服务架构因其灵活性和可扩展性被广泛采用。随着服务的细分与增多,API网关作为微服务架构中的关键组件,承担着请求路由、负载均衡、权限校验等重要职责。然而,随着流量的增长和业务复杂度的提升,API网关很容易成为性能瓶颈。本文将深入探讨API网关在微服务环境中的性能优化策略,包括缓存机制、连接池管理、异步处理等方面的具体实现,旨在为开发者提供实用的性能提升指导。
|
1月前
|
缓存 负载均衡 监控
构建高效微服务架构:API网关的作用与实践
【2月更文挑战第31天】 在当今的软件开发领域,微服务架构已成为实现系统高度模块化和易于扩展的首选方法。然而,随着微服务数量的增加,确保通信效率和管理一致性变得尤为重要。本文将探讨API网关在微服务架构中的核心角色,包括其在请求路由、安全性、负载均衡以及聚合功能方面的重要性。我们将通过具体案例分析,展示如何利用API网关优化后端服务,并讨论实施过程中的最佳实践和常见挑战。
|
1月前
|
搜索推荐 数据挖掘 API
1688商品详情API在电商平台中的应用与实践
随着电子商务的迅猛发展,越来越多的商家选择利用API(应用程序编程接口)来提升其在线业务的效率和用户体验。特别是在商品信息展示方面,1688商品详情API作为连接商家和消费者的重要桥梁,扮演着至关重要的角色。本文将深入探讨1688商品详情API的功能、应用场景以及如何通过该API提高电商平台的商品信息展示质量。
|
1月前
|
缓存 监控 API
Python Web框架FastAPI——一个比Flask和Tornada更高性能的API框架
Python Web框架FastAPI——一个比Flask和Tornada更高性能的API框架
58 0
|
1月前
|
缓存 监控 负载均衡
构建微服务架构下的API网关实践指南
【2月更文挑战第22天】在现代的软件开发实践中,微服务架构因其灵活性和可扩展性而成为众多企业的首选。随着服务数量的增长,有效地管理这些服务的入口——API网关,成为了确保系统稳定性和效率的关键。本文将探讨如何在微服务环境中构建一个高性能、可扩展的API网关,以及它如何帮助实现请求路由、负载均衡、认证授权和监控等功能,同时提供了一系列实施的最佳实践和面临的挑战解决方案。
|
1月前
|
监控 测试技术 API
构建基于微服务架构的高效API网关
【2月更文挑战第20天】 在现代软件架构中,微服务已成为企业开发云原生应用的首选模式。随着服务数量的增长,有效的服务管理和请求路由变得至关重要。本文将探讨如何构建一个高效的API网关来管理微服务架构中的服务交互,重点讨论其设计原则、关键组件以及实现方法。通过引入API网关,我们旨在简化客户端与服务的通信,提供统一的访问入口,增强安全性,并提升系统的可扩展性和维护性。
|
1月前
|
分布式计算 API 数据处理
Flink【基础知识 01】(简介+核心架构+分层API+集群架构+应用场景+特点优势)(一篇即可大概了解flink)
【2月更文挑战第15天】Flink【基础知识 01】(简介+核心架构+分层API+集群架构+应用场景+特点优势)(一篇即可大概了解flink)
60 1
|
16天前
|
缓存 前端开发 API
API接口封装系列
API(Application Programming Interface)接口封装是将系统内部的功能封装成可复用的程序接口并向外部提供,以便其他系统调用和使用这些功能,通过这种方式实现系统之间的通信和协作。下面将介绍API接口封装的一些关键步骤和注意事项。