百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

复杂系统如何在不停机升级同时保持稳定?你必须考虑以下几个点...

off999 2025-02-21 15:02 25 浏览 0 评论

背景

在互联网行业,线上服务的升级更新可谓家常便饭。据统计,在过去的一个季度中闲鱼工程师们执行了千余次发布,总计更新的代码数量超过百万行。

这些发布中,有一些可能只更新了几行代码,而有一些可能执行了整个集群的迁移升级。而无论这些变更的影响面有多大,我们都必须保证线上服务的可用性,用户无感知。本文将以闲鱼搜索服务的迁移升级为例,向大家介绍其背后的技术方案。

闲鱼搜索服务基本架构

闲鱼的底层搜索服务由查询规划服务 Search Planner、查询理解服务 Query Planner、打分排序服务 Rank Service 以及搜索引擎 Heaven Ask 3 所组成。它们之间的相互调用关系如下图所示:

可以看到,整个搜索服务是由多个相互独立的微服务所构成的。不同的微服务之间相互隔离,通过预先向外暴露的接口提供服务。所有的微服务最终通过 Search Planner 收口,对外提供统一、完整的搜索能力。

在底层搜索服务之上,还有业务逻辑层和接入网关层,具体架构在此不再赘述。用户的搜索请求先通过网关层转发给逻辑层处理,再向底层搜索服务发起搜索请求。这条请求链上包含数十个集群,调用深度达到两位数,整个过程中提供服务的服务器数量可能有成百上千。

对于这样一个复杂的系统,升级过程显然无法一蹴而就。好消息是各个微服务之间合理的解耦合给升级工作带来了很大的便利,有效避免牵一发动全身而导致无从下手,使我们可以分门别类地处理升级问题。

  • 注1:Search Planner 是一个基于函数式、服务化、可视化、并行化开发框架所构建的搜索服务网关层。

  • 注2:Query Planner 的主要作用是理解用户输入,然后对搜索词进行算法优化。最终获得更好的搜索召回结果。

  • 注3:Rank Service 是实时打分排序服务,它的作用是根据多维度的特征对搜素引擎召回的海选结果进行算法打分。分数越高的商品就越有机会出现在搜索结果的前列。

  • 注4:Heaven Ask 3 (问天3)是阿里巴巴研发的一款稳定高效、功能强大的搜索引擎。为阿里集团包括淘宝、天猫在内的核心业务提供搜索服务支持。

保持兼容

开始升级之前,我们首先需要确认被升级的服务是否保持了向前与向后兼容性。保持兼容不仅减少了工作量,也减少了升级所导致的故障风险。

为了尽量避免升级导致的不兼容,我们可以总结一些开发原则:

  • 远程过程调用(RPC)需要能够忽略未知参数,并且允许缺失参数。

  • 如果需要删除已有参数,需要与所有依赖方确认。可以先将参数标记为 Deprecated 而不是直接移除。

  • 使用参数时,区分缺省值和缺失值。

  • 如果接口无法保持兼容,则创建新接口代替旧接口。不要破坏旧接口的兼容性。

在升级时,先升级那些没有外部依赖的服务。等到被依赖方升级完毕之后,再去升级依赖方。确定了每一个服务的升级顺序之后,我们再根据服务的实际情况确定升级方案。

无状态服务升级

正式进入升级流程,我们首先关注搜索链路中的被设计成无状态服务的部分,例如用于处理业务逻辑的 Java 微服务、用于处理查询逻辑的 Search Planner 等。它们的共同特点是,每个请求处理完毕之后,关于该次请求的资源即被释放。不同的请求之间没有相互依赖和时序要求。同一个无状态服务内不同的机器节点是完全等价的。

无状态服务的特点使得它们很容易通过水平扩展来动态扩缩容。因此在保证兼容的前提下,它们的升级流程相对通用并且简单:

  1. 根据服务最小可用度决定分批数。

  2. 选取一批待更新的容器,停止服务。

  3. 批量升级容器、更新镜像。

  4. 等待这一批容器全部恢复服务后,继续更新下一批容器。

一般来说我们可以通过把状态存储在消息队列、缓存、数据库或者其它外部中间件中来达成服务的无状态。把服务设计成无状态的好处显而易见:升级时不需要分配额外的机器资源,升级速度快,变更代价小,因而可以支持频繁的迭代更新。但是,这种设计也给状态访问和更新带来了额外的开销,在某些性能敏感的场合可能是不适用的。

