优步工程师之间有一个共同的公理,即创建新功能就像一边开车一边修理汽车引擎。当我们扩大到目前每天支持1400万次旅行的水平时,这个公理中的汽车升级为喷气式客机。事实是,我们在全球700多个城市开展业务,人们依赖我们的交通和收入,没有停机时间。新特性和系统升级必须进入生产系统。
2018年,Uber的支付工程师迁移了数十种复杂支付工具的功能,同时为数亿用户保持现有的支付功能运行。这是一个困难但有益的技术挑战,它为我们在生产环境中如何运作提供了一个可靠的例子。
我们在这次迁移中使用的流程和学到的经验教训,可以应用于任何无法承受停机时间的大型系统的工程师。
背景
当优步推出前期的价格,我们开始分离授权和收费的概念。由于乘客可能会改变目的地或取消乘车请求,所以在旅程开始时收取费用是没有意义的。相反,我们会授权乘客的信用卡支付车费金额,并持有这笔资金直到旅行结束。一旦旅行结束,就会处理信用卡费用。
建立这个支付功能需要在我们的后端修改8个微服务。在接下来的几年里,我们增加了新的支付工具,让用户更方便地使用优步平台,并推出了新的业务,如优步外卖、优步货运、Jump电动自行车和滑板车。我们很快意识到,不断更新所有服务来处理每个新的用例是一种不可持续的开发时间的使用。
为此,我们在2018年启动了一个项目,允许授权保持逻辑编写一次,并在我们现有和未来的产品中使用。然而,这个项目的一个要求是,当我们迁移到这个新系统时,我们不需要任何停机时间。
预迁移
作为优步的技术堆栈从最初的单片代码库发展到基于微服务的架构,我们开始精通于进行大规模迁徙。这些以往的经验告诉我们,要想成功地进行迁移,我们需要具备以下条件:
- 跟踪:将当前生产流量转发到新系统并观察其行为
- 验证:验证新系统的正确性
阴影
将生产流量跟踪到我们的新Auth服务给了我们信任票,在迁移过程之后或期间,生产流量将被正确处理,没有任何回归(在验证部分中讨论)。另外,将生产流量投影到新服务上,可以粗略估计每秒请求数(RPS)、流量模式和其他性能统计数据。
所有原始生产请求都被转发到认证服务,使用专用的程序延迟芹菜队列.使用专用芹菜队列的优点是它们不会影响生产流中的现有作业。此外,由于我们的遗留服务是用Python(芹菜的母语)编写的,所以这是一个很容易的选择。
验证
在开始迁移之前,我们验证了新的Auth服务将正确地处理生产流量,没有任何回归。验证取决于很多因素,我们想要达到的目标是:
- 我们从Auth服务传递给支付服务提供者(psp)的请求参数必须与遗留服务相同。
- 基于PSP的响应,认证服务达到与遗留服务相同的状态。
我们只需要验证我们应该传递相同的参数并以相同的方式实现函数。在处理不同的编程语言、不同的存储模型和其他因素时,完成后一部分尤其困难。我们问自己:我们只需要相同的功能还是整个功能实现?我们决定采用前者,因为我们只需要与遗留服务匹配的功能。
另一个目标是为Uber的其他部分提供一个新的通用接口,用于对psp的Auth、Void和Capture调用。此外,我们需要根据响应管理Auth服务的状态和生命周期。为此,我们确保在执行Auth、Void和Capture调用时将相同的参数传递给PSP的端点。根据响应,需要在新数据库中更新AuthHold状态。
由于PSP是外部的,我们不希望在我们的旧系统和新系统中提出相同的请求。它将使流量加倍,PSP的行为可能是不确定的,并且不清楚哪个是请求的主要系统。
如上所述,给定一个函数(f),如果输入相同,那么输出也应该相同。这也是我们用于验证的方法。当旧系统发出请求时,我们缓存了来自PSP的请求和响应,目的是稍后将流量重放到新的Auth服务。对于缓存,我们使用复述,,一个内存中的数据结构存储,并基于' namespace ' +UUID键值,如下所示。我们选择Redis是因为它读取速度快,而且在这种情况下,存储不需要高度耐用。
哈希(' namespace ' +UUID),其中的namespace是请求或响应
利用我们所做的影子工作,我们为隐藏到Auth服务的请求添加了轻微的延迟。基于请求,Auth服务构造自己的请求参数来调用PSP的端点,我们的支付集成组件将根据原始请求参数验证请求参数。此外,由于我们不能调用PSP的端点两次,响应将从Redis中重新播放特定的请求,AuthHold状态将在我们的认证服务数据库中更新,如下图1所示:
我们还需要验证旧系统和新系统是否根据响应达到相同的AuthHold状态。
在运行了几天的验证之后,我们在不同的psp和状态转换的边缘情况下发现了数十个参数不匹配。通过多次迭代,我们修复了验证错误和状态转换错误,并保持验证运行,直到我们将所有内容迁移到新系统。这个验证步骤为我们提供了信心和证据,证明新服务可以正确地处理生产流量,而不会出现任何倒退。
推出
一旦我们对我们的验证指标有了信心,我们就最终准备好将新系统放入我们的生产流程中。首先,我们构建了A/B仪表板来监控新系统与遗留系统相比的业务级指标。我们每天都检查这些仪表盘。我们还决定最好一次推出一种支付方式,以弥补额外的差异。
对于每一种支付方式,我们都从一组员工的测试计划开始,以测试已知的成功和失败案例。然后,我们开始让所有优步员工都使用新系统,然后逐步增加现实世界的用户,从0.25%、1%、5%到100%推出。
经验教训
在优步(Uber),以及我们想象的大多数科技公司,员工迁移是不可避免的事实。每个技术堆栈都是不同的,但我们所学到的经验教训应该是普遍适用的,尤其是在不允许停机的情况下。我们的主要经验包括:
- 技术债务:推动团队为多年来产生的任何技术债务提供资金。它可以帮助团队在未来更快地前进,并提高开发人员的生产力。
- 试错:不要指望第一次就能得到正确的验证。计划多个迭代。
- 数据分析:在进行任何迁移时,有数据分析师作为团队的一部分将有助于尽早发现问题,特别是在支付领域。
- 迅速完成:最后的迁徙应该是快速的,没有返回的机会。回滚到遗留系统的选项可能会被滥用,从而阻止迁移完成。
- 迁移是一条长尾:对于采用新数据模型的消费者来说,所有数据都需要可用。我们需要在Uber的其他团队开始工作之前完成开发。
如果您对具有实际影响的动态生产环境中的系统感兴趣,请考虑申请角色在我们队!






