阅读 311

史上最详细的新浪广告系统技术架构优化历程

内容来源:2017 年 08 月 10 日,新浪广告开发技术专家徐挺在“第二届APMCon中国应用性能管理大会【大规模网络架构优化专场】”上进行的《新浪广告系统的服务化优化历程》演讲分享。IT 大咖说作为独家视频合作方,经主办方和讲者审阅授权发布。

阅读字数:4858 | 13分钟阅读

观看嘉宾演讲视频及PPT,请点击:t.cn/EAEQLSN

摘要

其实新浪很早就开始研究广告系统了,根据UserID + CookieID + 用户行为日志等多重要素进行用户区分,进而针对个体用户做控频投放。同时为了更好的监控广告服务平台的性能和准确性,新浪广告技术团队在此基础上进行了众多改善措施。

技术架构和痛点

sinaX 服务化之前的架构

可能有些朋友对互联网广告的技术架构不太了解,这里先介绍下大概的构造。基本上所有的公司的技术方案中都有一个adexchange——负责广告流量接入以及流量优化的模块(图中的中间模块)。

从上端过来的广告流量都会进行流量优化,优化完成后将向下方的各个投放DSP询价。

整个过程类似拍卖,一个流量进来后,如果被判断为优质流量,就会向一些DSP发起竞价请求,DSP根据内部的算法优化以及用户行为优化给出价格,adexchange接收到这些报价后会决策出胜出者,最后广告请求被给到胜出者。这里面adexchange主要在做两件事,一是决策出竞价胜出者,二是生态层面的调整,adexchange对下方所有投放引擎并不是一致均等对待的,而是根据阶段性广告生态的要求做出调整。

早期我们的adexchange是基于Nginx服务器,在服务器内部有Lua写的业务逻辑,也就是前面提到的竞价规则和流量最优化调度。

SinaX 旧架构的痛点

由于adexchange对投放引擎并非是一视同仁,所以整个流量呈现的是一个漏斗模型。

在adexchange的开发过程中,为了能够更好的实现漏斗模型以及业务提升,我们在内部进行了二次开发,开发过程中发现了很多问题。

用Lua做功能开发并不方便,因为需求的频繁变化导致需要即时响应,而即时响应的过程中有些功能使用Lua嵌入非常麻烦。Lua代码可维护性差,一旦出现人员变动,新人阅读之前代码的成本很高。

第三是业务内的监控很难做,如果采用Nginx加Lua的方式,严格来说唯一的解决方案就是使用某种方式将日志输出,很难在Nginx内部提取状态量发送到关键业务检测点上。最后是测试问题,基于Nginx加Lua的环境做完全量测试或者全量灰度测试很麻烦,一般只能通过直接嵌代码的方式解决,无法采用一些传输的技术方案。

当时之所以要选择Nginx加Lua的方式,是因为觉得Nginx性能不错,配合Lua也能很好解决性能方面的问题。所以对我们来说性能不是瓶颈,真正的痛点在于二次开发。

广告引擎旧架构的痛点

在最前面提到的技术架构图下方有着很多的投放引擎,上图就是引擎的具体实现。2014年的时候这些引擎都是基于tomcat服务器实现,包括用户画像查询、投放单检索、算法粗排等功能都在tomcat服务器上。

这样的架构所带来的最大问题就是线程模型,Tomcat是阻塞式的线程模型,线程分配出去后必须完成请求后才能释放,但是当时我们的日流量非常大,使用tomcat成本很高,效率也很低。

(基于tomcat系统碰到的问题)

Tomcat架构带来的最大业务痛点在于无法应对突发的流量冲击。如果流量在某一时刻突发性的增长几倍,这时除非之前的负载很低,否则只能采用降级和熔断的方式减轻tomcat的压力,但是这样的做法其实会损失公司的收入。

技术痛点总结