有状态服务升级

我们继续关注有状态的部分。有状态服务升级的麻烦之处在于,状态的存储、恢复、转移往往由服务根据实际情况单独设计(或者根本没有设计),因而升级较为困难。我们可以简单列举一些相对通用的有状态服务升级可选方案。

  • 接入层网关提供热更新的能力(例如 Nginx),把状态的保持隔离在接入层内部。适合需要长时间保持状态的场景。

  • 渐进更新,新请求逐步切换到新服务上处理,旧服务处理完存量请求后销毁。适合短时间保持状态的场景(例如游戏服务、实时音视频通讯服务)。

  • 创建全新的服务副本,通过数据双写保持新旧服务状态一致,逐步用新服务取代旧服务。

在闲鱼搜索的架构中,搜索引擎本身提供的虽然是无状态服务,但是引擎内部保存了用于处理索引分区,增量进度的各种状态。最终使用的升级方案如下:

  1. 使用新版本镜像创建一个完全独立的新引擎。

  2. 新旧引擎全量数据同步。

  3. 增量数据同时向新旧引擎发送。

  4. 新引擎上线,逐步扩大承接流量的比例。

  5. 旧引擎不再承接流量后下线。

和无状态服务的升级相比,这种方式不仅额外使用了一倍的机器资源,而且每次升级都需要做一次复杂而繁琐的服务配置。如果服务本身不是无状态的,还需要自行编码实现切流逻辑,保证同一个用户的请求能够落到同一个集群上。整体升级成本较为昂贵,只适合更新频率非常低的服务。如果服务的更新频率较高,则应该根据服务的实际情况设计实现升级成本更低的方案。

服务发现

在升级过程中,服务发现机制承担着重要作用。它为我们提供了以下功能:

  • 保证分布式一致性

  • 服务优雅上下线

  • 负载均衡

  • 流量调控与请求降级

  • 同机房优先调度

  • 跨机房容灾调度

服务发现是流量调控的总阀门。一个成熟稳定的服务发现机制不仅可以有效避免发布导致的请求成功率抖动,也为发生异常时快速回滚止血提供了保证。

风险防控

对搜索链路的每一个集群按照依赖顺序进行服务升级、挂载、切流无疑是高危操作,稍有不慎就可能引起线上故障。因此,我们按照阿里巴巴安全生产三板斧原则对升级流程进行了梳理:

  • 可监控:重要链路的重要指标均提前保证监控覆盖。例如请求总量,请求成功率,请求响应时长等等。确保重大问题可以通过监控指标及时发现。

  • 可灰度:任何变更都不允许未经灰度直接全量发布到线上。对于无状态服务,我们一般通过调整服务发现中的权重或者调整机器比例来完成灰度放量。对于部分不能随机灰度的情形,我们设计了按用户分批放量的机制。

  • 可回滚:变更系统提供了通用的一键回滚能力,但并非是最快的方式。在很多情况下,我们在执行变更前就做好了把待更新的机器或集群在服务发现上重新挂载或移除的准备,从问题发现到恢复的时间基本是秒级的。

总结

综上所述,复杂系统不停机升级的原则和流程可以概括如下:

  1. 服务间解耦与隔离,确保单次升级的范围和影响可控。

  2. 根据兼容性和依赖关系决定服务的升级顺序。

  3. 根据服务是否无状态决定升级方式。

  4. 提前准备好监控和回滚方案,灰度升级。

闲鱼搜索服务升级的整个执行过程经历了两个月的时间。这其中我们既保证了用户无感知,线上服务稳定运行,也保证了与我们合作开发的算法团队以及其他工程团队的正常开发不受影响。

在实际执行的过程中,我们还遇到了很多细节上的问题。例如创建新服务时未能提前合理预估预算需求,导致升级过程中不断挪借预算,拆东墙补西墙。又比如异地多活部署带来的延迟问题迫使服务保持单元化,给升级过程中的流量调控工作带来了很多挑战。这些暴露的问题也为我们继续完善架构和方案提供了指引。

希望本次的分享能够给大家带来一些帮助和启发。

相关推荐

Python钩子函数实现事件驱动系统(created钩子函数)

