You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
14 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 13 | 高可用架构案例如何实现O2O平台日订单500万
你好,我是王庆友。在上一讲中,我和你介绍了高可用系统的设计原则和常见手段。今天呢,我会通过一个实际的案例,告诉你具体如何落地一个高可用的架构,让你能够深入理解和运用这些高可用手段。
## 项目背景介绍
先说下项目的背景。这是一个小程序点餐平台,用户在小程序上点餐并支付完成后,订单会先落到订单库,然后进一步推送到门店的收银系统;收银系统接单后,推送给后厨系统进行生产;同时返回小程序取餐码,用户可以凭取餐码去门店取餐或收取外卖。
这个项目服务于一家大型的餐饮公司公司在全国有大量的门店他们准备搞一个长期的大型线上促销活动促销的力度很大用户可以在小程序上先领取优惠券然后凭券再支付1元就可以购买价值数十元的套餐。
结合以往的经验,以及这次的促销力度,我们预计在高峰时,前端小程序请求将会达到**每秒10万QPS**,并且预计**首日的订单数量会超过500万**。在这种高并发的情况下,我们为了保证用户的体验,**系统整体的可用性要达到99.99%**。
你可以先了解一下这个点餐平台的具体架构:
![](https://static001.geekbang.org/resource/image/f0/de/f0767d06f06e5166687d54b2c4b7a4de.jpg)
这里呢,我具体说下系统主要的调用过程,以便于你更好地理解它:
1. 小程序前端通过Nginx网关访问小程序服务端
2. 小程序服务端会调用一系列的基础服务完成相应的请求处理包括门店服务、会员服务、商品服务、订单服务、支付服务等每个服务都有自己独立的数据库和Redis缓存
3. 订单服务接收到新订单后先在本地数据库落地订单然后通过MQ同步订单给OMS履单中心
4. 门店的收银系统通过HTTP远程访问云端的OMS履单中心拉取新订单并返回取餐码给OMSOMS再调用小程序订单服务同步取餐码
5. 小程序前端刷新页面,访问服务端获得取餐码,然后用户可以根据取餐码到门店取餐或等待外卖。
## 高可用系统改造措施
我在前面也介绍了,这次活动的促销力度很大,高峰期流量将达到平时的数十倍,这就要求系统能够在高并发的场景下,保证高可用性。
所以基于访问量、日订单量和可用性的指标我们对原有系统进行了一系列改造最终顺利地实现了首日500万订单以及在大促期间系统4个9的可用性目标。这个500万的订单量也创造了中国单商户线上交易的历史记录。
在下面的系统架构图中我标出了具体的改造点主要有10处接下来我就给你分别具体介绍一下你可以通过这些具体的改造措施来真正理解高可用系统的设计手段。
![](https://static001.geekbang.org/resource/image/21/4c/218719777ff902f9b26cb88a6187ba4c.jpg)
### 前端接入改造
这里的前端有两个C端的小程序和B端的门店收银系统。前端部分主要是对三个点进行改造包括小程序端的CDN优化、Nginx负载均衡以及收银端的通信线路备份。
* **小程序端的CDN优化**
用户点餐前需要先浏览商品和菜单这个用户请求的频率很高数据流量大会对服务端造成很大的压力。所以针对这一点我们通过CDN供应商在全国各地构建了多个CDN中心储存静态的商品数据特别是图片这样小程序前端可以就近访问CDN流量无需通过小程序服务端缓解了服务端的压力。
* **Nginx负载均衡**
这个小程序点餐平台之前是直接利用云服务商提供的LB它只有简单的负载均衡能力。为了能应对这次的高并发流量现在我们独立搭建了数十台的Nginx集群集群除了负载均衡还提供限流支持如果QPS总数超过了10万前端的访问请求将会被丢弃掉。
另外Nginx在这里还有一个好处就是可以实时提供每个接口的访问频率和网络带宽占用情况能够起到很好的接入层监控功能。
> **补充说明:**一台Nginx一般可以支持数万的并发本来这里无需这么多台Nginx这是因为云服务商对单个LB的接入有网络带宽的限制所以我们要通过提升Nginx的数量来保证接入有足够的带宽。
* **收银端的通信线路备份**
门店的收银系统会通过前置代理服务器来访问云端的OMS系统这个代理服务器部署在商户自己的IDC机房原来只通过电信线路和云端机房打通。在这次改造中我们**增加了移动线路**,这样当电信主线路出问题时,系统就可以快速地切换到移动线路。
### 应用和服务的水平扩展
首先针对小程序服务端的部署我们把实例数从十几台提升到了100台水平扩展它的处理能力。在上面的架构图中你可以看到小程序服务端依赖了7个基础服务每个基础服务也做了相应的水平扩展由于应用和基础服务都是无状态的因此我们很容易扩充。
这里的基础服务是Java开发的原来是用虚拟机方式部署的现在我们把基础服务全部迁移到了**容器环境**,这样在提升资源利用率的同时,也更好地支持了基础服务的弹性扩容。
### 订单水平分库
在大促情况下,下单高峰期,订单主库的**写访问**频率很高一个订单会对应6~7次的写操作包括了创建新订单和订单状态变更订单的**读操作**,我们之前通过一主多从部署和读写分离,已经得到了支持。
但负责写入的主库只有一个实例,所以这次我们通过**订单的水平分库**扩充了订单主库的实例数改造后我们有4个主库来负责订单数据写入。数据库的配置也从原来的8核16G提升到了16核32G这样我们通过硬件的垂直扩展进一步提升了数据库的处理能力。
这里的订单水平分库在实现上比较简单,我们是**通过订单ID取模进行分库基于进程内的Sharding-JDBC技术实现了数据库的自动路由**。后面的课程中,我会专门介绍电商平台的订单水平分库,它会更加复杂,到时你可以做个比较,如果有需要的话,也可以在实际项目参考落地。
### 异步化处理
你可以看到在前台订单中心和后台OMS之间我们需要同步订单数据所以这两者是紧密耦合的。不过这里我们通过**消息系统**对它们进行了解耦。 一方面前台下单要求比较快后台OMS的订单处理能力比较弱OMS库没有进行水平分库通过消息的异步化处理我们实现了对订单流量的削峰另一方面如果OMS有问题以异步的方式进行数据同步也不会影响前台用户下单。
还有在小程序服务端,在用户支付完成或者后台生成取餐码后,我们会以**微信消息**的方式通知用户,这个在代码中,也是通过异步方式实现的,如果微信消息发送不成功,用户还是可以在小程序上看到相关信息,不影响用户取餐。
### 主动通知,避免轮询
在原来的架构中前台小程序是通过轮询服务端的方式来获取取餐码同样商户的收银系统也是通过轮询OMS系统拉取新订单这样的收银系统有上万个每隔10s就会拉取一次。这种盲目轮询的方式不但效率低而且会对服务端造成很大的压力。
经过改造后,我们落地了**消息推送中心**收银系统通过Socket方式和推送中心保持长连接。当OMS系统接收到前台的新订单后会发送消息到消息推送中心然后收银系统就可以实时地获取新订单的消息再访问OMS系统拉取新订单。为了避免因消息推送中心出问题比如消息中心挂掉了导致收银系统拿不到新订单收银系统还保持对OMS系统的轮询但频率降低到了1分钟一次。
同理小程序前端会通过Web Socket方式和消息推送中心保持长连接。当OMS系统在接收到收银系统的取餐码后会发送消息到消息推送中心。这样小程序前端可以及时地获取取餐码信息。
### 缓存的使用
我们知道,缓存是提升性能十分有效的工具。这里的改造,就有两个地方使用了缓存。
* 当收银系统向OMS拉取新订单时OMS不是到数据库里查询新订单而是把新订单先保存在Redis队列里OMS通过直接查询Redis把新订单列表返回给收银系统。
* 在商品服务中菜单和商品数据也是放在了Redis中每天凌晨我们通过定时任务模仿前端小程序遍历访问每个商品数据实现对缓存的预刷新进一步保证缓存数据的一致性也避免了缓存数据的同时失效导致缓存雪崩。
### 一体化监控
在前面各个节点可用性优化的基础上我们也在系统的监控方面做了很多强化。除了常规的Zabbix做系统监控、CAT做应用监控、拉订单曲线做业务监控以外我们还对系统实现了一体化的监控。
在这里所有的节点都在一个页面里显示包括Web应用、Redis、MQ和数据库页面也会体现节点之间的上下游关系。**我们通过采集节点的状态数据,实时监测每个节点的健康程度,并且用红黄绿三种颜色,表示每个节点的健康状况。**这样,我们就可以非常直观地识别出,当前的哪些节点有问题。
监控的效果如下图所示,在下一讲中,我就会为你具体地介绍这个监控系统。
![](https://static001.geekbang.org/resource/image/ba/b0/ba4c6d94de2121ceef0e7cb579c2d8b0.jpg)
在实践中,这套监控系统也确实发挥了巨大的作用。很多时候,在系统问题还没有变得严重之前,我们就能够识别出来,并能进行主动干预。
比如说小程序服务端的部分节点有时候会假死这在Zabbix监控里往往看不出来但在我们的监控页面中这些节点就会飘红我们就可以通过重启节点来快速恢复。还有好几次系统有大面积的节点出问题了我们通过节点的上下游关系很容易地定位出了真正出现问题的地方避免所有人一窝蜂地扑上去排查问题。
除了这里我介绍的优化措施以外,我们也为系统可能出问题的地方做了各种预案。比如说,我们保留了部分虚拟机上部署的基础服务实例,这样如果容器出现了问题,基础服务可以快速切回到虚拟机上的实例。
## 系统改造小结
到这里为止,系统主要的优化措施就介绍完了,**但我们是如何知道要配置多少个节点,有没有达到预定的效果呢?**
对于这个问题,我们的做法是,**按照10万QPS和99.99%的可用指标要求,通过大量的压测来确定的。**
* 首先,我们对每个节点进行接口压测,做各种性能优化,确定好需要的机器数量;
* 然后我们利用JMeter模拟小程序前端发起混合场景的调用以此检验系统的抗压能力以及在压力下系统的可用性是否达到了预定的要求
* 最后我们在生产环境中根据压测环境按照服务器1:1的数量进行部署保证性能不打折最终这个小程序下单平台总的机器规模也达到了数百台的量级。
这里,我想结合着上一讲和你介绍的架构原则,来让你更深刻地理解这次系统可用性的改造过程。
从正面保障的角度来看,我们首先在各个环节都**避免了单点**,包括远程通信线路,这样能保证任意一个节点出了问题,都有其他实例可以顶上去;其次,我们通过节点的**垂直扩展和水平扩展**,大幅度提升了系统的处理能力,包括应用、服务和数据库的扩展;我们也有效地利用了**Redis缓存**,对高频的订单和菜单数据的读取进行了优化。
在**柔性处理**方面我们通过异步处理来优化系统的性能和避免大流量的直接冲击包括使用消息系统解耦前台下单系统和后台OMS系统以及通过及时的消息推送避免前端盲目轮询服务端。
同时,我们在**系统接入层**通过Nginx进行限流为系统的可用性进行兜底这样在流量超过预估时能够有效地避免后端系统被冲垮。
最后,我们通过**强有力的监控手段**,可以实时全面地了解系统运行状况,随时为异常情况做好准备。
## 总结
今天我与你分享了一个实际的O2O点餐平台在面对高并发流量时我们是如何对系统进行升级改造保证系统的高可用的。相信你在上一讲理论的基础上通过进一步结合实际的场景能够深入地理解如何运用各种高可用的手段了。
高可用的处理方式有很多,我这里给你介绍的也只是一部分,希望你能够在实践中,结合具体的业务场景,灵活地落地高可用的设计。
不过,无论我们采取多么周密的措施,总会有些地方我们没有考虑到,系统可能会出现各种各样的问题,这个时候对系统进行全面的监控就非常重要了。下一讲我会就如何做好系统的监控,和你做详细的介绍。
**最后,给你留一道思考题:**你当前的系统有单点吗?这个单点有没有出过问题呢?
欢迎你在留言区与大家分享你的问题和思考,我们一起讨论。如果这节课对你有帮助,也欢迎你把它分享给你的朋友。感谢阅读,我们下期再见。