饿了么技术往事(中)

image.png

在上一篇文章《饿了么技术往事(上)》中,我介绍了饿了么最先期 All in One 阶段的架构,以及第二阶段业务系统拆分与团队运营的一些思考,以及我对于架构师职责的感觉,接下来我会详细介绍饿了么全面服务化的架构演进历程。前端

1、中间件

业务线的工程师深陷到快速的迭代和业务复杂性当中,业务的快速增加、外卖行业午晚高峰业务特色带来的并发挑战,领域拆分后所需的服务体系框架支撑,责任天然落到了中间件团队。面试

当时中间件团队主要负责的三件事就是发布系统、SOA框架、统一的数据访问层。算法

1. 发布系统

外卖业务周末的单量一般比工做日要高,可是工做日事故率要高于周末,为何?变动是万恶之源,周末不多发布。因此,发布系统接手管控,取消手动发布的模式,解决发布回滚的问题,经过发布自动化提升效率的同时,回收服务器的权限,下降安全和稳定性的隐患。固然发布系统的做用远不止于此,后续这个体系及其团队充当起了基础架构演进的核心角色。这个是后话了。数据库

2. SOA 框架

SOA框架是支撑业务服务的骨架。和多数相似框架同样,为应对复杂的服务体系,服务注册和发现,常见的基于Design for failure的设计,熔断、限流、舱壁、多集群隔离这些功能都同样。可是,较特殊的地方在于——咱们有两套SOA框架,Java 版和 Python 版。前面提到,咱们有两个主要的技术栈 —— Java 和 Python,使得咱们凡是须要 SDK 的地方,都须要支持两种语言,毫无疑问会对增长中间件团队负担。在当时确实是个难题,这个如今固然也有解,后面会提到。编程

体会和教训——是否应该统一技术栈?缓存

关因而否应该统一技术栈,没有一个标准的答案。每一个公司的技术栈和技术体系,有其造成的背景,如同架构同样,不放在上下文里面讨论合理性,每每没有结果,烟囱型也好、L型也好,只要是适合本身的技术和架构就好。安全

Python 技术栈当时已经支撑了不少核心系统,推翻现有系统,换技术栈的时间成本不可忽视。而当时市场竞争很是激烈,对于饿了么这样的创业公司,数据、时间和人是最宝贵的。并且,有一支能力很是强的 Python 技术团队,从里面抽调部分工程师,支撑 Python 技术栈的中间件建设,也不会带来额外的人力成本。维护两个技术栈,中间件团队的负担会增长,可是,换取的是时间和优秀的工程师,仍是划算。这些 Python 工程师里面,负责业务系统的不少人后来也成长为独挡一面的角色,跟上了业务快速增加的步伐(后续会有相关的内容分享)。而负责中间件的 Python 工程师,他们的一些创造性实践,也为咱们后续架构演进奠基了基础。服务器

好的技术体系和架构,起决定性的不是技术栈,最终仍是优秀的工程师。网络

3. 数据访问层

由于多技术栈的存在,DAL 层选择了中心化的方案,而没有采起 SDK 。统一的数据访问层为后续分库分表、限流保护、多数据中心上线后的数据纠偏打下了基础。为了保证系统有足够强的吞吐能力,DAL 层采起了异步 IO 的方案来处理出入流量,中间件的最高境界是你们会忘记它的存在,DAL 层涉及到底层和数据库的交互,尤其敏感,而这个中间件几乎作到了,没有出现太重大事故,也不多有开发吐槽这一层的问题。后来,这个一直稳健的团队在饿了么多数据中心建设当中,负责了核心的流量调度及容灾切换管控体系。你们都习惯了叫 DAL,不少人不知道这个系统叫 Athena。架构

基于 DAL 的上线,DBA 和 DA 这个时期就忙着给各个团队作分库分表的事情:

按业务功能领域切分——拆库
按照访问频率、动静态属性等等规则——垂直分表

基于Hash Partition(须要注意的是避免热点和Rebalance带来的成本)—— 水平Sharding

总之就是选择合适的 Partition 策略,下降数据库单个实例的负载。存储后来能支撑住千万级单量,除了上游队列的削峰、缓存的缓冲、数据库读写分离之外,也得益于适当的 Data Partition 策略。

2、大前端

其余团队还在拼命追赶业务、填坑补课的时候,大前端团队知足业务需求的同时,还为开源社区贡献出了很是优秀的产品 Element。就在你们认为这支团队会继续在前端领域上一骑绝尘下去的时候,使人没有想到的是,这个团队几年后会爆发出巨大的潜力,成为整个架构体系升级中一个举足轻重的角色。为何叫大前端,由于他们和传统的前端团队作的事情不太同样,后面会讲到。

体会和教训——找到优秀的工程师多么不容易

招聘优秀的工程师,持续招聘优秀的工程师,这是一句正确的废话。可是有多难,带过团队的应该都深有体会,特别是你的公司尚未自带光环的状况下。优秀的工程师会吸引来更多更优秀的工程师,反之亦然,面试这个过程是双向的,尤为是优秀的工程师。有业务压力的时候,主管很容易扛不住,下降要求。当时大前端团队校招淘汰率仍是挺惊人的,换来的是这个团队的工程师很高的技术素养和基本功,为后面成为一个真正的全栈团队打下了的基础。

Leader的我的能力,决定了他(她)是这个团队的地基仍是天花板。

3、大数据

基于 Hadoop、Spark、HBase 的经典大数据架构这个时候也搭建起来了,由于是自建的数据中心,因此这些产品都须要有一个专业的团队来运维,所以大数据也有了本身的运维和中间件团队。在这个阶段,在线和离线数据同步、数据治理上面还不完善,由于产品化还在路上,不少工具缺失,致使不少团队都要本身直接去从数仓取数,不得不维持运营团队支撑定制化的手工取数需求。各个团队喊得最多的就是大数据的人不够,想要本身作。核心仍是业务发展太快。后面随着大数据团队逐渐壮大,更多强援加入,各个产品相继成熟才得以缓解。

4、风控安全

这是一个不得不说,可是也不能说太多的团队,因此这部分只能务虚一些,任何一个到了必定规模的企业,风控安全团队是“真”底线。其余技术团队在面对这个一样是负责技术的团队面前,有时候确实也挺一言难尽的,这个时候高层的支持相当重要。尤为是从 0 开始建设这个团队,对内的扫盲和对外风控,同样艰难。

若是说一个技术公司,系统毁了,有什么还能留下来,就还能重建,那确定是数据(如今可能还要加一个算法模型)。有什么缺失了,随时均可能垮掉,那确定是风控安全。

饿了么的这支风控安全团队,对内、对外、对线上、对线下、对其余……都面临不少挑战和冲突,堪称业务专家的羊毛党和无孔不入的黑客,确实使人叹为观止。而咱们的风控也经历了从开始的粗粒度约束、到依赖业务规则针对各类补贴、帐期等场景兜底、再到依赖算法模型实时风控主动拦截的阶段。

若是你们身边有作风控安全的同窗,请珍惜,哪怕他们有时候看到系统处处是窟窿的时候,脾气暴躁。由于他们成天面对这么多黑暗面,还能对这个世界报以但愿。开个玩笑,从人道的角度出发,这个团队须要按期的心理按摩。

这个阶段,咱们初尝了算法的威力。一开始只有搜索,可是尚未推荐召回系统,当时给推荐系统的物理机是咱们能拿得出手的最好的物理机,其余业务系统分配的大都是虚机。系统上线之后,效果、转化率都还不错。以后不久这一待遇被另外一个团队承包——负责配送履约的智能调度团队,大数据、机器学习、算法模型须要充分发挥功效,须要长时间紧贴业务、深入理解业务,在智能调度领域咱们也作过很多艰难的尝试、吃过不小苦头,直到咱们有了本身的算法专家团队。

这个阶段咱们还经历了第一次外卖行业的大促——517大促,让你们真切感觉到了这个市场的巨大潜力,同时系统的一系列短板也暴露无遗,除了积累了大促的经验之外,更大的收获是让咱们看到架构还有很大的升级空间。还收获了一支全链路压测团队,他们在从此架构升级以及系统质量、容量等稳定性保障过程当中,扮演了关键角色。

在饿了么技术往事系列文章的开篇,我提到了饿了么的技术体系经历了如下四个阶段:

核心系统 All in one 的早期架构;

以系统领域化拆分、业务系统和中间件等基础设施分离为基础的全面服务化的架构;
随着自动化平台、容器调度体系成熟,治理从传统运维向 DevOps 转变的基础设施体系;
多数据中心体系基础上的 Cloud Ready 架构成型。

如今咱们前两个阶段基本完成了,开始了相对而言最艰难的阶段了……

第三阶段:脆弱的系统,苦逼的运维

这个阶段,咱们的业务已经发展到必定规模,系统的长时间抖动或者崩溃,很容易上热搜,尤为是饭点时段。发生事故时候,冲在第一线的除了各业务线的工程师,还有运维团队,他们每每是最早响应,排障冲在第一线的团队。这个阶段说是靠他们生扛顶住了稳定性的压力也不为过:平常基础设施部署、事故发生时的应急响应、事故发生后的基础设施优化和改进措施落地,他们都承担了不少。

