转载

猴年说说耍猴的12306

12306已经成为每年春节绕不开的热点。在猴年的春运之际,InfoQ再次重拾这个话题,与各位一起探索这个影响亿万人的公共服务。正如小道君 所说的 那样,今年朋友圈里、微博上抱怨12306的少了。不得不说,这是一个很大的进步,唯有进步值得颂扬。希望明年我们不必再跟踪这个热点。

吐槽奇葩验证码

相信无数人已经见识过今年 12306 各种神奇的验证码了,吐槽归吐槽,我们来看看验证码到底是怎么回事?验证码的学名叫做CAPTCHA,即“图灵测试”(看过电影《 机械姬 》的同学因该对此并不陌生)。验证码通常是由计算机生成一个对人类而言很容易而对电脑而言非常困难的问题,能回答者被判定为人,但验证码测试其实不是标准的图灵测试。验证码的目的是阻止人类恶意利用计算机做受限的事情。早期的验证码大多用扭曲的文字来实现,用以避开OCR的机器自动识别。后来,验证码的复杂有时让真正的人类也难以识别。毫无疑问,12306对验证码的使用已发挥到了极致。

图像识别方法可以分为两大类,模型的方法和搜索的方法。模型的方法是在业界研究和使用最多的方法,基于搜索的方法直到大数据时代才出现。2012年前后,深度卷积神经网络在图像识别领域开始应用,此后,深度学习的方法击败了所有传统的方法,使得图像识别的准确率向前迈了很大一步。最新的 研究结果 使得识别率达到了96.3%。阿里巴巴研究员/资深总监华先胜先生表示,只要验证码系统不断快速地增加和迭代系统的数据,验证码的破解基本上是不可能的任务。

折翼的前端

对于广大购票者来说,刷票软件并不陌生。浏览器厂商、黄牛党的刷票技能与12306道高一尺魔高一丈的对决,一度成为媒体争相报道的对象。我联系了著名刷票软件开发者 木鱼 并与之谈及了相关的话题,他源于个人使用而开发的刷票插件,曾一度导致GitHub遭受了DDoS攻击。

  • 导致对GitHub那次DDoS攻击的主要原因如下:
    • Chrome在某次更新后增加了同源限制策略(大概是在2012年),HTTPS无法加载HTTP的资源。
    • 木鱼没有HTTPS服务器,也没有相关证书。
    • 订票助手需要自动更新检查,这个是刚需。
    • 并没有找到比较简便的做法能绕过同源限制。
    • 那时候并不清楚GitHub内部的区别和限制。
    • GitHub在流量略高的时候会返回错误(403)。
    • 经过调整的检测策略并没有对重试操作增加延迟和次数限制(虽然默认间隔并不短,但始终的高频率403导致请求越攒越多)。
    • 后期改用新的检测方式不依赖于Github和HTTPS(iframe代理模式),但之前的客户端因为GitHub不返回正确信息导致无法更新,也就无法降低请求量。
    • 综上所述,在GitHub越来越限制的时候,其承受的流量是逐渐增加的。只要能正确返回数据,流量是会直线降低的。可以说情况也始料未及,对GitHub认识不足导致其躺枪,实非木鱼本人所愿。后来的解决方案分了好几个阶段。

代码托管:

随后木鱼回忆了代码托管的几个阶段。

  • SAE托管:尝试将检测更新的信息文件放在SAE上。然而SAE是个不靠谱的家伙,在2013年12306闹事之后强制直接关闭并删除了木鱼的托管(发信息说是因为公安部的文件要求)。不过这些影响并不大,因为此时SAE上的信息文件只是过渡,最新版的更新代码已经改为iframe内嵌框架代理了。所以SAE的流量在短暂的高峰后,迅速下降。
  • iframe代理模式:虽然Chrome有HTTPS同源限制策略,但并不影响内嵌的iframe来自于HTTP。所以后期(也就是临近2013年春节的时候)的更新已经全部更新为iframe模式。换句话说,就是内嵌一个隐藏的iframe,由它去加载检测更新的页面,然后通过参数传递版本信息并判断版本。这个操作很灵活,但有个限制,就是提示必须在iframe里,对外层的窗口由于跨域也无法操作,所以提示方法很有限,并且无法保证正确检测更新。
  • 扩展检测更新模式:之前的订票助手一直使用UserScript模式,可以兼容Firefox、Chrome等。但由于2013年春节12306做的手脚比较多,纯粹的UserScript能做的事情很少但限制很大,所以开始放弃UserScript模式,转而全面使用Chrome扩展模式。此时,更新代码完全可以放到扩展的后台页面中,通过前台页面触发更新。

刷票软件技术演进:

木鱼强调: 验证码识别绝对不是技术进步,而是技术的滥用 。因为购票需要秩序,你可以在守秩序的范围内为用户出谋划策,教他购票为他免去繁琐无用的环节,但你不能公然无视购票秩序越过12306设置的种种规则。验证码识别就是其中一个典型的技术滥用,可以说是对12306信息安全的一种攻击。如此下三滥,估计也只有国内的这些公司加不明真相的群众才能做得出来了。而木鱼在开发刷票软件过程中主要经历了如下几个阶段:

  • 最早的UserScript时代:其实最早这些东西是在Firefox上依托 GreaseMonkey/Scriptish 实现的,然后恰好Chrome也支持UserScript,所以可以跨浏览器使用。彼时各大浏览器厂商和IT厂商并没有意识到这个市场,甚至连黄牛软件都没有,12306除了卡慢也没有各种暗坑和意外状况,所以这是个很美好的时代。基本上只要你肯刷,刷到票都是你的,根本用不到自动识别验证码之流。所以很多早期的同学会怀念这个时代,觉得这个时代的插件很牛,看到票绝不失手, 其实不是插件牛,而是那会儿的环境太纯洁 了。
  • Chrome扩展时代:后期由于几大互联网公司的浏览器介入,市场的推波助澜,导致12306意识到问题的严重性,开始着手封堵。UserScript工作于页面级别,能做的事情非常有限,针对12306的限制(比如12306曾经依据User Agent封堵访问、判断Refer来源等)几乎无事可做,所以后来渐渐放弃了UserScript模式,转而全面使用Chrome扩展模式。在Chrome扩展的后台页中,可以实现修改请求信息、长驻留任务等,可以做的事情比较多。这个模式一直发展到2014年春节,基本上达到了登峰造极的程度,功能和实用性最为丰富。但可惜的是,2014年春节12306出来的新版,让这个工作模式开发难度陡增(因为新版的12306大量使用了闭包和第三方库封装,代码实在太臃肿了),并且12306在高峰时会因为自己的原因甚至直接卡死浏览器,导致用户体验极差(甚至会有不少人觉得这是插件的问题),所以在这之后,Chrome扩展也渐渐取消了UI界面。
  • 独立页面购票时代V1:早在2014年春节的时候,某数字就搞出了独立页面订票。在这之前,其实我也是考虑过这种模式的,但是当时在独立页面和内嵌12306两种模式之间衡量的时候,觉得内嵌12306更为稳妥,然而事实证明我的观点是错误的,因为难度极大不说,12306掉的链子会带上我一起掉链子,并且还光荣背锅。所以在2014年春节后,木鱼也走上了全面独立页面的道路。独立页面其实更容易实现,用户体验和功能都容易做得比较好。这个模式一直延续到现在。简单地说,就是 通过独立的页面来实现对接12306接口,实现订票
  • 独立页面购票时代V2:在实现独立页面购票后,由于是独立页面,所以可以做的事情是比较多的。此时我开始考虑做一些原来想做却没法做的事情,或有一些新的情况(如限售)是否可以通过技术手段降低些影响。在2014年中到2015年,我设计并实现的这些功能包括:
    • 跨站推荐:如果一趟车因为长途因素限售,自动检测并监控临近的车站,尽可能提高发现车票的概率。
    • 中转推荐:通过服务器后端算法找出可以中转的线路并提供相关信息。
    • 辅助信息提示:如当前车站以及始发站起售时间提醒、购票难度提醒等等。
    • 聊天室:刷票路上做个伴。

以上这些功能可以说订票助手都是首推并做得最好的。后来其中的一些功能也为其它的浏览器厂商所借鉴,但并不是他们的重点。很多的浏览器厂商(如数字、某狗、黄易等等)把精力放在了全自动上面(如验证码识别)。

对于国内各厂商的抢票版产品,木鱼评价如下:

  1. 开源在国内意味着可以免费用没限制想改就改、想抄就抄;
  2. 反正吹牛不上税,我说我第一就是我第一(简称不要脸);
  3. 天下大一统,你好我也好。

总的来说,和12306对接的接口没那么复杂也并不是什么机密,所以在这之后的各大独立APP、软件等,其实可以说是大一统的场景,并没有基于谁的版本。木鱼认为在他之后,其实 并没有让我感觉眼前一亮的新功能设计 ,可以说超越其设计的功能的产品(无论是独立软件还是APP还是独立购票页面)并没有出现。

前端技术的发展:

接着,我跟木鱼谈了一些前端技术的现状,木鱼感慨道:

  • 新技术新标准(如HTML5,WebGL,NodeJS乃至Reactive本地化等)推行的越来越快,受众越来越广,是很值得高兴的事情。
  • 以前觉得 搞C++的都是大牛,因为C++实在是太高深了 。现在觉得 搞前端的都是折翼的天使,因为前端水太深了
  • 除此之外,前端和移动结合也越来越紧密,比如淘宝的APP很多地方都是内嵌的HTML5页面。但木鱼本人认为这些都有点过度膨胀了,他本人是很不喜欢这些APP的,因为它们共同的特点是卡、费流量、慢,交互复杂而诡异。

抢票软件与公平:

不可避免地,我跟木鱼再次提及了抢票软件与公平的话题。木鱼的主要观点如下:

  • 抢票软件会做成什么样子,取决于作者的素质。车票作为一种稀缺资源(相对于需求来说),是不可能每个人都惠及的。因此无论你怎么做,总有人拿不到票。当一种资源无法普及给每个人并且依赖个人去自己争取的时候,那就是没有平等可言的。 我们只能去构造一个良好的秩序去保证每个人购票的机会和权利。 因此,在购票这件事情上,我们只能要求不要有明显可控却放任不管以及有意而为之导致的不公平和秩序错乱。
  • 同时,作为IT公司、软件开发者,也是具有一定的社会责任的。作为购票软件的开发者,是不是使用了特殊的、异于常规的技术手段,以一种常人所不能及的速度和功能去进行操作,决定了它是不是会导致不公平。
  • 作为市场的受体, 用户往往是盲目的 ,因为他们的目标就是买一张票,有票就行别的什么都不会讲究。在缺乏监管的情况下,不少软件作者和企业为了吸引用户获得市场,会利用用户的无感知以及无监管的特点,做着看起来为人谋福利实则破坏秩序的行为。 这些行为的典型代表就是离线抢票和验证码识别。它们利用自己所拥有的常人不具备的硬件设备资源、服务器资源以及机器手段,肆无忌惮地破坏购票秩序对12306设置的购票秩序肆意践踏,然后标榜自己是在为自己的用户抢票,然而这些手段都极大地损害了正常用户的购票过程,乃至对12306正常的经营活动造成强大的干扰。
  • 与此同时,不在市面上流通或为一小部分人使用的黄牛软件,也会无视种种秩序,竭尽所能占用12306服务资源,在此同时对其他正常用户造成极大的干扰,对秩序也会有极大的破坏。

所以说,是不是会造成人为不公平,要看软件具体怎么做。

鸟瞰混合云架构

先前有文章报道了12306采用Pivotal GemFire分布式内存计算平台进行技术改造的过程,重点阐述了12306如何解决网络阻塞和集群性能水平扩展的问题。

猴年说说耍猴的12306

笔者针对12306业务的特点,与京东云首席架构师杨海明作了一番简单的交流,主要内容如下:

核心的瓶颈:

12306购票业务与电商秒杀系统有诸多相似的地方。通常的秒杀系统包含 数据展示数据处理 两个层面的能力:数据展示层面一般是通过CDN在全国范围内部署,实现各地访问的加速,因此访问层面的压力不大,核心瓶颈实际上在后台请求响应方面。后端必须同时能够支持高并发请求,并返回用户请求结果足够快。实际瓶颈不在CPU的处理能力,而在于I/O的吞吐速度以及数据一致性方面。在通常的电商秒杀系统里为了实现尽可能快一点,后端存储使用内存级别的操作,然后在慢速磁盘及高速内存中增加闪存卡,在磁盘阵列中增加SSD。虽然进行了磁盘层面的增强,但各地秒杀访问依然会带来数据一致性的问题,为了让全国各地的用户有平等的权利秒杀商品(比如新疆的客户与北京的客户)就需要部署多数据中心,同时实现多中心交易。

架构的选择:

分布式内存计算平台(Pivotal GemFire)对于秒杀及12306之类的业务是一个正确的选择,但不是唯一的选择。在市面上还有其它各种分布式内存计算平台,在这里就不一一赘述了。但内存计算平台对于提升秒杀系统的处理能力毋庸置疑。此类业务的基本特性在于商品数目有限制,但在短时间内消耗光,查多买少;设计系统时,需要考虑读多写少的特性,尽量将压力往前端移,降低对数据层面的冲击。12306及秒杀业务的一个重要特点就是业务总量固定,比如12306对于一段时间的火车票是一个确定的数据量大小,秒杀业务在一个特定时间,商品的库存及商品的品类也是一个固定的数据量。因此,对于内存数据库层面,并不需要非常大的容量;相反,用户的请求如何合理的分散到各个负载服务器上,并汇总到这个数据后端,是秒杀平台设计优化的重点。

分库操作:

原则上,需要对12306的业务及数据格式充分了解,才能把握其瓶颈及拆分点。12306涉及的是国家的铁路命脉,可以通过铁路的形态进行拆分,比如高铁、动车、快车及普通车;也可以通过地域进行拆分,比如北京中心、上海中心、广州中心、西安中心;再或者根据以往购票查询热度进行划分。原则是将数据分到多个地方,降低中心的压力。

小型机与X86优劣:

虽然外界报道12306业务是x86取代小机,但京东云首席架构师杨海明认为小机不会完全被x86架构取代。在所有12306的报道中,所解决的是查询的问题,而不是改变数据库的问题,小机+Oracle或者小机+db2的传统结构在确保数据一致性方面依然有绝对的优势。我们只是看到12306的查询库使用了分布式内存计算平台。也就是说,假设一个x86服务器出现问题,并不会造成铁路秩序的混乱;相反,增加x86的前端,非常适应之前描述的将压力往前端迁移的原则,将用户的访问压力用更廉价的x86来扛住,而把数据一致性和可靠性的任务交给更稳定更可靠的系统平台。在x86及小机的架构选择中,京东云首席架构师杨海明认为性能的瓶颈并不是绝对的问题。小机在RISC架构上的优势,在内存访问上的优势,及软硬一体优化的优势依然明显;相反,x86的廉价、生态的丰富、越来越强的处理能力及易用性方面,也越来越吸引大量的用户。在现在对于系统的选择、成本的控制,以及出现问题的责任影响,成为决策者考虑的因素。举个例子,如果12306的后台票务核心系统在春节期间出现2小时无法购票,那造成的将不仅仅是经济损失,甚至可能引起各种不安定因素;相反,如果某电商的秒杀系统无法完成交易,那损失是可以控制的。

CDN缓存:

CDN缓存解决的是用户访问端的问题,12306及秒杀场景下这些用户访问所带来的网络带宽是因为秒杀活动新增的,超过网站平时使用的带宽,这些带宽必须和运营商临时购买。为了减轻网站服务器的压力,更好地利用CDN、反向代理等性能优化手段,需要将秒杀商品页面以静态页面的形式缓存在CDN上。秒杀开始,用户刷新页面时,用户请求不会到达数据服务器,而是通过缓存直接提供给终端用户。用户体验的好坏,就是看CDN内容更新的效果。

更多关于12306的内容

至此,我们从前到后、从用户到开发迅速地遍历了12306的全貌。我们也有理由相信,随着时代的不断发展,这一关系国计民生、牵涉亿万人的公共服务会越来越好。随着春节假期结束,2016年的征程依然开始。愿InfoQ的读者在猴年里一切顺利。

InfoQ上还有几篇关于12306的内容,感兴趣的读者可以延伸阅读如下:

  • 技术专家支招12306.cn性能优化
  • 12306的“三宗罪”及其背后的原因
  • 12306订票助手插件拖垮GitHub事件始末
  • 12306订票助手作者讲Chrome扩展开发

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ,@丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入InfoQ读者交流群 猴年说说耍猴的12306 (已满),InfoQ读者交流群(#2) 猴年说说耍猴的12306 )。

原文  http://www.infoq.com/cn/articles/hot-12306
正文到此结束
Loading...