淘宝开放平台技术历程

jopen 11年前

   2006年底,阿里巴巴提出了“Work at Alibaba”的战略,20来号人就被拉到湖畔花园马云的公寓里面开始一个叫阿里软件的公司创业。当时对于“Work at Alibaba”有个朦朦胧胧的感觉,就是要为中小企业提供一个工作平台,但是工作平台又需要是一个开放的平台,因为卖家的需求是长尾的,当时火热的 Salesforce给了阿里人一些启示,那就是做一个支持二次开发的工作平台,半开放式的来满足各种卖家的长尾管理需求。此时,软件市场上就开始培养起来最早的一批TP(淘宝开放合作伙伴),迄今为止很多非常成功的TP就是从那个时候开始进入淘宝卖家市场。

  但经过一年的平台建设,发现开发者非常难利用平台做二次开发,只有阿里软件公司内部团队构建了三个不同的CRM软件。这时候淘宝来了一个业界的技术牛人:王文彬(花名:菲青),这位淘宝新晋的首席架构师找到阿里软件的平台架构团队,谈到了当时业界还非常新颖的一种技术平台——开放平台,由于阿里软件已经在做类似的开放工作,希望能够以合作的方式来试水开放平台。当时双方都是一种尝试的态度,因此最后敲定,投入一个人,两周时间,看是否能够出原型,如果可以,那么就继续做,如果出不了原型,那么就此结束。两周时间,负责阿里软件的架构师放翁,参看着美国雅虎的开放模式,吭哧吭哧的就搞出了开放平台第一个雏形,没想到就这样开启了5年的开放之路。后面会根据时间轴来说一下开放平台的产品和技术的变革,每一年会发生很多事情,但是调出的一点一滴是当年最有感触的。

  2007年:萌芽

  SOA盛行的年代,内部架构服务化成为开放的第一步,内部服务不做好隔离,开放就意味着风险不可控。支付宝今天的服务框架SOFA(类ESB),淘宝的HSF(OSGI),阿里软件的 ASF(SCA)都是那个年代的产物,但服务化带来的痛却是一样的,不论是OSGI或者SCA之类的服务框架,本身服务化规约设计都类似,但难题也都摆在每个架构师和开发者面前——服务单元Bundle的粒度控制,服务之间依赖管理,性能与规范的冲突,调试与隔离的平衡。这些都使得一线开发者和平台框架实现者出现非常多的矛盾,而这个过程最后能活下来的框架,最后都是摒弃掉了很多企业级的设计思路,因为SOA架构从企业级产品演变而来,而服务化后的内部平台要面对的开放平台天生就是互联网的产物。

  2008年:雏形

  这一年到年底,平台开放淘宝服务30 个,每天调用量2000w,这一年的开放平台的开发者面向的客户主要是阿里巴巴上的中小企业和淘宝C店卖家。开放平台建设初期要解决的就是三个问题:1. 服务路由(外部可以获取内部信息);2. 服务接口标准化(统一方式的获得各种标准化信息);3. 授权(外部合法的获取内部信息)。服务路由其实就是写一个高效的HttpAgent,服务接口标准化就是对象文本化(Json,xml)。今天在各大开放平台广为使用的OAuth协议,当前处于0.6版本,没有任何实际的互联网开放平台使用,直到Google 2008年底慢慢对外推广开放的时候,OAuth被封装到Google的Open SDK中,才使得很多中小互联网公司使用这种看似极其复杂的两阶段授权交互模式。淘宝初期采用的是自有协议,因为OAuth2以前的逻辑复杂且使用不方便,直到2011年才开始支持OAuth2,同时做了部分的安全增强。授权解决了开放最大的一个问题:用户安全的对应用访问其数据受信。用户从此不用赤裸裸的将用户名/密码交给一个应用软件,应用也可以在允许的范围内(操作,数据,授权时长)充分利用用户授权来玩转创意。

  有了上面的三板斧(路由,数据规范,授权),开放平台正式开门迎客了,没有对外做任何的推广,数据就蹭蹭蹭的走到了第一个1000w日均调用,此时两个互联网的新兴技术开始在开放平台中尝试,Memcached和Hadoop。今天看来这两个技术已经被大规模使用,2008年时却是在吃螃蟹,2 台虚拟机要抗1000w的路由,势必要求对于路由和校验信息能够有足够强的缓存,Memcached无疑是最好的选择,但当时号称分布式缓存的 Memcached其实是集中式缓存的一种,真正的分布式缓存都还在纠结于一致性和效率的问题(2, 3阶段提交)。此时需要有一种方式能够保证效率(可扩展)和稳定性,于是我们封装了Memcached客户端,提升当时BIO的Java客户端的性能,同时引入了客户端负载均衡和容灾的设计,这种设计已经被应用在现在很多大型分布式系统里面。另一方面每天上千万的访问也让技术和产品对访问的行为有很强的分析需求,此时Hadoop在雅虎的充分利用引起了我们的重视(当时的雅虎技术创新一直都是业界的领头人),通过仅有的两台机器和一堆技术文档,摸索着我们搭建了公司内部的第一个Hadoop集群,而所写的Hadoop入门实践也成为当时Hadoop入门的基础文档,对于每天2000w的日志分析需求来说,Hadoop用的是游刃有余,但随着业务的不断发展,Hadoop离线分析所带来的问题也凸显出来,MR程序面对灵活多变的分析需求显得不易维护且效率低下(数据反复读取分析),于是我们也开始思考怎么来改进一下这个新玩意儿。

  2009年:产品化

  这一年到年底,平台开放淘宝服务 100多个,每天调用量4000w,这一年是开放平台的开发者面对的主要是淘宝C店卖家,卖家工具成为服务市场的主流。这一年是变化的一年,阿里软件年中的分拆使得开放平台的归属有些微妙,一种情况是留在阿里云,作为集团的基础设施,另一种情况就是跟着主要的业务需求方淘宝走,最后我们还是说服了博士,结束了阿里软件的老平台,淘宝正式开始自己的开放之路。来到了淘宝,业务开放迅猛增长,从30个API猛增到了100个API,没有对外做任何业务推广,平台调用量到了年底翻番。此时技术上的挑战又聚焦到了性能上,一次API call的业务消耗平均在30~40ms,开放平台当时的平台处理消耗平均在10ms左右。我们做了数据打点和分析,发现最大的消耗在于互联网数据接收,同时大量的图片数据上行,更是加大了平台处理时间,同时从访问日志分析中可以看到很多无效的请求也占用了非常多的处理时间,这也意味着无效请求和有效请求一样在消耗着有限的容器线程资源。于是我们开始尝试自己封装字节流解析模块,按需解析上行数据,一来提升数据分析的性能(并行业务和数据增量分析操作),二来可以用最小代价处理异常请求(当发现不满足业务规范,则立刻丢弃后续所有数据),这块实现被叫做LazyParser,主要的实现重点就是最小化数据缓存来并行业务和数据解析操作,上线后效果不错,整体处理平均处理时间从10ms降低到了4ms(包含了异常处理的优化和解析性能的提升)。

  另一方面,Hadoop的MR问题也日益突出,一大堆MR的class维护成本高,性能问题也随之出现。此时我们开始尝试抽象分析业务场景,想到的是是否能够通过配置就可以完成各种统计分析需求。要用配置替代code,其实就看是否可以穷举code所实现的各种统计需求。当回顾SQL的理念时,发现其实所有的统计在切割成为KV作为输入输出时,所涵盖的需求无非是max, min, average, sum, count, distinct(这个是2012年实现的,用了bloomfilter和AtomicLong),再复杂一些无非就是上述几个操作结果的数学表达式运算,因此KV输入和KV输出的离散统计配置需求已经抽象出来了,接着就是把统计完的一组组KV根据K来做Groupby,就生成了传统意义上的报表。(K,v1,v2…)从此以后,每天的统计需求都通过配置来改变,再也没有一大堆MR代码,同时一次数据输入就可以完成所有分析的处理,性能上得到了极大的提高。(后话,当后面有人推荐我们去看hive, pig的时候,我们发现原来路都是这么走过来的)

  虽然Hadoop每日分析抽象出模型配置解决了性能和易用性的问题,但是对于即时分析却不太适合,当时出于监控的需求,希望能够一个小时就可以对数据做一次增量的分析,用于监控服务整体的调用情况,保证对异常问题的即时排查。由于一天4000w的量还不算很大,因此当时就直接考虑采用MySQL 分库分表的方式然后定时的去做SQL的查询,结果发现效果不错。当然这个过程又产生了一个小组件,要直到4000w的日志数据写磁盘和DB双份必然会带来不少的IO消耗,同时这个系统并不是帐务系统,丢掉一点日志也没关系,因此就采取了异步批量数据外写的设计(多线程守护各自的一块Buffer页,定时外刷或者满页外刷),这样在双写的情况下,单机的Load也没有超过0.7。

   但快到年底的时候,发生了一件事情让我们头痛不已,同时也成为了开放平台的一个“隐形炸弹”。一天晚上,突然发生平台大规模拒绝服务的告警,半夜爬起来观察了一下整个集群,发现业务处理时间从平均的30~40ms,上升到了1s,仔细观察了一下,某一个业务的响应时间大幅攀升,从原来20ms的响应时间飙升到了1s以上,此时由于Http请求的同步性,导致前端服务路由网关的集群线程都释放的非常慢,阻塞处理这个业务的请求,而其他正常的业务(淘宝开放平台背后的服务是不同团队维护,处理时间从1ms到200ms都有)也无法被访问,因此才有了开始的全线告警的产生。当晚后面发现是这个业务团队的一次发布中忽略了数据库索引建立导致服务耗时增加,但这个问题开始时不时的来拜访开放平台,开放平台稳定性受制于任何一个业务方,这是不可接受的~~~对于这个问题,起先考虑集群拆分,将重要业务和不重要业务拆分,考虑到实施成本(不同服务的利用率差异很大)和业务隔离是否彻底(重点业务也会相互影响),放弃了这个想法。当时又想到了软负载切割,Haproxy和LVS,一个是七层的网络软负载切割,一个是四层的负载切割,由于涉及到业务,于是还是考虑走七层的软负载切割,尝试一台Haproxy挂7台虚拟机,然后运行期可动态调整配置在出现问题的时候可人工干预切割流量。就这样,我们有了告警以后可以手动切割的半人工方式干预措施,起码不在心惊肉跳时手足无措了。但我们依然晚上睡不踏实…(期间考虑过Web请求异步化,Servlet3的模式来规避同步Http 带来的平台阻塞,但当时唯一支持Servlet3的jetty和tomcat做压力测试,效果都很不稳定)

  2010年:平台化

  这一年到年底,平台开放淘宝服务 300多个,每天调用量8亿,这一年淘宝正式开始对外宣传开放,淘宝开放年,赢在淘宝,很多今天年收上千万的TP在这个时候成为了先锋(10年以前的可以叫做先烈),产品层面上,这一年除了卖家工具的继续发展,SNS热潮的兴起带动了淘江湖的买家应用,游戏应用的淘金者蜂蛹而入,开放的服务也继续保持 300%的增速,覆盖面从卖家类延伸到了买家类,从简单的API提供,到了淘宝网站支持深度集成应用到店铺和社区。

  8个亿的访问量下再用MySQL做流式分析已经不靠谱了,分析时间要求也从一个小时提升到了20分钟,此时经过快1年半的Hadoop使用和学习,再加上对分布式系统的了解,正式开始写第一版的流式分析系统,MR的抽象依旧保留,而底层的数据计算分析改用其他方式,这个“其他方式”和 Hadoop的差异在于:1. 分析任务数据来源于远端服务器日志(主要通过pull而非push)。2. 任务分配和调度采用被动分配(有点类似于volunteer computing的模式),mater 轻量的管理任务,slave加入即可要求执行任务,对任务执行的情况不监控,只简单通过超时来重置任务状态。3. 任务统一由Master来做最后的Reduce,Slave可以支持做Shuffle来减少数据传输量和Master的合并压力,Master负责统一输出结果到本地。总的来说就是数据来源变了,数据不通过磁盘文件来做节点计算交互(只在内存使用一次就丢掉了),简化任务调度,简化数据归并。这样第一版本的流式分析出来了,当然后面这些设计遇到的挑战让这个项目不断在演进,演进的各种优化几年后发现都在hadoop或者Hive之类的设计中有类似的做法。这个系统3台虚拟机抗住了8亿的日志即时分析,MySQL日志分析就此结束。

   这一年另一个重大改变就是更多人对开放的价值有所认同,淘宝从一个部门的开放走到了淘宝公司的开放,什么叫做部门开放?就是在10年以前大部分的API开放都是开放平台这个团队来做封装维护,30个api还可以支撑,100个api已经让一个专业的小团队应接不暇(当然不得不承认,迄今为止淘宝最有全局业务知识的还属这个团队的成员),300多个api这种势头基本上就无法由一个团队来作了,业务变更带来的接口不稳定经常被投诉,因此我们启动了服务轻量化的“长征项目”,逐渐通过工具和平台将服务接入变成自动化的方式,将原来开放一个服务需要点对点,手把手花一周时间实施完成的过程,通过自动化服务发布平台,一个人一天时间就可以发布一个服务,并且服务的文档,多语言版本SDK都自动生成。这样就具备了服务轻量化的基础,然后将各个新开放的业务采用这种模式接入,而老业务逐渐的归还给各个业务方去维护。这样一来,服务的“稳定性”(业务方面)得到了非常大的提升,用户对于服务的满意度也得到了极大的提高。

  但这担子放下了,那担子又挑上了,在上面谈到去年后台应用不稳定导致平台整体不稳定的问题在轻量化以后出现的频率和次数更多了,因为发布和维护都落到了后台部门,此时对于各个系统的把控就更弱了,这晚上睡觉不安稳,KPI中的稳定性指标基本就没法定了。唯一能够彻底解决问题的办法就是http服务异步化+事件驱动+虚拟隔离线程池。那年年中的时候对Jetty7做了一次压测,发现Continuations的效果已经可以上正式环境了,于是开始在Jetty7的基础上做Http服务异步化+事件驱动的封装,同时也实现了一个虚拟隔离线程池做配合。具体设计细节这里就不多说了,参看blog,简单描述原理就是:1. 将前端容器线程和业务处理隔离(类似NIO和BIO的设计差异)。2. 业务处理如果依赖于外部系统则采用事件驱动的方式来减少线程等待,同时提高线程占用资源的利用率(这点上来说理想和现实还是有很多细节差异的,在实现的时候必须根据依赖系统消耗时间占总时间的比例看是否需要事件驱动,事件驱动带来的切换消耗是比较大的)。3. 通过一个大的线程池虚拟设置不同业务可消耗的最大资源数,来充分的共享资源在异常情况下限制业务占用过多资源(任务处理开始排队而非无度的占用资源)。这个组件上线以后,没过几天就发生了一个典型的案例,一个业务2点开始响应时间从10ms上升到了40ms,然后继续上升到200ms,当时给这个业务模拟设置最大的线程资源数是20个,就发现那时候由于RT时间提升,线程资源释放的慢,20个慢慢的被消耗到顶了,此时这个业务的队列开始从0到100到 200到…(当然防止内存过多占用,会丢弃超过队列长度的业务处理),而其他业务还是正常的使用着资源,平台平稳,到了4点多,业务方收到告警修复以后,RT时间下降到了10ms,队列中的请求数量开始减少,最后队列清空,线程资源占用下降到正常水平。从此以后震子同学开心的和我说:开放平台稳定性的 KPI可以随便大胆的写几个9了。(技术敢做敢想,才能高枕无忧)

  2011年:市场化

  这一年到年底,平台开放淘宝服务758个,每天调用量19亿,这一年SNS热潮消退,游戏逐渐淡出,卖家市场依旧生意火爆(很多TP的年收益让人咂舌),营销工具崭露头角成为开发者新宠,淘宝客成为了开放新宠(这一年返利网和团购一样火,只是前者收钱,后者烧钱)。

  就在开放平台开发者前景一片大好的时候,出现了一个让开放转变和收缩的导火索,一家做营销工具的公司“团购宝”,每天凌晨都会通过接口同步客户设置的一些优惠商品信息到淘宝网,结果那天凌晨,微博上突然很多人说某些店都是一块钱的便宜货,要知道这种事情在微博盛行的时代,传播速度之快,影响之大,当即很多卖家商品都被1块钱拍下。最后发现是线下的商品价格不知道怎么全被修改成1块钱,然后凌晨一同步,就导致出现了上面的一幕。从那时候开始,开放平台的KPI中增加了一个重中之重——安全,包括后面的很多技术产品都是围绕安全展开。此时第一个被波及提升能力的系统就是流式分析集群,20分钟一轮的数据分析要求压缩到3分钟,同时数据量已经从8亿每天增长到了19亿每天,花了2个月时间断断续续的优化集群结构设计和单机处理能力,里面经历的内容有一天我翻Hadoop的优化过程时看到了相似的场景,具体就不在这里赘述,详细内容依然去挖blog。简单来说:1. 充分利用多核能力用计算换内存。2. 磁盘换内存,用并行设计来保证整体业务时间消耗不变甚至减少。3. Slave shuffle来减少Mater的合并压力。4 .数据压缩减少数据传输消耗和内存占用。…

  另一方面,由于2010年对于jetty7的充分理解和封装,此时看到了又一个新技术的契机,2010年的时候去美国参加Javaone,当时看到有老外用Jetty7的特性来实现Comet功能,Comet原先主要用于CS结构的应用搬到互联网上,因为不能用TCP的长连接,所以不得不用 Http的长连接来替代原来的模式,同时国外开放平台也关注很多新型的API设计,其中就有推ter的Streaming api,这种通过http长连接方式推送消息到外部isv的模式引起了我们的注意。因此我们决定将Jetty7上的封装近一步升级,支持Comet长连接方式,后端通过事件驱动的模式主动推送内部消息给外部,避免外部轮询业务接口。这个设计的最重要点就是如何用最有效最少的线程来守护多个长连接,支持到后端事件驱动的数据下行,如果给每一个长连接支持一个数据推送守护线程,自然即时性最高,但代价就是众多空置连接的守护线程消耗,预知细节挖blog。这种模式刚出来的时候,从上到下都是质疑声,觉得太不符合常规做法了,常规做法就是pull,认为开发和无法接受,认为稳定性一定不靠谱。经过2011年的双十一,当天几个“尝鲜”的开发者一台PC就支持几百万的订单高速处理,就让很多人明白了,技术要敢想,代码要敢写,细节要敢专,没什么不可能。也就从这以后,多样化服务TQL, Schedule API, ATS从开放平台的土壤上都长了出来,为更多的场景,更多的终端,提供了各种解决方案和创新实现。

  2012年:垂直化

  这一年到现在,平台开放淘宝服务900多个,每天调用量25亿,这一年淘宝客由于公司方向转变热潮消退,无线乘势而起,新业务(机彩票,酒店,理财等),P4P,数据类服务都开始运营API,开放平台开发者的客户群体也从C店卖家增加到了B的品牌商,渠道商等。

  这是一个业务多变的一年,这也是淘宝内部对开放平台认可的新阶段。第一个阶段是放任不管,开放平台部门自己要开什么,封装什么。第二阶段是开放什么业务方负责支持开放,但开放后的结果概不了解,也无所谓了解。第三阶段就是业务主动要开放,开放后开始运营服务,培养ISV市场,带动业务的正向发展。

  这一年由于业务量的增长以及分析需求到用户纬度,因此在2011年底启动了流式分析集群重构升级的项目,将新的分析集群项目命名为 beatles,希望他能够象甲壳虫一样,小虫吃树叶,再多都吃的下。2011年底到2012年初,用了近2个半月的时间做了一次完整的重构,将那么多年的补丁经验和老代码重新设计和实现,并且将Mater根据业务可垂直切分,最终解决Master归并压力的问题,当然期间的技术优化点也不少,因为我们的目标从3分钟压缩到了1分钟,而我们的数据量翻番,统计纬度细化到了用户纬度(意味着结果也会很大,不靠文件做中转如何来实现需要更多的分拆和协同设计)。

   这一年起了两个比较创新的项目:JS SDK和无线SDK(IOS,安卓),这两个SDK的出现一定程度上由业务和安全两方面决定。首先2011年年底启动了社区电子商务化的项目,也就是现在所说的轻电商(XTao)项目,将更多的网站和淘宝衔接起来,此时网站间的融合就要求更轻便和简易,最成功的案例就是非死book,于是年初的时候,拿这FB的JS SDK一阵看,就开始动手写了,期间很高兴拉了UED同学入伙,这才使得这个JS SDK变的更加靠谱,更加专业(前端这活谁都可以玩,但要玩好最终还是要对那一系列苦逼的浏览器兼容有所深入理解,现在想想自己当时还是有点无知者无谓的)。同时有了JS SDK,买家类的服务安全性有所保证,因为原先的REST调用在授权以后是无法知道是否是用户发起的还是服务器发起的,而JS SDK一定程度上还要校验Cookie的有效性,可以部分的保证用户的在场知情。而下半年的无线SDK,就是间或的苦读1个月的各种文档,然后就开始动手玩了,由于对Java语言,动态语言,脚本语言都有比较多的使用,因此Objective-C上手并不是那么困难,同时没有涉及到过多的MVC的内容,做 SDK基础层的东西还是比较得心应手,就这样iOS的无线SDK版本就出来了,此时在开放平台的技术团队内部正在执行一个叫做Hack project的活动,其中一个自主项目就是安卓的SDK,因此一个月后,安卓的SDK顺利的诞生了。这两个无线SDK所担负的职责就是把控无线安全问题,不仅淘宝,业界其实很多公司都还没理解无线开放的风险到底有多大,OAuth2基本就无法保证无线的用户安全,因此如何在SDK和服务端融入更高级别的安全设计,成为了无线SDK诞生的第一个重要需求。

    另一方面,开放平台安全体系的构建成为2012年的重点,从两个角度去对安全做的全方位的控制:1. 用户。用户授权更细化了授权操作范围(细粒度到了数据范畴),授权时长。所有信息可监控,归档,快速定位,我们内部叫做Top Ocean,简单说来就是对所有的访问日志做归档,归档的载体是块状文件,归档时对块状文件的所有纪录按照需求建立索引,然后保留索引,上传本地文件到远端分布式文件系统备份。实时的监控服务调用和应用访问,授权异动。2. 第三方应用。采用监控集群对所有ISV的服务器做安全扫描,对普通的Web安全漏洞做扫描,对应用的可用性和响应时间做监控。同时,正式启动聚石塔项目,提供弹性计算和存储能力及可靠的安全网络环境给ISV,帮助ISV提供自身应用的安全性。

  至此为止,5年左右的技术历程部分的展示在了大家的面前,这些只是5年中比较有代表性的一部分,同时技术的发展也只是开放平台的一部分,前5年是技术变革带动开放平台发展,而接下去5年将会是业务变革和理解带动开放平台的阶段,对业务的理解直接决定了开放平台的价值所在,上面文章中轻描淡写的讲了5年内不同开放业务的兴衰,其实这背后却有更多耐人寻味的故事,而5年后的今天淘宝的格局:集市(C2C),天猫(B2C),一淘(电商搜索返利入口),无线,新业务,O2O(本地生活),团购平台(聚划算),这些平台的价值是什么?如何找到自身定位?如何借助外力发展?如何面对流量入口的兴起,传统互联网企业的电商化,电商平台的竞争?这些才是开放平台2012及下一个5年的精彩所在。

原文链接