云架构的进化史:从单体应用到云原生 trifecta 的演进之路
2018年的杭州梦想小镇,共享办公空间的灯常常亮到凌晨三点。刚毕业的计算机系学生林晓攥着皱巴巴的商业计划书,和两个合伙人在白板上画满了电商系统的功能模块:用户注册、商品展示、购物车、下单、支付、售后,一共七大模块。团队只有五个人,两个前端、两个后端、一个兼做产品和运维的“万金油”阿凯,“先把东西做出来跑通,比什么都重要”是所有人的共识。经过两个月的通宵赶工,基于Spring Boot+Vue技术栈的单体应用正式上线,所有代码打包成一个JAR包,部署在2核4G的ECS云服务器上,商品图片存在对象存储OSS里,用户和订单数据存在云数据库RDS中。上线第一天就跑了1000个订单,林晓盯着后台的GMV数字,觉得手里的冰美式都甜了几分。
最初的两年里,单体架构给云小铺带来了实实在在的便利:新功能开发快,加个商品分类只要改两个接口,联调一天就能上线;运维简单,只要盯着这一台ECS的CPU和内存就行,出问题重启一下就能解决;团队协作也顺畅,五个人都在同一个代码库里干活,代码冲突最多半小时就能解决。到2019年初,云小铺的日活已经稳定在5000左右,月GMV突破200万,在垂直手作饰品赛道里已经小有名气。
第一次阵痛:单体架构的极限与2019年618的崩溃
2019年618大促是云小铺的第一次大考。运营提前两周在抖音和小红书投了信息流广告,预热期的商品浏览量就翻了10倍,阿凯特意把ECS升到了8核16G,数据库也做了读写分离,看起来万无一失。然而大促第一天早上10点,监控告警突然炸了:ECS CPU占用率100%,数据库连接池全部占满,用户下单页面一直转圈,支付成功率掉到了30%。客服的电话在十分钟内被打爆,后台的退款申请像雪片一样涌进来,林晓盯着屏幕上手抖的订单数据,脸瞬间白了。
阿凯抱着笔记本电脑蹲在云服务商的机房冷风里,手冻得有点僵,连续重启了三次服务器,才勉强把系统拉回来,但此时距离故障已经过去了2小时,直接损失GMV 120万,后续的一周里,因为用户体验差导致的流失率达到了15%,相当于一个月白干。
事后团队复盘,单体的所有痛点都在这次故障里集中爆发:首先是迭代效率的天花板。为了应对大促,团队提前一个月就想加个“大促专属优惠券”的功能,但因为要同时修改商品、订单、营销三个模块的代码,联调就花了3天,发布还要凌晨停服1小时,最后只能放弃,眼睁睁看着流量流失。其次是扩容的粗放性。为了应对大促流量,阿凯提前一周把ECS升到了8核16G,但大促结束后,日常流量只有大促的1/10,服务器利用率常年只有10%左右,每个月浪费的云成本就有2000多块。更致命的是故障影响面的不可控:后来查日志才发现,故障的根源是商品模块的一段慢SQL,因为大促时商品查询量翻了10倍,直接把数据库拖垮,进而导致整个单体应用全部卡死——一个非核心模块的bug,拖垮了整个业务。
这次故障让林晓意识到,单体架构已经成了业务发展的瓶颈。恰逢团队刚扩招到10个人,新增了两个业务线:一个是ToB的企业定制礼品线,一个是C端的直播带货线,原来所有人都挤在同一个代码库里的开发模式已经难以为继:上周为了改一个商品详情页的bug,两个开发人员的代码冲突了3个小时,最后还误改了订单的退款逻辑,导致100多个订单被重复退款,损失了近20万。林晓拍板:必须做架构改造,第一个方向就是微服务。
> 【纸书侧边栏】康威定律的第一次应验:1967年计算机科学家梅尔文·康威提出“设计系统的组织,其产生的设计等同于组织之内、组织之间沟通结构的拷贝”。云小铺5人团队时,单体架构刚好匹配扁平化的沟通结构;当团队扩张到10人、业务线增加到3条时,单体架构的代码库已经成了沟通效率的瓶颈,架构改造势在必行。
> 【电子版交互】点击观看2019年618故障复盘会的录音片段:[链接],听技术团队当时的讨论细节。
第一次架构演进:微服务拆分与混乱中的成长
技术负责人老周是公司里最早接触微服务的开发,他花了整整一周时间研究阿里、京东的微服务落地案例,最后基于领域驱动设计(DDD)的方法论,把云小铺的单体应用拆成了7个独立的微服务:用户服务、商品服务、订单服务、支付服务、营销服务、物流服务、客服服务,每个服务对应一条业务线,由独立的业务团队负责开发、测试、部署,服务之间通过RESTful API调用。
拆分的过程比预想的要狼狈得多。第一次拆分上线就出了事故:大促前的压测里,订单服务调用商品服务查询库存时,因为商品服务的响应时间超过了1秒,触发了超时重试,导致商品服务的请求量翻了3倍,直接把商品服务打挂,进而导致整个下单链路全部失败。团队蹲在会议室里改了三天代码,加了熔断降级、超时重试、服务隔离的配置,才把这个问题解决。紧接着又遇到了分布式数据一致性的问题:有一次用户支付成功之后,订单服务写入订单状态成功,但库存服务因为网络波动没收到扣减库存的请求,导致商品超卖了200件手链,用户投诉的电话又打爆了客服。后来团队引入了最终一致性的方案,通过消息队列保证库存扣减的可靠性,才把这个问题解决。
最麻烦的是运维复杂度的飙升:原来部署只要传一个JAR包到ECS就行,现在要部署7个服务,每次发布都要盯着7个服务的启动日志,出问题的时候根本不知道是哪个服务挂了,有一次商品服务挂了,团队花了1个小时才定位到根因,导致故障时间大大拉长。后来老周咬着牙上了服务治理框架,加了链路追踪、监控告警、日志聚合的体系,才慢慢把运维的复杂度降下来。
但微服务带来的收益也是实实在在的:拆分之后,每个业务团队可以独立迭代,商品团队改详情页不用再等订单团队排期,发布频率从原来的每月1次提升到了每周2次;大促的时候可以只扩容订单、支付、商品三个核心服务,IT成本比2019年降了40%;故障影响面也大大缩小,2020年双11的时候,营销服务因为bug挂了,但用户依然可以正常浏览商品、下单支付,只影响了优惠券的使用,故障恢复时间从原来的2小时降到了20分钟,最终2020年双11的GMV突破了800万,是2019年的4倍,系统全程没有出现大的故障。
老周后来在复盘会上总结:“微服务不是银弹,它把‘代码的复杂度’换成了‘架构和运维的复杂度’,但当你团队超过10个人、业务线超过3条的时候,这点架构的复杂度,远比几十个人挤在同一个代码库里的效率损耗要划算得多。”
> 【纸书插图1-1】云小铺2020年微服务架构拓扑图(见正文第32页) > 【电子版交互】点击查看云小铺微服务架构的动态调用链路演示:[链接]
第二次架构演进:Serverless迁移与成本的结构性优化
微服务解决了迭代和故障的问题,但2021年新的问题又来了。云小铺的业务开始出现明显的波峰波谷:平时日活10万,大促的时候能冲到1000万,直播带货的时候流量更是忽高忽低,有时候一场直播只有几千人看,有时候突然爆火,几十万人同时涌入。用ECS部署微服务的话,为了应对峰值流量,必须提前预留大量的服务器,但日常的利用率只有15%左右,每个月浪费的云成本就有近1万块。更麻烦的是新业务的试错成本:比如运营想做个“每日签到领积分”的小功能,原来要申请服务器、部署环境、配置域名,前后要一周时间,等上线的时候,运营的热度早就过了。
技术团队开始调研Serverless架构,最后选定了阿里云函数计算(FC)作为迁移目标,因为云小铺早就用了阿里云的ECS、RDS、OSS,生态兼容性好,迁移成本低。迁移的策略是先迁边缘业务,再迁核心业务的非核心逻辑:第一批迁的是图片处理逻辑,原来用ECS跑定时脚本处理用户上传的商品图片,压缩、加水印、转格式,每个月ECS成本是2000块,迁到函数计算之后,按调用次数付费,每个月只要300块,成本直接降了85%。第二批迁的是营销活动的静态页面,比如拼团、秒杀的活动页,原来要提前一天扩容服务器,现在直接托管在函数计算的Web托管服务里,自动扩缩容,2021年618秒杀的时候,流量是平时的100倍,函数自动扩容了2000个实例,全程没有卡顿,成本只比平时多了2倍,要是用ECS的话,要提前预留50台服务器,成本是平时的10倍。第三批迁的是核心链路的无状态逻辑,比如订单的合法性校验、优惠券的核销,迁到函数计算之后,弹性能力更好,大促的时候不用再提前扩容,系统自动就能扛住峰值。
Serverless的迁移也不是一帆风顺的。第一个问题是冷启动延迟:函数计算是事件触发的,第一次调用的时候需要初始化运行环境,延迟能达到1-2秒,用户能明显感觉到卡顿。后来团队给核心函数加了预留实例,冷启动延迟降到了200毫秒以内,用户体验几乎没有影响。第二个问题是调试麻烦:原来开发的时候本地就能跑,现在要上传到云端才能测试,后来用了开源的Serverless Devs工具,支持本地模拟函数计算的运行环境,调试效率提升了好几倍。第三个问题是厂商锁定:原来的代码耦合了阿里云函数计算的API,要是换云厂商的话要重写一遍,后来团队用了开源的Serverless框架,抽象了底层API,支持多云部署,缓解了厂商锁定的问题。
迁移完成之后,云小铺的整体IT成本又降了35%,弹性场景的扩容时间从原来的1小时变成了秒级,新业务的上线时间从1周变成了1天,运维团队再也不用在大促前熬夜扩容服务器,阿凯甚至有时间去学做运营的活,帮团队做了好几次活动策划。林晓后来开玩笑说:“以前我们是给云厂商打工,买一堆服务器放着吃灰,现在我们是按实际用的量付费,每一分钱都花在刀刃上。”
> 【纸书侧边栏】Serverless核心概念:FaaS(函数即服务)和BaaS(后端即服务)。云小铺用的函数计算属于FaaS,只要上传代码,就能按调用次数和执行时长付费,不用管服务器的运维;OSS、RDS属于BaaS,是云厂商托管的 backend 服务,不用自己搭建维护。
第三次架构演进:事件驱动架构与业务敏捷性的爆发
2022年,云小铺的业务又上了一个台阶,团队扩张到了50个人,业务线也增加到了8条,新的问题随之而来:订单支付成功之后,要同步调用库存、物流、积分、客服、数据平台5个接口,整个下单链路耗时2秒,用户体验很差;而且只要有一个下游接口超时,整个下单就会失败,有一次库存系统升级,导致整个下单链路挂了10分钟,损失了近50万GMV。更麻烦的是业务迭代的效率:运营每次想加个新功能,比如“下单之后给用户发优惠券”“下单之后给用户加会员等级”,都要改订单服务的代码,排期、测试、发布,最少要3天,运营觉得技术团队太慢,技术团队觉得需求太杂,两边经常吵架。
技术团队开始调研事件驱动架构(EDA),最后选定了阿里云EventBridge作为事件总线,RocketMQ作为消息中间件,把所有业务事件都抽象成标准化的事件,发布到事件总线上,下游服务只需要监听自己感兴趣的事件就行,不用再同步调用。
落地的最典型场景是订单全链路的事件化:用户下单支付成功之后,订单服务不再调用下游的5个接口,而是只发布一个标准化的「OrderPaid」事件到事件总线上,事件里包含了订单ID、用户ID、商品ID、支付金额、支付时间等所有必要的信息。库存服务监听到「OrderPaid」事件,自动扣减对应商品的库存;物流服务监听到事件,自动生成发货单;积分服务监听到事件,自动给用户加100积分;客服系统监听到事件,自动给用户发下单成功的短信通知;数据平台监听到事件,自动把订单数据写入实时数仓,运营大屏上的GMV、订单量、用户复购率等数据实时更新,不用再等T+1的离线数据。
这个改造带来的变化是颠覆性的。首先是链路性能的大幅提升:原来同步调用5个接口要2秒,现在只要发布一个事件,耗时只有500毫秒,用户体验好了很多。其次是故障隔离:有一次积分系统升级挂了,完全不影响用户下单,只是用户暂时拿不到积分,等积分系统恢复之后,从事件总线里重放没处理的事件就行,不用回滚订单服务。最重要的是业务敏捷性的爆发:运营想加个“下单之后给用户发10元优惠券”的功能,原来要排期2周,现在开发人员只花了2小时,写了一个新的消费者服务,监听「OrderPaid」事件,处理完之后发优惠券就行,不用改订单服务的任何代码,测试完直接就上线了。后来运营又提了好几个类似的个性化需求,比如“下单之后给高价值用户送小礼品”“下单之后给用户推送相关的商品推荐”,都是几个小时就上线了,运营团队对技术团队的满意度直线上升。
到2022年底,云小铺已经把所有核心链路的事件都做了标准化,一共抽象了120多个业务事件,新业务的上线周期从原来的平均7天降到了2天,订单链路的可用性从99.5%提升到了99.99%,故障率降了60%。林晓在年会上说:“以前我们做架构是给技术自己减负,现在做架构是给业务加速度,技术不再是业务的瓶颈,而是业务的引擎。”
> 【电子版交互】点击观看云小铺事件驱动架构的动态演示视频,看一个订单从支付到全链路流转的全过程:[链接]
演进的本质:没有最好的架构,只有最适合的架构
回顾云小铺四年的架构演进之路,其实没有哪一次是“未雨绸缪”的规划,全都是被业务推着走的:单体架构是创业初期快速验证模式的必然选择,微服务拆分是业务和团队扩张后的无奈之举,Serverless迁移是降本增效的自然选择,事件驱动架构是业务敏捷性需求倒逼的结果。
三种架构的核心价值和适用场景也完全不同:
- **单体架构**的核心是“简单”,适合创业初期团队小于10人、业务复杂度低、需要快速验证商业模式的场景,它的优势是开发快、运维简单,缺点是迭代慢、扩容难、故障影响面大,是业务从0到1的最佳选择。
- **微服务架构**的核心是“拆分”,适合团队大于20人、业务复杂度高、需要独立迭代多个业务线的场景,它的优势是迭代快、故障隔离、可独立扩容,缺点是架构和运维复杂度高,是业务从1到10的必经之路。
- **Serverless架构**的核心是“弹性”,适合流量波动大、弹性需求高、需要快速试错新业务的场景,它的优势是自动扩缩容、按需付费、运维成本低,缺点是冷启动延迟、厂商锁定,是降本增效的利器。
- **事件驱动架构**的核心是“解耦”,适合异步流转、多系统协同、需要快速响应业务变化的场景,它的优势是解耦、异步、可追溯、业务敏捷性高,不适合强一致性的核心交易场景,是业务从10到100的效率加速器。
老周后来在团队的内部分享里说:“很多人问我们为什么一开始不直接上微服务、上Serverless,其实架构选型从来没有‘最优解’,只有‘当前阶段的最优解’。创业初期你上微服务,等拆分完黄花菜都凉了;业务流量稳定的时候你上Serverless,反而可能因为冷启动问题影响用户体验。架构是跟着业务走的,不是跟着技术潮流走的,能解决当前业务痛点的架构,就是最好的架构。”
> 【Markdown结构化标签】 > #云架构演进 #单体架构 #微服务 #Serverless #事件驱动架构 #云小铺案例 #架构选型
```mermaid timeline title 云小铺架构演进时间轴(2018-2022) section 2018年:单体架构时代 创业初期上线 : 5人团队,Spring Boot单体,1台ECS 日活5000,月GMV 200万 : 迭代快,运维简单 section 2019年:第一次架构危机 618大促崩溃2小时 : 损失120万GMV,单体痛点集中爆发 决定拆分微服务 : 匹配团队扩张和业务复杂度提升 section 2020
```mermaid timeline title 云小铺架构演进时间轴(2018-2023) section 2018年:单体架构时代 创业初期上线 : 5人团队,Spring Boot单体,1台2核4G ECS 日活5000,月GMV 200万 : 迭代周期1个月,年云成本8000元 section 2019年:第一次架构危机 618大促崩溃2小时 : 损失120万GMV,单体痛点集中爆发 启动微服务拆分 : 按DDD领域驱动拆分为7个独立服务 section 2020年:微服务落地期 双11 GMV突破800万 : 系统无重大故障,发布频率从月更到周更 IT成本较2019年降40% : 故障恢复时间从2小时降到20分钟 section 2021年:Serverless迁移期 启动Serverless迁移 : 先迁边缘业务,再迁核心非核心逻辑 年云成本较2020年降35% : 新业务上线周期从1周降到1天 section 2022年:事件驱动落地期 订单全链路事件化 : 抽象120+标准化业务事件 年GMV突破5000万 : 订单链路可用性从99.5%升到99.99% section 2023年:云原生Trifecta落地 微服务+Serverless+EDA组合 : 核心/弹性/异步场景分别匹配最优架构 618大促GMV突破1.2亿 : 峰值流量是日常200倍,系统无故障 ```
2021年春节刚过,云小铺的技术团队就开始了Serverless的迁移工作。老周把迁移路线分成了三步:第一步迁非核心的边缘业务,第二步迁核心业务的非核心逻辑,第三步迁核心业务的弹性逻辑,每一步都要做至少一个月的灰度验证,不能影响线上业务。
第一批迁移的是图片处理服务。原来云小铺的用户每天会上传近10万张商品图片,团队用ECS跑了一个定时脚本,每10分钟扫一次OSS里的新图片,压缩、加水印、转WebP格式,再存回OSS。这台ECS的配置是2核4G,每个月成本是2100元,但实际CPU利用率只有8%左右,大部分时间都在空转。迁移到阿里云函数计算之后,改成OSS事件触发:用户上传图片之后,OSS自动触发函数,处理完之后存回OSS,按调用次数和执行时长付费,每个月平均调用30万次,每次执行时间平均200毫秒,算下来每个月只要287元,成本直接降了86%。
但迁移的过程中也踩了不少坑。第一个坑是权限配置问题:原来的ECS脚本用的是ECS的RAM角色,默认有OSS的读写权限,迁移到函数计算之后,团队忘了给函数配置对应的RAM权限,导致函数运行的时候一直报“Access Denied”错误,查了整整3个小时才找到原因。后来团队总结了一套《Serverless迁移权限配置Checklist》,每次迁移之前都要检查IAM角色、策略绑定、环境变量配置三个核心项,再也没出现过类似的问题。第二个坑是依赖包体积问题:原来的图片处理脚本依赖了PIL库,打包之后的函数包有200多M,上传到函数计算之后,冷启动时间长达3秒,用户上传图片之后要等3秒才能看到处理后的结果,体验非常差。后来团队把依赖库换成了更轻量的Pillow-SIMD,并且把公共依赖打包成了层的共享包,函数包大小降到了10M以内,冷启动时间降到了800毫秒,再后来给核心函数配置了预留实例,冷启动时间进一步降到了200毫秒以内,用户完全感知不到延