阅读 12275

大麦网技术二三事

杭州周杰伦2017年项目,大麦网抢票系统抢瘫了,据传阿里内部炸了锅,大麦在阿里序列里直接进入了被鄙视链的第一名,江湖上也是声名狼藉。作为大麦故人(大麦网前技术总监及初创团队核心),我已经是不止一次的在各种场合听到诸如“大麦网技术水平太烂了”、“大麦基本没什么技术含量”、“大麦的技术还处于旧石器时代”等评论。对于差评,系统宕机摆在眼前,也确实是无需辩驳;但作为大麦网初创团队成员与最有资格谈论大麦技术的成员之一,犹豫再三,还是决定聊一聊大麦网的一些技术往事,只为缅怀。

我是2008年4月份加入的大麦,这是我第一份工作(如果不算大学时兼职的话,初中开玩Q-BASIC,上四年大学干了两年兼职,写了三四十万行代码,挑战杯大赛获奖者)。

那时候还没有“大麦网”,只有“中国票务在线”。在某个阳光明媚的周末上午,我走进了东中街32号楼旁边的元嘉国际公寓821面试,这是一套loft的公寓。进门前我曾一度怀疑自己是不是进了传销窝点,怎么看也不像一家正规公司。我和我的面试官、日后的好哥们-王威聊得很投机,我们都喜欢技术,属于把技术当生活的那类人,并不觉得这是一份工作,而是既让自己玩、竟然还给钱的美事(玩中文分词、搜索引擎、元搜索…),出门时我已经决定要来这家公司了。

2009年初我们在快速做死了旅游、机票、SNS、酒店、客栈、电影……等一系列产品后,最终决定把演出购票做深,打造一个电商模式的购票网站。对于决定去做这件事情也有点逗,我、王威做死了上述一系列产品之后,心情很沮丧,满满的挫败感,我们一起在东方银座停车场的花坛边上坐了一晚上,不说话、抽着烟、看着来来往往的人,抠着脚(不是我)。记不清后来怎么起的话题,总之最后把烟一丢,决定做个演出购票电商网站,然后第二天上班就开始开发,接下来就是满满的五年加班岁月,平均每天工作超过12小时,几乎没有周末和节假日,天天见识凌晨四点钟的北京街头。神马睡袋帐篷的Low爆了,我们是椅子上、会议桌上、台球桌下、办公司的角落地上……都睡遍了。

大麦网站部分的整个技术体系我一手搭建(截止至2015年大麦重构前),同时我也是大麦用户库里的第2号用户(哈哈哈),2010年定岗技术总监,至2013年12月我离开前,大麦网历年历届的抢票准备及协同工作都是由我主持。2013之所以离开,是因为大数据刚刚兴起,我希望能够在大麦内启动大数据相关的事情,但是那会大家都看不懂,所以一直没得到支持,于是便放弃了大麦的期权离开,成为一家公司的技术合伙人,做了一些数据相关的业务(所以也沦为了大麦初创团队里唯一一个没有期权收益的成员)。

元嘉国际公寓821,大麦网的起点
元嘉国际公寓821,大麦网的起点

谈起大麦,大麦是一家闷头做事、行事低调的公司,大麦的技术团队也非常低调。我在大麦的6年时光里,我们从来没有在任何公众场合发过言说过事。实际上,在大麦网前期,由于硬件的投入很少,所以对技术的要求到了近乎苛刻的地步。2013年我离开前,对团队硬性要求是页面服务端执行耗时100毫秒算及格线(含选座、下单),每个页面上线前先压力测试看达标不达标,不达标全部打回继续优化。我们对代码的优化是先把代码注释成空页面,然后一行一行加代码,看执行耗时增加了多少去优化的。我曾逼着某同学连续加班了三个通宵,把业务页面从1秒多,优化到了300毫秒、继续优化到了100毫秒、60毫秒……(幸好不恨我~)。我们用4台服务器扛过几万人的疯狂抢票没有挂(2009李宇春演唱会),虽然系统反应慢了点,说起来都是泪……慢的原因之一是因为买的水晶头质量太差,其中一台服务器内网线的水晶头松了,流量全部走了外网……(虽然我们穷~但那会我们人穷志不短,妄图拿三五台服务器开始做一个改变几千万人购票体验的事情)

大麦技术团队前期奉行的是工匠精神,有许多令我们骄傲的东西。或许是后续的传承出现了偏差。言归正传正传聊技术,本文不聊负载均衡、分表分库、SOA、缓存、CDN、云……等一系列满街跑的程序员不管实操没实操过,一张口都能侃几句的“大并发高负载解决方案”。时间、篇幅、精力都有限,也无法长篇大论,因此我会从几处细节切面入手,简单聊一聊。

01 在线选座核心技术

