时间倒回到10月08日,中午吃饭刷着刷着微博发现微博突然挂了。
我一开始以为是家里网不好,后来换了流量刷还是刷不出内容,并且报error,我就知道微博应该是挂了。
往朋友圈一看,原来是鹿晗和关晓彤微博互圈“宣布恋情”了。要不是以前看过《好先生》这部剧没准我还真不认识关晓彤。陆地cp前几天不是还在炒着吗?怎么这么突然?诶..贵圈贼乱啊。
这个时候不同的人就会有如下不同的反应:
老板心里想:哪些家伙在加班又得扣钱了,拿起电话赶快给CTO打了个电话;
CTO心里想:这帮家伙叫放假别上线,又乱整,CTO立即联络开发和运维老大开了个电话会议,此刻掐上了:
开发老大:你们运维假期怎么不维护好!
运维老大:你看放假前上线的一定有Bug!
CTO:鹿晗和关晓彤恋爱了,扩容服务器!!
开发老大:你看吧,说是运维的问题!
运维老大:收到,立马处理!
于是乎,开发和运维的工作微信群开始了这样的对话:
-
运维:卧槽?怎么访问流量这么高?是出啥bug了吗?卧槽!不会是又有人出轨吸毒了吧?emmmm....我的国庆假期可还没结束啊!
-
运维:兄弟们,快醒醒!快加机器啊!系统要崩了!
-
开发:别催!再催自杀!
-
leader:测试在扩容之后赶紧拉出来测测!
-
测试:人在家中躺,锅从天上来!
然整个事件当中,除了无数粉碎的少女心,最无辜躺枪还如此敬业的小哥哥,非这位微博搜索运维工程师丁振凯莫属啦,结婚当日,遇鹿晗公布恋情,不得不从酒席上离开处理微博异常后继续婚礼,心疼小哥哥三秒钟......
然而你不知道的是,这位小哥哥的岗位是
Linux云计算运维工程师
作为Linux运维云计算的从业者,有木有感觉挺自豪的!!!
相信大家也知道,Linux云计算运维工程师也是我们马哥教育《Linux云计算》课程培养的人才,汤哥想问问,小哥哥,你学习过马哥的课程吗?哈哈哈哈
言归正传!
到底服务器是怎么奔溃的,我们来看看各位程序员大大们的分析:
我觉得不像数据库挂了,微博这种级别的架构根本不是简单的分布式server+DB就能抗住的,别说鹿晗关晓彤搞个大新闻,就算平时运营的压力也扛不住。
刚才王高飞说加一千台服务器暂时顶住了,数据库是不可能临时这么弹性伸缩的,能伸缩的无非就是HTTP Server、各中间层服务、缓存或消息队列。
大概是微博自动扩容的算法没写好,或者没敢全交给算法来做。比如你发现流量升高了,自动下单加几十台服务器能接受,突然加一千台要是程序出bug的话微博得白支出多少钱啊……多半是这个量级的扩容需要运维手工来确认。
而且是在长假最后一天的中午爆发的,不是访问高峰期,服务器也准备不足。明星公布恋情这件事又没法预警,谁知道他们啥时候心血来潮忽然介绍女朋友啊……
知友热评
因为没有了卓伟打提前量,太突然
——肖小邦
根据目前已有的信息猜测是数据库被压垮了,先发猜想,稍后写个程序分析当时的点赞评论转发数据验证猜想。
微博这样的网站,如果被大流量压垮,不太可能是非必需字段没有容错。之前经历过几次热门事件,我相信在爆发热点新闻的时候,微博会暂时牺牲一点数据准确性来保证关键服务可用。也就是说,光读请求很难压垮微博。
根据事故时的微博点赞数、转发数、评论数、评论的回复数、评论的点赞数、转发的评论转发点赞数等的量,微博极可能是由于事发当时需要写入数据库的请求太多(写行为峰值可能达到了几十万甚至更高),以及大部分写都会落到同一条微博上,而且某些写操作还需要触发相应的其他写行为(回复评论需要通知评论者、点赞需要进关注者 feed 等),数据库压力过大扛不过来,最终跪了一会儿。其实如果缓存做好,这时候还是可以满足核心数据读请求的(当然微博缓存做的并不好,我微博个人页数据错误很久了反馈也没用)。如果数据库压力过大时对部分写请求异步化,或者考虑暂时抛弃部分请求换取稳定性,当然这样也各有利弊,不一定是好的。
可以抓取当时鹿晗发的微博的所有评论转发回复点赞的时间,看下故障前几秒成功的写行为究竟有多少。
不负责任的未经验证的猜测(画图水平有限,省略了部分过程,但是从上下两个过度的箭头数,大致表达了很多请求是读且未压到数据库,将就看吧[手动捂脸.jpg]):
相比于微博的此次服务器崩盘后用户的一阵抱怨,老板的奔溃,业内多数的Linux运维架构师对此次短暂的服务器拥堵都表示:牛,表现非凡!
当然你也许会问,淘宝每年在双11购物节这么多人其运行状态还挺好的。
应对淘宝双十一的高并发场景,这是可以提前做很多准备的,比如说提前购买带宽资源、增加服务器资源、进行完备的异地容灾等等,很多都是可预测的。而微博呢?热点事件随时都可能发生,所以这对于微博的运维工程师来说是很大的考验。
当然,现在的运维平台也是非常的智能了,可以对各项指标进行实时监控,一有异常,马上进行短信或者邮件报警,之后就是各个岗位的工程师人肉上场调配各类资源了。
那微博在平时为什么不增加一些服务器资源呢?
服务器资源、网络带宽资源等既重要,又昂贵。由于并不是每时每刻都必须应对高并发的场景,因此如果说在平时增加了冗余的服务器资源导致大量机器空载,也是一种很大的浪费。
我们在考虑提供可用服务的同时,也必须考虑一下成本。
下面针对高可用性架构中经常会提到的几个点来看一看这次事件的提前预防:
1、大型网站高可用架构
不管是对于小型网站还是大型网站来说,分层都是必须的:粗粒度的分层一般为应用层、业务层和数据层。横向分层之后,可能还会根据模块的不同对每一层进行纵向的分割。拿微博举例,我觉得它的评论模块和点赞模块应该是解耦的。越是复杂的系统,横向和纵向的分层分割粒度就会越细。很多时候你用起来以为它就是一个系统,其实后面可能是由几百上千个独立部署的系统对外提供服务。
2、集群
集群在大型网站架构中是一个非常非常重要的概念。由于服务器(不管是应用服务器还是数据服务器)容易发生单点问题,一旦一台服务器gg了,就必须进行失效转移。
3、应用服务器集群
一般来说,应用服务器必须是无状态的。什么叫无状态服务器呢?在介绍它之前,我们先来说一下状态服务器:状态服务器一般会保存请求相关的信息,每个请求会默认地使用以前的请求信息。这样就很容易导致会话粘滞问题:如果一台状态服务器宕机了,那么它保存的请求信息(例如session)就丢失了,可能会导致不可预知的问题。
那么相对的,无状态服务器就不保存请求信息,它处理的客户信息必须由请求自己携带,或者是从其他服务器集群获取。因此无状态服务器相对于状态服务器来说更加地健壮,就算是重启服务器甚至是服务器宕机都不会丢失状态。由此引申出来的另一个优点就是方便扩容:只要在增加的服务器上部署相同的应用并做好反向代理就能对外提供正常的服务。
4、session管理
既然应用服务器是无状态的,那么用户的登录信息(session)如何管理呢?比较常见的有下面四种方式。但是由于前三种都有很大的局限性,这里只聊聊基于集群的session服务器管理方式。
-
session复制
-
源地址hash(session绑定)
-
用cookie记录session
-
session服务器
我们在这里是将服务器的状态进行分离:分为无状态的应用服务器和有状态的session服务器。当然,这里说的session服务器肯定说的是session服务器集群。我们可以借助分布式缓存或者是关系型数据库来存储session。对于微博来说,这里肯定得用分布式缓存了:因为用关系型数据库的话数据库连接资源容易成为瓶颈,并且I/O操作也很耗时间。比较常见的K-V内存数据库有Redis。我觉得微博内容中的赞数、用户的关注数和粉丝数用Redis来存应该算是比较合适的。
5、负载均衡
既然提到了集群,肯定得说说负载均衡。但是感觉负载均衡应该可以归类到可伸缩性里面去,所以这里就不详细讲啦,就简单说说有哪些常见的负载均衡的方式以及负载均衡算法。
负载均衡方式
-
HTTP重定向负载均衡
-
DNS域名解析负载均衡
-
反向代理负载均衡
-
IP负载均衡
-
数据链路层负载均衡
负载均衡算法
-
轮询法
-
随机法
-
源地址哈希
-
加权轮询
-
加权随机
-
最小连接数
6、插播点别的
在微博的架构中,应该采用的是异步拉模型而不是同步推模型。什么意思呢?我们举个例子:鹿晗的粉丝有3000多万,关晓彤的粉丝有1000多万。
假如他俩发了条微博的同时需要往这4000万粉丝的内容列表中(假设这里用的是关系型数据库)推送过去,这就是简化的同步推模型。
那这样有什么缺点呢?首先,这样会消耗大量的数据库连接资源,更重要的是这样不太符合软件设计规范:因为对于两人的粉丝来说,分别由有3000多万和1000多万的数据是冗余的。假如说陈赫、邓超在第一时间对他俩的微博进行了点赞,此时瓶颈就来了:刚才往数据库里插入4000多万感觉还可以接受,但是现在四人的粉丝数加起来好几亿了,同时往数据库插这么多数据是不是感觉不太合适?
没关系,我们现在换一种内容推送方式:我们现在不用同步推了,而是用异步拉。我们每次在手机上刷微博的时候,如果想要看到更新的内容是不是都要下拉刷新获取?没错这就是异步拉。异步拉有什么好处呢?很明显的一个好处就是可以将热点数据进行集中管理,并且不用进行大量的数据插入冗余操作。另外对系统资源的消耗也较少。
那么微博内容从哪里拉呢?主流的解决方案是把热点内容放到缓存中,每次都去查缓存,这样可以减少I/O操作并且避免发生因资源枯竭造成的超时问题。
猜测说完了,我们来看看微博资深架构师应对高并发的架构实践与回顾:
面对每次服务器危机,微博能够在短时间解决问题,这与平时微博投入大量人力物力,重点发展弹性扩容方面的努力是离不开的。
从后端来讲,新浪微博可以分为Java和LNMP两大体系,特别是在LNMP方面积累了大量经验,所以当遭遇流量峰值挑战时才能做到从容应对。
那么新浪微博是具体如何做到的呢?
从架构角度,如何做到?
新浪微博在做架构调整之前,和很多公司的处理方案都相似,采用设备冗余与服务降级两大传统手段。
设备冗余。各业务提前申请足够的设备保证冗余,正常情况下一台服务器 CPU 约在 20%-30% 左右,当峰值到来会达到 60%-70%,系统就会严重警告,需要做服务器扩容。一般情况下,系统会准备一倍左右的冗余。但这就存在一个问题,流量如果翻三倍、四倍怎么办?
服务降级。当突发流量峰值,系统将对非核心业务以及周边业务进行降级,PC 主站只保留主 feed。内部降级的方式有很多,降级后用户基本感觉不到。如极端情况下,系统一定主要保留微博主站,其他功能模块全下线,进而保证服务不会挂掉。
但从公司、老板层面,肯定不愿意出现这样的情况,因为降级对广告的影响很大。但是在传统架构下,面对突然产生的流量,技术只能这样做。
这两种传统手段,面临一系列的扩容和降级难题:
1.设备申请周期长
如机房从其他业务挪用服务器,周期不会太长。但每年三节都会对常规流量进行预估,做好几倍扩容。假设机器不够,会发起采购,但周期会非常长。
2.扩缩容繁琐
如下图,当服务器到位,做扩容,又是一个繁琐流程,还需要多部门共同合作。
3.设备运营成本高
如每个业务都做一定的冗余,假设一百台服务器,要用二百台来保证正常运营,可以想象这个成本会非常高。举例,PC 和手机端,业务峰值不在一个点,峰值不在一起,利用率也有所差别,就算同一事件,每个业务的负载也会不一。
业务之间拆借,也是行不通。因为短时间内,无法应对服务器之间的环境差异、代码等差异,初始化完毕,峰值也消失了。
综上所述,扩容和峰值是面对突发流量峰值的两个解决维度,但存在的问题是扩容不能针对服务器快速扩容、降级又对服务器损害相对较大。
新浪微博决定从架构层面解决这两个问题,通过引入混合云 DCP 平台,达到下面的效果:
1.降低设备运营成本
不希望为冗余准备的大量设备,一年中的很多时间空闲。
2.实现业务弹性扩容
各个业务之间峰值不一样,可通过技术把环境抹平,实现随时化的自由调度。
新浪微博混合云DCP平台简介
DCP(Docker Container Platform)平台解决思路是从业务的弹性角度,在各业务之间,无论是 Java 还是 PHP 等,通过抹平所有的环境,快速应对峰值,同时在基础设施上支持跨云。之后,在内容服务器用完的情况下,可借用公有云这份解决方案,把流量峰值问题迁移到公有云。
业务弹性调度。如下图,是旧的应对手段和弹性扩容手段的对比。实现弹性扩容后,系统会使用容器化来抹平各业务环境,把各业务之间的冗余部分放到共享池。这样一来,哪个业务需要扩容,就可以很简单地从共有池把这部分设备扩容。
因各个业务之间的峰值和负载程度不一,把其他业务的冗余设备拆借过来,实际扩容能力相比以前的 1~2 倍,可以提升到 2-3 倍。
基础设施支持跨云。如下图,是私有云和公有云各自的优势。针对各自的特点,在部署流量时,需做一些侧重操作。如针对常规的流量,优先会部署到私有云,保障体验、性能等都是最优。这里涉及公有云安全性和私有云之间有一定差异以及在性能上会有一定下降的问题。
假设在公有云流量部署 1 小时,其中可能公有云一些服务还会依赖于私有云服务,这样就会出现跨机房调用的情况。可服务只是微慢,而不是降级或停用,和之前相比优势很大。
另外,假设把常规的负载也部署到公有云,只要把所有底层全部做多机房部署,根据不同业务做流量分配即可。
DCP 平台架构。如下图,是 DCP 平台抽象版架构,主要分为主机、环境、服务和业务四层:
DCP平台抽象版架构
1.主机层
这层的作用是假设需要扩容 50 台服务器,这些服务器可能来自内网、也可能来自公有云。上层在使用过程中,需要通过 Adaoter 来提供,优先内网,之后去公有云创建。
2.环境层
这层主要是编排的过程,封装众多的原则性行为的 API。
3.服务层
负责根据众多 API,组合搭建一个服务。
4.业务层
业务层通过负载均衡把流量引入服务器。
私有云“化零为整”。如下图,是一个基于 DCP 的模型,左侧是传统架构,假设长方形是每个业务需要的机器,如 A 业务要扩充两台,就承载不了,就需降级。右侧接入到混合云后,把所有业务的环境抹平,所有设备放到共享池,假设 C 业务需要三台设备,在新的架构下,可轻松从共有池选取。
基于混合云 DCP 平台的 PHP 服务 Docker 化
服务 Docker 化。Docker 是从逻辑层面虚拟化,把环境做镜像差异化从而进行快速部署。Docker 服务启动快,镜像一次制作,多次快速部署,尤其适合动态扩容部署。
PHP 服务 Docker 化的部署方案设计。如下图,粉色部分是 PHP 服务相关组件nginx、php-fpm、memcache、scribe 等,这些组件容器单独部署。像代码、配置、日志等经常变更,黄色部分通过挂载的方式和 Docker 容器互动。
镜像制作。镜像制作的步骤如下:
-
从镜像仓库中拉出 CentOS 作为基础镜像
-
运行镜像
-
在运行容器中安装 PHP 环境相关软件包
-
提交修改并推送至仓库
-
PHP 服务镜像制作完毕
镜像方案。基于 CentOS 6.7 来制作镜像,将 PHP 服务组件拆成独立镜像。这里涉及到的问题是“镜像占用空间相对较大,每个都超过 1G 大小。同时拉取镜像耗时太久,占用宽带较高。针对这个问题的解决办法:
将 PHP 相关的组件制作成一个镜像,服务通过容器命令来启动。
在部署过程中,主要解决三大核心问题:首次部署、代码上线与配置变更。
首次部署,如下图是首次部署的流程,由 DCP 平台发起扩容,把扩容的数据和业务相关的文件配置好,全部发到前端机。每一个前端机基于配置文件,进行拉取代码、启动容器和服务查询等操作。
代码上线。如下图,通过镜像完成上线,代码镜像使用 busybox 为基础,大小仅1M。
创建代码镜像。如下图,创建代码分为 Dockfile,Build,下载代码镜像、启动容器、拷贝代码三步骤。
配置文件更新。如下图,把配置文件制作成 Docker 镜像,每台机器拉取镜像,替换配置文件,自定义脚本执行 reload。
这里值得提醒的一些细节有:
-
Docker 化后,宿主机运行在 Centos7.0
-
内核升级到 3.10
-
容器中的启动命令需要前台启动
-
经常变更的部分放在镜像外,通过 volume 挂接容器
-
网络模式:选用 host 网络模式
-
容器的 reload 或优雅重启采用 dockerexec xx reload 方式
基于混合云DCP平台的PHP弹性扩容
说到弹性扩容之前,先说三个概念:服务、服务池、集群。
如下图,服务可以理解为业务,像新浪微博的红包飞、问答等。服务池,就是业务会部署到哪个机房。集群就是来自内网或公有云上的闲置不属于任何业务的机器
扩容流程。如下图,整个流程分为资源申请、环境初始化和服务上线三部分。管理员向混合云平台发起请求,之后完成设备、服务初始化的过程、调度中心对服务进行部署,包括代码的拉取。初始化完成后,通过服务上线,把4/7层流量切入,部署整个监控中心。
扩容模板。一个新业务需要支持弹性扩容,都需要在 DCP 平台配置,与之相匹配的是配置系统,如下图。镜像地址 image.1.6.1、所有服务器组件的参数、挂载的容器、代码的挂载位置、配置容器参数等都需要提前配置好。
流量切换。这里和阿里云合作非常多,把公有云已经初始化好的前端机 IP 加载到负载均衡中,自动重启、部署,无须人为操作。
弹性容量的考虑。Push 时,就要考虑扩容,针对某一事件在某一地区进行部署,基于以往的数据 ,评估系统预估需要扩容的服务器。还有稳定性高、低峰的服务,针对一些每天晚上 9、10 点出现流量翻倍的情况,实现自动扩,自动退。
扩容控制、效果。如下图是抗峰值,话题服务器的扩容过程以及完成后的内部效果。需要 16G 机器,50台,在扩容系统输入完成,五分钟搞定。大概15分钟左右,流量峰值被削平。
以上内容由编辑王雪燕根据侯青龙老师在 WOTA2017 “高可用架构”专场的演讲内容整理。
侯青龙
新浪微博主站研发负责人
侯青龙 ,2010 年加入新浪微博,先后参与过微博主站 V2 版至 V6 版的研发,主导过主站 V6 版、多机房部署以及淘浪合作等重大项目的架构设计工作。目前,他负责 PC 主站研发工作,致力于提升产品研发效率以及优化系统性能,善于在业务需求和产品研发之间找到最大公约数。在基于 LAMP 架构的大型系统架构设计、海量数据访问、分布式缓存设计等领域具有丰富的经验。
本文采编自:CTO训练营(微信号:gaozhao51cto)互联网架构师(微信:app-jiagou)知乎日报(微信:zhihuribao)
作者:侯青龙/互联网架构师/知乎日报
原文链接:你一定不相信,鹿晗表白后背锅的程序员是我,转载请注明来源!