事故的教训,也让咱们学会了遵循一系列业界积累下来的设计原则,为架构演进到下一阶段打下基础。

业务领域拆分、基础设施和业务系统分别建设后,给业务快速发展解绑了。可是包括稳定性在内的一系列挑战依然须要面对:

基础设施部署的标准化

系统的生命周期怎么管理?
每次故障都是昂贵的学费,故障能够避免吗?

复杂性带来的挑战:团队里面几乎没有人面临过这个体量的业务、这个复杂度的系统。快速交付的同时,如何保证系统的稳定和健壮?

咱们的系统架构接下来如何演进?

  1. DevOps

由于云上资源的灵活性,咱们在云上搭建了两个测试环境:alpha做为开发环境,用于软件工程师平常开发调试;beta做为集成测试环境,用于测试工程师完成系统交付上线前的集成、回归测试。费了九牛二虎之力才达成全部团队的共识,推进beta环境的系统和数据的完整性建设。在这里面发挥重要做用的,除了各个业务的开发、测试、运维团队,还有一个就是以前提到的负责发布系统的团队,这个团队不只仅提供了一个简单的发布系统,基于持续集成和持续部署实现的开发、测试、生产环境类似化,是咱们的系统架构继续演进的开端。

技术团队职责细分后,运维团队提供了保姆式的服务,这把双刃剑的另外一面,就是开发团队很容易造成惰性,对本身的系统管生无论养,对系统的容量、治理关心不够,由于有运维团队。这就带来不少问题,代码不是运维工程师写的,可是有些团队系统甚至是运维工程师部署的。由于开发团队最贴近业务需求,需求变动可能带来将来的潜在容量风险,他们比较有发言权;而容量水位的现状反过来是运维团队更了解。由于这个时候,不少基础设施运维还没彻底自动化,因此难以统一化、标准化,每一个运维工程师都有本身的运维风格,平常排障上,有时候须要开发和运维一块儿才能完成。

此外,只生不养的思惟方式,客观上也容易形成算力成本变成糊涂帐。这个时候,开发、部署、系统运营(治理)角色的不统一带来的问题就会凸显。

应用Owner要成为名副其实的Owner,须要有应用的全景视角,对应用生命周期的把控能力。这个阶段,开始推进从虚拟化到容器化的转型,发布系统从一个简单的CI、CD的体系,延伸到了算力和调度的领域。基于一系列运维自动化工具的建设和全面容器化调度的实施,从而带来标准化的运维,才能把开发工程师(应用的Owner)推到应用完整的生命周期运营的位置上,胜任DevOps的角色。这个时候,事实上底层的算力平台,已经具有云上PaaS的雏形了。

在这个过程当中,也作了很多尝试,好比,为了提升 alpha/beta 这两个测试环境的基础设施交付效率,有过一段时间基于 slack 的 ChatOps 实践,工程师都比较欢迎;还有过 Infrastructure as Code 和 GitOps 的实践,很惋惜当时各方面条件和时机都不够成熟,没有持续推广。

体会和教训——DevOps

alpha 和 beta 环境:

工程师在开发机上自测是否是就能够了,“在我机器上是好的”这句话估计开发工程师都说过或者听过,在开发阶段提供alpha环境,目的就是为了开发、测试、生产环境的尽可能接近,避免因为开发、测试、生产三个阶段因为环境差别巨大带来的问题。解决不了“在我机器上是好的”这个问题,没有办法大规模顺利上云。工程师本身的电脑,某种程度上是一台“mommy server”,上面运行着须要的一切环境,并且每一个工程师的祖传环境还不同,这类环境在生产上是不可复制的。

Build & Release:

怎么作到高质量快速交付,保证系统的稳定?

在快速迭代的同时,作到快速试错、快速纠错、快速回退。须要发布系统作到每一个编译的版本、每次发布的版本,像代码同样,可回溯可跟踪。关键在于build和release是immutable的

首先,build和release有惟一的ID,才可追溯,可回滚;

其次,是配置分离,把和环境(dev/test/product)相关的config从代码中剥离开来,不然系统很难迁移,更不用说大规模上云。第一反应多是,把和环境相关的config写在xml或者yaml文件就能够了,可是,这些文件也是代码。

相似的,将这些随环境变化的config写在发布流水线的脚本里面,都不是完全分离的方式。由于发布环境会发生变化,可能未来有更多的测试环境、更多的数据中心、每一个数据中内心面可能还有多泳道。

所以,要作到“build once, deploy many times/every where”,config要存储在环境的上下文中,好比开发、测试、生产环境各自有一个配置中心,线上系统拉起的时候,先从配置中心拉取配置信息。要衡量环境相关的config和代码是否已经分离,看看能不能开源就知道了(抛开价值和代码质量不谈)。

OPS

接触过传统的运维工程师都知道,这是一群责任心极强的人(删库跑路,铲平数据中心的事情是不可能干出来的,虽然有能力……),他们维护着系统的底线,第一次517大促事故的时候,咱们靠运维工程师救了你们一命。

可是,即便有操做的SOP,只要是人,执行重复任务的次数足够多,总会犯错。而每一个资深的运维工程师,都有本身祖传的脚本,一夫当关万夫莫开,可是休假就麻烦了,特别是在高铁上信号很差的时候……最佳实践→ SOP → 脚本 → 自动化工具产品,沿着这个路径迭代彷佛不可避免。

传统的运维工程师角色的演进方向,一个是为云上的IaaS/PaaS服务,对操做系统和底层硬件有着丰富经验的,仍是运维工程师,他们当中开发能力强的,转型SRE,对运维产品理解深的,能够选择 Technical Product Manager 的角色,为云上运维相关平台产品提供解决方案,或者凭借丰富的云上系统落地实施经验,为各上云企业提供实施方案。

另外一个方向,因为合规和其余缘由,还有部分没有上云的企业,依然须要基础设施运维工程师。随着云逐渐变成和水电煤同样的社会基础设施,运维工程师只写操做系统脚本、实施部署的时代已经渐行渐远了。

架构的历次演进,和几回事故或者险些酿成事故的“冒烟”事件,有着很大的关系:

交易系统崩溃的“饿死了”事故,咱们开始分离关键路径和非关键路径,建设了非关键路径的降级能力。故障应急响应常规三板斧:重启、回滚、降级,至此完备。

第一次 517 大促入口崩溃的事故,是咱们核心系统上云的开端。

F5 的 CPU 被打满,让咱们意识到网关做为入口难以扩展的巨大风险,从而基于从新构建的大网关体系,取代了 F5 这一层硬件负载均衡。大网关体系是咱们多数据中心架构最核心的系统之一。

基于 VIP 的 keepalived+HaProxy 负载均衡体系下,各类 failover 和上下游频繁扩缩容过程当中,相关的稳定性冒烟或者事故频发,促成了充当 data plane 的 sidecar  上线,这是咱们构建类 Service Mesh 架构体系最重要的组件。

核心交换机 bug 引起的数据中心故障,对咱们下决心建设多数据中心体系有着很大的影响

关于这些事故和架构的故事,随着架构的演进,后面会逐个展开。

那个时候,咱们经常自嘲是“事故驱动”型开发(Disaster Driven Development)。不少工程师除了本身的工位,在公司里面最有“感情”的就是整面墙都是监控大屏的NOC做战室,大小事故、各类大促活动值守,熬夜全链路压测,里面经常挤满熟悉的面孔。

体会和教训——

(1)事故复盘

事故复盘和按期的故障验尸总结会是一个很好的机制。很容易被忽略的是,除了找到事故发生的 root cause,还须要从中发现存在的隐患,而不是 case by case 的解决问题,复盘的目的是阻止相似的事情再次发生,必要的时候,能够引入业务、产品、技术共同解决。

另外一个陷阱是,故障复盘变成追责的过程,那么参与复盘的各方就很容易陷入互相指责、洗脱责任的怪圈,反而忘记了复盘的根本目的,也容易浪费大量时间,引发没必要要的内耗。只要是参与复盘的人,都是有责任在身上的,为未来的故障负责,若是相似事故再次发生,或者没有在复盘中发现应该发现的隐患,参与的人都难辞其咎。

复盘结果要避免惩罚为目的 —— 除非违反了规章制度(底线,不排除有些是恶法,但不在讨论范围内)。不然甩锅、不做为的氛围会日渐滋生,自省有担当和有做为的我的或者团队,很容易成为吃亏的一方。事故复盘的过程,是了解各个团队甚至组织文化的一个视角。

(2)弹性设计

物流、交易经历事故后,各自采起的措施再次印证了,反脆弱的设计是咱们的应用发展到今天的核心设计思路之一。