钩子函数(HookFunction)是现代软件开发中一个重要的设计模式,它允许开发者在特定事件发生时自动执行预定义的代码。在Python生态系统中,钩子函数广泛应用于框架开发、插件系统、事件处理和中...

Python函数(python函数题库及答案)

定义和基本内容def函数名(传入参数):函数体return返回值注意:参数、返回值如果不需要,可以省略。函数必须先定义后使用。参数之间使用逗号进行分割,传入的时候,按照顺序传入...

Python技能:Pathlib面向对象操作路径,比os.path更现代!

在Python编程中,文件和目录的操作是日常中不可或缺的一部分。虽然,这么久以来,钢铁老豆也还是习惯性地使用os、shutil模块的函数式API,这两个模块虽然功能强大,但在某些情况下还是显得笨重,不...

使用Python实现智能物流系统优化与路径规划

阅读文章前辛苦您点下“关注”,方便讨论和分享,为了回馈您的支持,我将每日更新优质内容。在现代物流系统中,优化运输路径和提高配送效率是至关重要的。本文将介绍如何使用Python实现智能物流系统的优化与路...

Python if 语句的系统化学习路径(python里的if语句案例)

以下是针对Pythonif语句的系统化学习路径,从零基础到灵活应用分为4个阶段,包含具体练习项目和避坑指南:一、基础认知阶段(1-2天)目标:理解条件判断的逻辑本质核心语法结构if条件:...

[Python] FastAPI基础:Path路径参数用法解析与实例

查询query参数(上一篇)路径path参数(本篇)请求体body参数(下一篇)请求头header参数本篇项目目录结构:1.路径参数路径参数是URL地址的一部分,是必填的。路径参...

Python小案例55- os模块执行文件路径

在Python中,我们可以使用os模块来执行文件路径操作。os模块提供了许多函数,用于处理文件和目录路径。获取当前工作目录(CurrentWorkingDirectory,CWD):使用os....

python:os.path - 常用路径操作模块

应该是所有程序都需要用到的路径操作,不废话,直接开始以下是常用总结,当你想做路径相关时,首先应该想到的是这个模块,并知道这个模块有哪些主要功能,获取、分割、拼接、判断、获取文件属性。1、路径获取2、路...

原来如此:Python居然有6种模块路径搜索方式

点赞、收藏、加关注,下次找我不迷路当我们使用import语句导入模块时,Python是怎么找到这些模块的呢?今天我就带大家深入了解Python的6种模块路径搜索方式。一、Python模块...

每天10分钟,python进阶(25)(python进阶视频)

首先明确学习目标,今天的目标是继续python中实例开发项目--飞机大战今天任务进行面向对象版的飞机大战开发--游戏代码整编目标:完善整串代码,提供完整游戏代码历时25天,首先要看成品,坚持才有收获i...

python 打地鼠小游戏(打地鼠python程序设计说明)

给大家分享一段AI自动生成的代码(在这个游戏中,玩家需要在有限时间内打中尽可能多的出现在地图上的地鼠),由于我现在用的这个电脑没有安装sublime或pycharm等工具,所以还没有测试,有兴趣的朋友...

python线程之十:线程 threading 最终总结

小伙伴们,到今天threading模块彻底讲完。现在全面总结threading模块1、threading模块有自己的方法详细点击【threading模块的方法】threading模块:较低级...

Python信号处理实战:使用signal模块响应系统事件

信号是操作系统用来通知进程发生了某个事件的一种异步通信方式。在Python中,标准库的signal模块提供了处理这些系统信号的机制。信号通常由外部事件触发,例如用户按下Ctrl+C、子进程终止或系统资...

Python多线程:让程序 “多线作战” 的秘密武器

一、什么是多线程?在日常生活中,我们可以一边听音乐一边浏览新闻,这就是“多任务处理”。在Python编程里,多线程同样允许程序同时执行多个任务,从而提升程序的执行效率和响应速度。不过,Python...

用python写游戏之200行代码写个数字华容道

今天来分析一个益智游戏,数字华容道。当初对这个游戏颇有印象还是在最强大脑节目上面,何猷君以几十秒就完成了这个游戏。前几天写2048的时候,又想起了这个游戏,想着来研究一下。游戏玩法用尽量少的步数,尽量...

取消回复欢迎 发表评论: