大数跨境
0
0

一个每秒超过3万请求的微服务开发经历

一个每秒超过3万请求的微服务开发经历 二进制跳动
2024-09-14
3
导读:一个每秒超过3万请求的微服务开发经历

一、ifood 外卖平台介绍

ifood 是一家巴西外卖平台公司,平均每天可送出 100 多万份订单,且每年增长约 110%。作为外卖平台,其访问峰值多在午餐和晚餐前后,周末时会更高。在特殊日子(如营销活动时),访问量曾打破记录,平台获得历史最高峰值,例如一个微服务达到了每分钟 200 万请求的峰值。

微服务开发案例

最近,ifood 的一名工程师介绍了他们的微服务开发案例,故事中的微服务在内部被称为账号元数据。虽这是个通用名字,却也解释了该服务的目的,即处理账号的元数据。那什么是账号元数据呢?主要指非关键的用户信息,它就像一个通用存储,将不同地方的数据汇总起来,既方便客户端调用,也服务于其他微服务。这样一来,开发者只需调用一个微服务,而非 10 个。

最早在 2018 年,账号元数据建立是为了存放一些杂乱信息。因需要一点结构和查询能力,且容易扩展,所以选择了 AWS 提供的 DynamoDB。需说明的是,当时已明白系统可能会增长,公司规模也相当大,平均负载有挑战。但没预估到会从每分钟 1 万个请求增长到 20 万,最终达 200 万。

这个微服务刚发布后,使用者不多。然而,几周后的一些新架构调整,让这个系统变得非常重要。现在,账号元数据不仅在用户每次打开应用时会被调用,还被很多团队在很多不同场景下访问。所以当前的问题是,需要能够稳定地每分钟处理 200 万个请求。

微服务中元数据的存储方式

正如前面所说,这个微服务存储了账号的元数据。在数据库中,将这些元数据分割成不同的上下文,在代码中称为 namespace(命名空间)。一个客户(以 customer_id 作为分区键)可以有 1 到 N 个 namespace(作为排序键),并且每个 namespace 都有一个固定的、强制性的 schema,插入前通过 jsonschema 来定义和检查。有了这个机制,就能确保无论将数据插入哪个 namespace,都能遵从其模式和正确用法。

数据插入的方式及问题

我们采用这种方法是因为在这个系统中,读和写是由不同的团队来完成的。插入工作由数据科学团队完成,他们每天从内部工具中导出数百万条记录到这个微服务中,并通过 API 将这数百万条记录分割成每次 500 条进行批量调用。所以,在一天中的特定时间,这个微服务会收到数百万次的调用(间隔 10 到 20 分钟),将数据插入到 DynamoDB 中。如果接收 API 直接将数据写入数据库,会碰到 Dynamo 扩展的一些问题,而且响应时间过慢也是个问题。

解决方案及好处

解决这个瓶颈的方法是数据团队不直接将数据写入数据库,而是由 API 接收这批记录,并将它们发布在 SNS/SQS 消息队列上,SNS/SQS 将被另外一个模块来消费,然后验证这些记录,如果没问题,就保存在 Dynamo 上。通过这种方式,接收到这批记录的接口可以非常快速地响应,不依赖 HTTP 连接进行写入,因为与 Dynamo 的通信可能会失败,再次尝试可能会使 HTTP 响应时间变得非常慢。另一个好处是,可以通过调整队列消费程序,来控制从 SQS 读取数据以及在 Dynamo 上写入数据的快慢。

账号元数据的其他调用情况

在这个流程之外,账号元数据也会被另一个服务调用。每当平台收到一个新订单就会调用它,并更新这个订单的一些信息。鉴于 ifood 每天订单量超过 100 多万,微服务也会接受到这个数量的调用。

异常情况及处理

在这样的调用量下,出现一些异常情况也是很正常的。在我们的案例中,我们看到 Dynamo 中的一些查询耗时超过 2 - 3 秒,而 99.99% 的调用都在 17ms 以下。尽管这些慢查询每天只有几千次,但我们希望为团队提供更好的 SLA。所以我们决定如果碰到 Dynamo 超时就进行重试。也和相关团队讨论过,让他们在调用我们的 API 时配置一个低超时。他们大多数 HTTP 客户端的默认时间是 2s,所以我们改成了大约 100ms。如果他们碰到超时(比方说微服务对 dynamo 做了重试,但又失败了),他们可以重试,并且很可能会马上得到响应。

部署及测试

为了部署它,我们使用 k8s(达到 70 个左右的 pod),并随着每秒请求的增长而进行扩展。DynamoDB 被设置为供应(provision)而非按需。一个重要的步骤是确保系统能够在真正高吞吐量的情况下健康地工作,我们每天对它进行负载 / 压力测试,以确保新版本部署没有降低性能。通过这个负载测试的结果,我们可以跟踪一个接口是随着时间的推移和它的发展而变好还是变坏。

微服务的重要性及潜在问题

随着时间的推移,这个微服务变得越来越重要。如果由于某些原因出现故障,那将会是一个不可承受的问题。

解决方案

为了解决这个问题,我们要求团队通过 Kong(我们的 API 网关)来调用微服务,并在那里配置了一个 fallback。如果微服务宕机或返回 500,Kong 会激活回调,客户端会得到一个默认结果。

fallback 的具体内容

在这种情况下,fallback 目前指向一个 S3 bucket,里面有系统会提供的数据副本。虽然可能是一些过时数据,但这总比不返回任何数据要好。

【声明】内容源于网络
0
0
二进制跳动
15 年 + 技术老兵 架构师|技术总监|科技创业技术合伙人 曾任职苏宁科技、电讯盈科、联想云 专注架构设计与技术落地
内容 739
粉丝 0
二进制跳动 15 年 + 技术老兵 架构师|技术总监|科技创业技术合伙人 曾任职苏宁科技、电讯盈科、联想云 专注架构设计与技术落地
总阅读629
粉丝0
内容739