在线选座,现在已经应用得非常普遍了,从演出到电影、到机票,被应用得炉火纯青。如果我没有记错的话,大麦应该是国内最早在线上对C用户提供在线选座的公司,第一个版本我们参考了TicketMaster ,09年李宇春第一次正式使用 。 当时是遥遥领先于国内任何一家公司的同类产品,后续竞友们也出了一系列同类产品,且投入的服务设备甚至比大麦还多 ,却无一例外没扛过抢票高并发。原因在于大麦的在线选座在一些技术细节上处理得非常好,甚至现有的大麦技术团队都并不清楚自己的系统是怎么做的,实际上大麦的技术方案还没有实施到极致,还有提升的空间。离开后我进一步思考了下可以采用以下方案:

1、基于bit设计的座位数据传输协议

注意,是Bit,不是Byte、不是JSON、不是XML。截至今日,我看了许多在线选座的产品,包括行业内所谓神级公司的,无一例外都在用JSON,最多做了个GZIP压缩。当然了,财大气粗可以拿服务器和网络带宽去扛,也是无可厚非的。压缩算法是通用算法,耗费服务器计算资源,在数据协议本身没有优化到极致情况下我是禁止使用的。大麦在线选座的第一个版本用的是JSON数据做座位图数据传输,一个场次座位数据量将近1MB,在打开选座的时候能看到进度条加载的明显痕迹,而后来新数据协议实施完成后根本看不到进度条加载,因为同等信息数据传输量已缩小到了1KB左右。不仅仅是数据量缩小了1000倍的事情,基础好的同学应该能看明白这意味着什么,1KB的报文比1MB的报文在IP数据分片传输上的性能和可靠性要高出多少。

这里会牵涉到大量的位运算和数据类型基础知识,所以搞不懂long型数据64bit相比int型数据32bit意味着什么的小伙伴请绕行。

1)文本协议换成非文本协议