我们整个2014年所碰到的技术痛点总结起来主要有四点。

  • 功能单体化严重。所有的广告投放逻辑和投放业务都实现在单个的Nginx加Lua,以及tomcat中,任何的改动都面临着很大的风险。

  • 模块间的功能交互是plug-in方式。这里的模块指的是算法,对于广告来说每天都会有上线,这就意味着要频繁更换plug,增大了系统风险。

  • 编程模型不适应业务的发展。编程模型指的是tomcat单线程必须完成任务才能释放,这种方式很容易堵塞业务,唯一的解决办法就是不计成本部署大量的tomcat服务器。

  • 系统运行状态不透明。

痛点问题的技术分析与选择

针对以上的这些痛点,我们进行了技术分析和选择。

功能服务化。各服务相互独立,降低业务耦合度,提高产品需求开发的响应时间。

RPC。服务间的通讯采用RPC,我们在对比各个thrift、gRPC、dubbo和Finagle等RPC框架的生态圈后,最后采用Twitter开源的finagle RPC框架。

监控。对于广告系统来说监控方面最关需注的有两点,一是系统状态实时监控与跟踪,二是业务数据实时分析与统计。系统实时状态不仅限定于物理机,还要关注qps和超时率以及请求花费的平均时间。业务数据主要和广告业务相关,比如某个广告的实时点击率以及变化,这些业务的实时分析需要及时的反馈给广告投放家或代理商,因为他们会直接对效果不好的广告进行优化。

新浪广告系统的服务化过程

技术的权衡取舍

1、服务分化问题

过去在服务化的初始阶段,我们认为可以将漏斗模型每层需要过滤的量都做为一个服务,这样不仅在业务逻辑上很自然,对于业务开发和新功能增加来说也很方便,要增加产品功能只需要改动对应的服务。

但是后续在原型测试的时候发现部分服务的上下游调用过于频繁,这是因为看上去是经过抽象的漏洞模型,其实流量在模型中未必是平均漏下去的,有可能在某一层会漏的比较少。上下游的频繁调用带来的最直接影响是带宽占用过大,处理时间的I/O占比高。

后来我们对最初设计的原型进行了改动,现在可以自动的根据流量特征来实现服务的合并。

(重构之后的架构图)

新架构中流量下来后会先经过RB和PB,也就是价格最高的在顶层然后往下一层一层筛选。可以看到有些流量是直接到引擎的,它绕开了中间环节,提高了I/O和效率。

在adx的服务化过程中我们最大的体会就是,不要完全的根据业务逻辑进行服务化划分,一定要通过原型用真实流量进行测试才会发现实际上需要做的服务划分。

2、广告检索

(广告引擎服务化的架构)

在广告系统中会碰到这样几个过程,所有流量下来后会经过adx被漏到具体的投放引擎中。投放引擎执行投放过程中,由用户立项服务查询用户画像,根据用户画像会拿到一堆投放单存放在待投候选集中,之后与算法进行交互判断出最适合的投放单,广告投出去后会有计费服务进行计费。所有的服务都会接入到log service进行日志处理。

当时对整个技术的权衡取舍中,我们在Index service上花费了很多精力。最初的方案是直接将Index service做成一个服务,但是随着qps增加数据量已经影响到了Index service的可扩展性。后来我们决定通过rsync的方式周期性的推数据集到投放服务中,这样就解决了调用传输数据量大,影响系统扩展性的问题。

高可靠性和可扩展性的架构

1、系统容错性

在考虑高可靠性和可扩展性的过程中,我们认为有一点最需要解决,即系统容错性,这也是分布式系统中需要考虑的问题。系统容错性具象化来看主要有三点,一是面向错误进行系统设计,这些错误不仅是通讯错误、还包括物理机宕机以及一些不可预测的错误;二是服务的无状态设计;三是故障服务的自动替换。

2、系统可靠性

对于系统可靠性,我们在架构过程中体会最深的有三点。

  • 跟踪监控程序运行状态和热点。运行状态的监控很好理解,而之所以要跟踪热点,是因为服务程序中一些过去未发现的热点随着时间的推移可能会被引发出来,这些热点如果没有及时跟踪,一旦碰到意外状态会马上变成灾难。

  • 实时监控系统异常处理,直接隔离异常服务。

  • 服务分等级做多重备份。一般考虑到容错性和高可靠性我们可能会对同一个的服务部署多个备份,同时这也意味着成本增加。而在广告系统中,我们会将服务进行分级,对于第一等级的做N重备份,对于低等级的考虑到成本问题只做1到2重备份。