传统思路是基于一个上下文可控的理想系统环境下作出的设计,尽可能避免一切意外的发生。而反脆弱的设计,偏偏假设黑天鹅事件必定发生,是墨菲定律的信徒,开句玩笑话,云厂商若是承诺你“咱们必定会挂”,你必定要珍惜,你面对的是一个坦诚相待的乙方,值得托付。这不是推责给云厂商,这是由云上基础设施的特征决定的,大多数场景下,云上提供的服务是基于大规模标准化服务器(Off-the-shelf hardware)构建的虚拟化、容器化基础设施(Immutable Servers),而不是超高规格的个性化定制独占设备(Snowflake Servers)——没法规模化,成本也会大规模上升,所以,会更注重快速恢复能力,水平扩展能力,总体的健壮性,而不是具体某一个单机 SLA。

因此云上系统更强调算力的抽象,CPU核数、内存、网络带宽,把数据中心看做一个超级计算机,和 CPU 具有纠错机制同样,云上基础设施不是不会发生错误,只是结合它的“操做系统”(好比 Kubernetes),提供的是纠错能力(好比容器的故障转移 —— 故障容器销毁,新容器拉起,本质上也是冗余),而云上业务系统须要适配这类纠错机制实现本身的自愈 —— 面向云编程 —— 接受短期的抖动(Transient Fault)会不时发生的这一个事实。

物流经过补偿机制加强本身的健壮性,交易引入 chaos engineering,都是基于这个上下文。要求应用是 stateless 或者 disposable 的,目的是为了 crash 后可以迅速拉起,快速自愈——因此,尽可能分布式缓存,尽可能少本地缓存,应用拉起时初始化的工做尽可能少,交给独立的服务干这些事。业界的不少模式实践:bulkhead, circuit breaker, compensation transaction, retry都是指向提高系统的弹性(resilience),足够健壮的系统可以在经历系统抖动后,迅速自愈。

故障和意外同样,难以免。咱们能作的是减小人祸,敬畏生产环境,由于一次故障影响的多是骑手一天的生计、商户一天的营收、用户的每日三餐。同时,提升系统的健壮性和自愈的能力,在故障发生的时候,尽量的避免演变成更大的灾难,及时止损。

2. 黑天鹅

这个阶段,咱们经历了一个大事故,原由就是核心交换机挂了,可能有人问,不都堆叠的吗,不都有主备吗,不都自动切换的吗,说得都对,可是都挂了。由于交换机的一个bug,主备切换后,备机也很快被网络风暴打挂,没经历过咱们也不相信。此次又“饿死了”,咱们只能坐等供应商的工程师抱着设备打车到机房更换,这个时候,一群人挤在应急响应指挥室(NOC做战室)里一点办法都没有。

在第一次517大促以后,咱们就开始第一次容灾尝试了,当时采起的是最快最简单粗暴的方案,用最短的时间,在云上搭建一个了灾备环境并跑通了业务链路。但这是一个冷备的环境,冷备最大的风险,就是平常没有流量,真正 failover 切换的时候,有比较大的不肯定性。此次事故再加上另外一个因素,咱们下决心将技术体系推动到下一个阶段。

体会和教训——上云

2016年第一次517大促,10点开抢的瞬间,咱们系统崩掉了,要不是当时一个很稳的运维工程师,淡定操做限流,可能很多人在饿了么的职业生涯当时就结束了。由于对当时的基于Nginx和部分自研插件的网关层比较自信,不相信网关层会顶不住,因此全链路压测的时候根本没有压这一层,过后复盘的时候发现是操做系统一个参数配置的问题,若是压测必定能重现。

由于业务的效果很好,大促就成为常态,事实上第一次大促,咱们是在本身的IDC里面用常规业务系统来扛的,因此影响到了非大促的正常交易。后面专门针对大促高并发大流量的场景设计了一套系统,也是隔离、排队、CDN、限流这些常规的套路,没什么特别的。可是,对咱们影响更深远的在于,这套体系彻底是在云上搭建的,2016年以前虽然云上有系统,可是生产环境流量不多,顶可能是短信触达这类系统在上面,更可能是用于搭建测试环境。在当时看来,云上强大的流量清洗、资源 scale out 能力,很适合大促的场景,后面,这套体系经历了屡次大促,没有波澜。

在云上搭建大促体系以及灾备节点的经历,让咱们后续在云上搭建全站的网关,并进一步构建整个数据中心,有了很是大的信心。下一篇我将继续介绍饿了么架构演变到了Cloud-Ready的状态,技术体系演进为业务发展提供了更多可能性。

做者介绍:黄晓路(脉坤),2015年10月加入饿了么,负责全局架构的工做。

原文连接本文为阿里云原创内容,未经容许不得转载。

相关文章
相关标签/搜索