JSON等文本协议的优点在于简单直观,肉眼可见,好开发、好维护。但是有些关键场合追求极致还是非常有必要的。以一个数字“1234567890”为例,在JSON协议中它需要占用的字节数是:10byte=80bit,而用int型只有4byte=32bit,对于一个座位ID动不动10位数字以上的系统,光传输1000个座位ID有效数据量差别就是:(80-32)*1000=48000bit=6000byte 约等于6KB,再加上JSON格式里的“{”、“}”、“=”、"""、动辄五六个英文字符的“seatid”属性名……可想而知多了多少数据量。

2)绝对值用相对值代替

讲这条之前,我们先看则科幻小故事,我便是受了这个故事的启发:

“一个外星人偶然来到了地球,觉得地球很有意思,想带资料回去。但是因为是偶然来的,自己的飞船不够大,不可能放下很多样本。于是外星人找到了一套大英百科全书,觉得这个很好,准备带回去。但是发现那还不行,因为那一套太多了,还是太重了。外星人就把字母全部用数字代替,于是外星人得到了一串长长的数字,通过飞船的计算机全部按照百科全书顺序排列好后准备带走,但是外星人又发现飞船上的计算机还要存储很多画面和视频,那串大英百科全书数字太长了,占了很多硬盘空间——我们假设外星技术也需要硬盘。那怎么办呢?外星人就测量了自己飞船精确的长度后,把飞船假设为1。又把那串长长的‘大英百科数字’按照小数点后的模式,参照飞船长度,在飞船外壳上某处刻了很小的一个点。于是外星人回去了,他只刻了一个点,却带走了大英百科全书。回去只要测量出飞船的长度,再找到那个点在飞船上的位置……”

同一场演出的座位ID,一般是同一个数量级,比如第一个座位ID是“2010092012”,最后一个座位ID很可能是“2010093012”,在数据流里一大串“2010092012、2010092013、2010092014…2010093012”,我都感觉自己傻。那么,为何不记录下起始座位ID,后续所有座位ID都只记录与它的偏差值呢?于是就变成了“2010092012、1、2、3…1000”的形式,是不是连文本形式都看起来干净利落的减少了许多?不止于此,继续阅读下一条。

3)无视既定的数值类型,按需配位

用到位运算的时候到了。上面聊到long型和int型,long型数值64bit支持从-9223372036854775808~9223372036854775807范围的数字,int型数值32bit支持-2147483648~2147483647范围的数值,都别说上面第2)条提到的相对值数字了,就是用绝对值数字你家卖票卖到下辈子座位序号也超不出这个范围啊,更何况还用不着负数 T_T。一个区域1000个座位加载下来,偏差值最大超不过1024,只需要10bit的空间就足以存储单个座位ID了,既有数据类型ubyte占8bit最大值255不够用,ushort占16bit最大值65535太浪费,我们需要一个只占10bit的数值……OK,把int、long、byte、字节统统从脑子里抹掉吧,眼前是一串“0101010101……”到无限长的数据流,老老实实用">>"、"<<"左右移动着玩吧。

4)座位状态2bit

座位有多种状态,比如“可售”、“已售”、“锁定”等,直接跟在座位ID后面拿2bit搞定吧。。。00、01、10、11,还能再支持一个状态~

5)一个座位4个坐标值减少到1个

这个估计是最傻逼的设计了,因为选座的每个座位是需要在场馆背景里画出来的,因此需要有每个座位的坐标。问题是选择了四个点来确定一个座位……T_T,尼玛的座位都一样长宽,记录最左上角的一个坐标不就完事了么……

2、说能卖的座位不一定能卖,说不能卖的座位一定不能卖

热门演出抢票往往抢得血流成河,经常小一万张票放出去30秒就抢没了,没抢到的粉丝们网上骂声一片。不过话说系统都是我写的我这么多年竟然没能成功抢到一次票[痛哭]。流量高到平时120倍,先别提分表分库拿数据库集群顶的方案的,大麦那会还没有阿里爸爸,没有动不动拨几个亿先花着的待遇。有次我协调7台服务器协调得鼻青脸肿,有一台还是调拨了部署着邮件服务端的破机器。怎么办啊,总不能两手一摊说搞不定了吧。。。穷家穷当,继续想辙啊。

座位数据在抢票那会是高实性数据,别指望在缓存里完成所有工作,出张重票你就哭去吧。琢磨来琢磨去想到一招可以解决:把所有座位状态预先同步到redis里(记住,一个座位一个坑),接下来对过来锁座的请求先访问缓存,缓存说能卖,不好意思不能相信你,穿透到后端数据库询问状态加锁座;缓存说不能卖,那铁定不能卖,不好意思您再抢其它座位去吧。。。

此举可以让数据库压力瞬间下降好几个数量级。

选座技术先聊到此,其它的部分都没什么难点,自行脑补。
本文第四部分附有我做的并发数据分析,感兴趣的同学可以算算数,看看是多少带宽、多少服务器、支撑了多少并发。

02 热门项目抢票的技术准备工作

当初我们在每次大项目前都会做非常精细的数据分析,对购票过程中系统的流程进行详细切分,评估业务过程中每个环节的并发压力 ,进行系统调优。以数据来评估和驱动系统准备工作,绝对不是粗放式地抱着上百台的服务器,算算服务器数差不多就洗洗睡去了。

1、抢票活动CheckList

从2009年起经历了很多大型项目的抢票,什么样的情况都经历过(譬如服务器松了个水晶头、机房出口带宽被流量怼死了服务器集体全部失联、正抢着票呢办公室突然断网等等许多人这辈子都碰不上的情况),总结了很多的经验,形成CheckList。每当有新热门项目时就拿出CheckList逐项检查看各项工作是否做到位。每场抢票活动结束后仔细总结,再往CheckList上补上几条。

CheckList里的每一条背后,都有一场血的教训。举几个栗子来说吧:

1)主要系统负责人配备3G上网卡:某次抢票活动进行中,办公室宽带挂了,大麦网直接失控裸奔……从此以后,抢票开始前都给主要的系统负责人配3G上网卡;
2)CRM客服系统大查询操作进行限制:某次抢票活动进行中,某分公司一客服MM手一抖,点击了一下订单汇总,数据库直接宕了……从此以后,开抢前先把客服系统里牵涉到大查询的操作全关闭了;
3)短信通道余额确认:某次抢票活动进行中,用户都没收到短信,因为短信通道钱用完了……
4)大麦网上第三方图标及JS移除:某次抢票,一开票网页加载不完,一查页尾上挂着的某权威机构的JS直接被怼死了,资源等待中直至超时,导致大麦的页面加载不完(赤裸裸的躺枪啊尼玛痛哭流涕了)
5)抢票前机房设备巡检:某次抢票,某台服务器的水晶头松了,数据流量全部走了外网系统超级慢……
6)抢票时机房派人值守:某次抢票,机房突然失联,从哪都连接不进服务器,大麦网直接失控裸奔……

满满的挂满了泪……

2、系统流程及负载评估表

我们会对系统流程详细分解后,预测各系统切面需要达到的负载量,反复优化与压力测试:

3、应对项目时职责明确、分工清晰

准备每一场项目,都如行军打仗。战场上最怕的是乱,在出现突发事宜时,调度有序、各司其职非常重要,是快速响应和故障处理的基础。我们会明确好各部分的分工与责任人。

4、项目后总结分析

没有总结就没有收获,失败不可怕,怕的是盲目的失败。因此每次项目结束后,需要对系统负载参数进行仔细总结与分析。举个分析的栗子(水平好的同学可以算算数):

03 有的放矢,摸清硬伤

谈系统优化,不能泛泛而谈,一张嘴不是一系列高大上的方案、就是现有的不堪到需要全部推翻重新全来一个理想中的完美架构。通常只有演化成功的系统,没有一开始就设计成功做得完美的系统。

鉴于篇幅有限,暂且就先分享到这里。

关注下面的标签,发现更多相似文章
评论