3、系统处理能力的可扩展性

做系统架构的时候开发人员通常认为添加服务器一定能提高系统处理能力,其实是未必的,我们不能指望通过简单的增加物理机和计算资源来提升系统的整体处理能力,它们甚至在某些qps峰值的时候还会拉低处理能力。

只有当单机服务处理能力的达到线性化能力的情况下,才能够保证后续系统处理能力的可扩展。

系统的服务处理能力扩展关键在资源分配上,一个分布式系统的关键点在于调度控制,过去调度控制可能只是一些请求,现在更多的是强调资源的合理分配。对于广告来说流量的峰值变化是很严重的,一般凌晨流量不会很多,在早上和中午会分别达到高峰,这样情况下资源的动态分配能够很大程度上缓解资源浪费。

过去我们认为广告请求最值得关注,但是在已做到前两个可扩展性的情况下,输入其实并不是问题,最关键的还是系统的数据热点。因为它往往是系统的瓶颈,比如在数据一致性无法保障的情况下,我们为了一些业务只能去提升一致性等级,这时候就很容易形成数据热点。

在设计系统的过程中一定要结合业务来看数据热点是否一直在,如果存在就将它降级。只有保证数据热点在监控或者系统分析中,不会被判断为一段时间内的热点的情况下,我们才会认为这个系统是可扩展性的。

4、系统架构的可扩展性

在系统架构的可扩展性上我们参考了lambda架构模式,它将系统分为批处理层、服务层、速度层,这样就可以将离线数据和实时数据结合起来对系统提供服务。

广告系统中其实很强调实时数据反馈,比如对于模型计算、投放效果、用户画像等,如果只采用离线数据效果并不会很好。第二个要点是数据一致性,由于广告系统内部的数据量并不是很大,所以最简单的做法是采用分级别的方式来给予保证。

第三点则涉及到一致性hash,虽然一致性hash可以恢复已经挂掉的节点,但是同时也会损失流量。所以我们后续去掉了过去采用的所有请求进来的一致性hash分配,而直接根据下面服务的负载能力来进行流量分发。

(系统优化效果)

技术收获和经验总结

服务划分粗细粒度把握

对于服务划分粗细粒度把握,首先要明确的是切忌简单按照业务逻辑进行服务逻辑划分;第二服务划分的核心是找出真正的业务中心和业务支撑;第三整个过程中没有统一标准,关键在于权衡。

服务的优化重点在业务逻辑的优化

对于系统问题开发人员一般认为通过服务化就可以解决,但现实是系统的热点不会随着服务化自然消失。

所以在进行服务化优化过程中一定要将重点放在业务逻辑的优化上,因为所有的服务化都是为了实现业务逻辑,只有在对业务逻辑有深刻理解情况下,服务的优化才是有目的的,而不是单纯从技术角度考虑。我们认为在服务化的过程中一定要遵循的原则是技术的实现只是手段。

服务需要平滑地切换上线

要对于已经现有的系统进行服务化,这就意味着一定时间内有两套系统需要平滑地切换上线。开发过程中我们开发部门的某个同事就曾表示系统的服务化就像飞行过程中换引擎。

这样高风险的过程中有两个关键点,一是灰度上线阶段时一定要考虑新旧系统并行相互兼容。第二是考虑单个功能的服务化上线对整体系统的影响。

服务治理

对于服务治理我们认为没有必要完全遵循它的原则,而更多的是从分布式系统角度来看,从数据一致性、容错性等方面考虑服务治理。我们对此做了一些划分。

首先是服务处理能力的自动降级和恢复;其次是服务保护,包括负载均衡和熔断;最后是服务的透明化运行,在服务化治理实践过程中会有很多的服务,要对这么多服务进行监控和跟踪,如果使用手动或者脚本的方式效率会很低,而且很不直观,这时候服务透明化就显的很重要。

以上为全部分享内容,谢谢大家!


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