汽车之家 | 广告内容自动化投放的技术思考和方案
赵伟冲 之家技术 稿
1.背景
1.1 项目背景
汽车之家作为汽车行业垂直类重要资讯平台,积累了大量用户,日均流量大;充分利用之家的流量,打造一款以汽车关注效能提升为目的,以内容+人群包智能匹配为核心的优质产品,更好的为买车、看车、用车的用户服务,这是智能关注平台系统这款产品的初衷和背景。
2.初版方案
2.1 说明
在了解项目背景,明确目标和要求后,分析项目的需求,需要明确项目的整体流程和项目(模块)如何拆分。
2. 2 项目分析
► 实现智能匹配自动投放
● 人群包:之家大数据按照人群包自定义标签进行人群包划分,划分多个不同人群包;
● AGC:智能图文系统根据人群包的不同I标签(用户买点偏好),定制化生产AI图文文章
● 排期:根据投放车系任务配置以及历史投放曝光情况,利用AI算法确定投放人群、渠道
● 外展:根据不同的文章内容以及文章I标签生产不同外展规格的标题和图片
● 智能关注平台:根据关注平台的配置的投放任务,完成整个智能关注平台的投放流程
► 充分利用之家流量,投放广告创意尽可能的覆盖所有渠道广告位
● 外展:实现覆盖尽可能多的广告位,就要支持生产广告位对应的规格外展;所以,算法外展除了支持不同文章生产标准外,还要支持按照同一篇文章不同规格的外展标题和图片
► 智能关注整体流程UML序列图
结合项目分析,整理出智能关注平台整体流程UML序列图如下:
► 初版项目业务架构图
结合项目分析,设计智能关注平台初版业务架构图 :
3.问题与挑战
3.1 说明
知道了整体的实现流程和系统拆分方案后,需要明确实现方案以及实现方案中遇到的问题和难点。
3.2 问题与挑战
►自动投放平台涉及众多环节,链路长,如何按照指定业务的实现顺序串行起来最大效率执行
● 整个投放流程,涉及任务创建、人群包圈定、人群包拉取、请求生产AGC、文章拉取、智能排期、外展生产、DSP投放、CMS投放 涉及6个系统 9个环节。
● 怎么调度每个环节的执行
● 怎么管理和控制执行环节
● 执行异常,如何快速提醒,如何控制是否失败重试
● 调度阻塞如何处理
►单日生产外展、创意数据数据量大,数据如何存储
● 数据分析:
每日生产外展卡片数量 = 任务数 * 人群包个数 * 每个人群包生产AGC个数* 支持外展规格数量。
按照单任务日均生产6.6w左右卡片,如果投放100个任务,那么就是外展日均总量660w(投放的任务数量和支持的卡片规格数量,会随着项目深入,还会增加);问题:按照外展日均660w左右的量级,如何存储这些数据?
►单日生产外展、创意如何提升生产效率
● 问题:生产外展卡片只是其中一个环节,外展数量与创意数量是1:1,单日生产660w左右的外展和创意,如何提升生产效率,缩短该环节的耗时是一个待解决重要问题。
4.实现方案
针对以上问题解决方案:
4.1 分布式定时任务框架
xxl-job的引入
► 分析与思考
● 项目中涉及的各个环节,可以抽象封装成待执行的任务,每个任务调度时间不确定,但是各个任务之间其实相互有依赖,这是典型的时间驱动场景,很容易想到可以做一个执行器服务,把各个环节的调度实现为一个个待执行定时任务;
● 生产卡片、生产创意的环节,数据量比较大,要求支持集群,支持分布式调度
● 调度任务较多,最好有可视化管理页面控制每一个任务的执行
● 结论:传统的定时任务已经不满足现在的分布式架构,所以需要一个分布式任务调度平台,目前比较主流的是elasticjob和xxl-job
►技术选型
► elasticjob和xxl-job分析与比较:
● 分析: elasticjob和xxl-job都支持任务分片处理,适用分布式场景,而且都提供友好的管理界面,符合智能关注项目的需求
● 应用对比:
相比xxl-job,elasticjob需要引入zookeeper , mesos, 增加了系统复杂度
xxl-job更为轻量,学习成本简单,失败策略和路由策略丰富,更适用智能关注应用场景
● 场景对比:
elasticjob关注的是数据,增加了弹性扩容和数据分片的思路,以便于更大限度的利用分布式服务器的资源,学习成本相对高些,推荐在“数据量庞大,且部署服务器数量较多”时使用;
xxl-job侧重的业务实现的简单和管理的方便,学习成本简单,适用场景:用户基数相对少,服务器数量在一定范围内的情景下使用;
● 结论: xxl-job相对更为轻量,学习成本低,且满足和适用智能关注应用场景,最终选取xxl-job作为智能关注系统的分布式定时任务框架
4.2 分库分表框架 ShardingSphere引入
1. 分析与思考
► 是否需要分库
● 分库依据: 如果数据库出现瓶颈,如IO瓶颈、连接数、硬件资源等性能瓶颈,可以考虑分库;
● 分库场景:
大量请求阻塞:在高并发场景下,大量请求都需要操作数据库,导致连接数不够了,请求处于阻塞状态
SQL操作变慢:如果数据库中存在一张上亿数据量的表,一条 SQL 没有命中索引会全表扫描,这个查询耗时会非常久
存储出现问题:业务量剧增,单库数据量越来越大,给存储造成巨大压力
● 分库同样带来一些问题,比如使系统的复杂性提高:
分布式事务
跨库关联查询问题
排序、分页、函数计算等问题
分布式ID问题
多数据源问题
● 分析:智能关注场景为单表存储过大,造成的CURD的性能问题,尚未出现大量请求阻塞等瓶颈,mysql并发连接数并不高,而考虑到分库造成的系统复杂度,如果分库反而得不偿失
● 结论:不分库
► 是否需要分表
● 数据分析:单任务日均生产6.6w左右卡片,如果投放100个任务,那么就是外展日均总量660w,两天就会上千万,单表数据量太大,影响存储和系统性能;
● 分表原因:单表太大,CRUD都受影响,索引膨胀,查询超时
● 解决方案:分表处理,切分成多个数据集更小的表
●拆分方式:分表有水平分表和垂直分表两种方式,
垂直分表:大表拆小表,就是将不常用的、长度较长的(如text)拆分到扩展表
水平分表:针对数据量较大的单表,按照某种规则,切分到多张表中(还是在同一个库中)
● 分析:智能关注系统日产数据百万级,单表存储过大,需要分表操作;智能关注自动投放平台系统按照日期生产物料数据,可直接采取按照创建日期进行切分即可。
● 结论:分表处理,且水平分表
► 最终方案
最终方案:按照上述分析,最终采取的方案为项目初期不分库,只是对生产量较大的卡片相关表、创意相关表进行按照时间切分,水平分表。
2. 技术选型
► 说明:分库分表之后可能会面临从多个数据库或多个子表中获取数据,一般的解决思路有:客户端适配和代理层适配。
业界常用的中间件有:
shardingsphere(前身 sharding-jdbc)
Mycat
► 原理分析
两者的定位是不一样的,shardingsphere是本地拦截处理,mycat是服务器端拦截处理
sharding-jdbc:基于AOP原理,在webapp本地进行sql的拦截,解析,改写,路由和结果归集处理
mycat:mycat是安装在服务器上的中间件工具服务,代码里直接连接mycat,由mycat做sql改写分发结果归集,归并数据结果完全解耦,保证数据库的安全性,支持多种开发语言的连接。
**► **优缺点:
sharding-jdbc:优点-相对mycat效率高;缺点-代码有侵入性且只支持java语言
mycat:优点-不用调整代码即可实现分库分表,将数据库连接地址改为mycat的地址即可;缺点-效率低
**► **技术选型对比
**► **总结
总结:sharding-jdbc与mycat都支持mysql数据库,都支持java语言,从这个角度考虑,这两个都可以;但是从性能和扩展的角度考虑,优先选择sharding-jdbc;
5.新的问题与改进方案
5.1 问题
以上的实现方案解决了主题框架的实现以及日均百万级数据的存储,已经可以初步实现创建任务、生产物料、引擎投放的功能了。
但是还有一个核心问题待处理:外展和创意日均660w的生产量,这个量级生产量,在单机测试环境生产,耗时4小时以上的时间才能生产完成,是自动投放平台链路的瓶颈,如何缩短全链路的生产工时,如何提升生产效率,是需要解决的关键问题。
5.2 改进方案1:并发处理
► 方案:
上述实现方案中已经选取了xxl-job,利用分布式定时任务可以按照执行数据按照应用服务台数进行数据分片,多台服务器多进程级并行处理,还可以每台服务器上进行多线程处理,提升效率;
多线程实现方案就采用比较成熟的spring线程池即可,减少重复创建线程的过程。
● 前提:下游的算法标题和图片服务不是瓶颈,足够强的处理服务;
► 问题: 工作线程数设置为多少合理的问题?
● 分析:使用spring线程池,关键配置线程池的5个参数:corePoolSize、QueueCapacity、MaxPoolSize、KeepAliveSeconds、RejectedExecutionHandler,最核心的是设置corePoolSize大小
● 定位:生产外展卡片在调用算法生产之前,需要读取排期数据、车型车系数据、文章数据,这些数据获取都是读取mysql和redis以及外部服务等,是典型的IO密集型
● 原理:N核服务器,通过执行业务的单线程分析出本地计算时间为x,等待时间为y,则工作线程数(线程池线程数)设置为 N*(x+y)/x,能让CPU的利用率最大化
● 方式:Worker线程在执行的过程中,有一部计算时间需要占用CPU,另一部分等待时间不需要占用CPU,通过拆分过程,日志打印方式,确定cpu计算时间和io交互时间
● 结论:线程数=16*(168ms+210ms)/168ms ≈ 36
► 实现:
● 结论:通过任务分片+多执行器多线程并行处理,可提升一定的生产外展卡片效率
5.3 改进方案2:减少IO-读取redis
改为localcache
► 方案:
生产外展卡片时候,组装请求参数,需要读取外展规格参数、车型车系等数据,把这些数据由mysql或者redis改为使用Cache来减少IO次数,提升性能;
► localcache选型:
采用Caffeine而不采用JDK自带Map,是因为Caffeine基于Guava Cache增强的新一代缓存技术,缓存性能极其出色;
JDK内置的Map可作为缓存的一种实现方式,然而严格意义来讲,其不能算作缓存的范畴。
原因如下:一是其存储的数据不能主动过期;二是无任何缓存淘汰策略;选型对比如下:
● 序列化
● 进程关系
● 性能
● 结论:
把生产卡片环节,读取dsp外展卡片规格信息以及车系、车型基本数据时候,由从redis读取改为从本地缓存读取,而localcache采用缓存性能较为出众的Caffeine,提升生产卡片组装数据过程的性能.
5.4 改进方案3:异步处理-
引入消息队列
► 异步处理: 对外推送创意投放由同步接口交互改为kafka消息推送
● 思考
投放过程生产的物料中,外展卡片与智能创意是1:1,同样的日均产量大(约每日生产660w),对外投放的实际上最终是智能创意,初版实现方案是智能关注同步请求dsp生产创意的接口(RPC方式)实现的。
● 问题:
最初方案选择同步接口方式实现初衷是什么?是否可以改为异步请求实现,提升投放端生产效率
异步实现具体采用哪种实现效果更优
● 方案
针对以上问题,思考如下:
问题1: 最初选择同步接口实现请求dsp创意生产,是希望在投放端可以持久化记录下来生产了哪些创意,并且如果某些创意如果有问题,可以在投放端进行请求操作批量下线操作;
但是其实,没有必要在智能关注投放端记录这些创意,实际情况下每日投放的创意数量巨大,如果创意有问题,真实下线的操作是按照文章粒度进行下线,在投放端只要维护文章与dsp端的任务计划和投放单元的关系即可,下线时候按照文章对应的任务计划或者投放单元粒度下线即可,在投放端不需要持久化保存智能创意结果。
结论: 综上所述,为了提升效率,不需要持久化保存创意,生产创意可以改为异步方式请求实现。
问题2: 异步常用的方案有多线程异步执行、定时任务异步调度、消息队列处理;由于不用在智能关注投放端持久化保存创意内容,所以不需要请求dsp创意接口了,其实前两种方案不适合处理现在面临的问题,而消息队列具有异步执行、削峰、解耦的特性,适合现在的场景。
结论: 综上所述,消息队列的方案更适合解决问题2
● 选型
目前业内主流消息队列主要有:kafka 、 RabbitMQ、RocketMQ
分析与总结:
► kafka
● 优点:常规的单机配置下,可以达到每秒十几万的QPS,适合大数据场景下使用
缺点:容易丢失数据——kafka收到消息不是直接落盘,而是放内存了,宕机后会导致丢失数据;功能相对其他两个MQ较单一
► RabbitMQ
● 优点:可以保证数据不丢失,能保证高可用性,也就是集群部署的时候部分集群宕机可以继续运行;然后支持部分高级功能,比如:死信队列、消息重试之类的。
● 缺点:吞吐量低,相较于Kafka只有每秒几万的级别,高并发场景下容易达到瓶颈;底层源码问题,他的开发语言是erlang,难以读懂和修改。
► RocketMQ
● 优点:吞吐量高,单机可达到10万QPS以上;可以保证高可用性,性能很高;而且支持通过配置保证数据绝对不丢失,可以部署大规模的集群;支持各种高级的功能,比如延迟消息、事务消息、消息回溯、死信队里、消息积压等;底层代码基于Java开发,便于阅读和修改。
● 缺点:兼容性上不是太好,没有在 MQ 核心中去实现 JMS 等接口,有些系统要迁移需要修改大量代码。
● 结论
综上所述,RabbitMQ性能较低,不适用智能关注的场景,kafka和RocketMQ都满足智能关注场景,最优选择为RocketMQ,但是由于是跨部门开发,由于DSP本身已支持kakfa消息队列,结合开发时间等因素,综合考虑最终选择的kafka消息队列来实现。
6. 完整技术方案
7. 数据结果
► 生产效率: 按照660w的数据量为例,由耗时4小时缩短至1.4小时,生产外展卡片由2.7w/分钟生产量提升效率至7.7w/分钟,效率提升2.8倍
► 业务支持: 目前智能关注平台支持100+车系任务生产与投放,并且支持了140个外展规格对应的广告位的投放,充分利用有效的流量实现智能投放,符合最初项目定的目标和要求;
8.补充说明
8.1 范围界定
本文章给大家介绍智能关注平台的自动投放平台系统相关技术,重点介绍的是实现投放的整体技术方案以及实现方案中遇到的性能问题和对应的提效的方案
整个智能关注平台还需要其他部门和系统的支持,比如:大数据人群包的圈定、算法组的智能排期实现、算法组的最优标题生产、算法组的外展标题与图片最优匹配、AGC系统智能图文系统的实现、dsp引擎的实现等等,这些技术方案不再本次分享范围内;
**9.总结 **
9.1 基础技术方案总结
汽车之家智能关注平台项目以内容+人群包智能匹配为核心,实现智能投放,针对该项目特点,引用分布式定时任务框架来完成各个环节的调用,经过技术选型对比,最终选择较为轻量的XXL-JOB作为分布式定时任务中间件来实现任务调度;
其中智能外展和创意日产数据量近660w,为了提效和实现合理存储,对外展相关表和创意相关表进行按照日期分表处理,引入分库分表框架ShardingSphere,解决了面临的问题。
9.2 改进提效方案总结
针对生产外展和创意量大提效的问题,采用并发处理策略,具体为xxl-job数据分片处理+多线程并发请求,同时针对请求过程中尽量减少IO次数,把组装请求参数读取mysql、redis改为读取本地localcache处理,完成生产外展卡片由2.7w/分钟生产量提升效率至7.7w/分钟,效率提升2.8倍效果
针对同样量大的创意生产,改为异步处理,对外推送创意投放由同步接口交互改为kafka消息推送,进一步优化生产创意的效率,并且做到了流量削峰和外部接口交互的功能解耦,整体链路提效20%以上。
参考文献
【1】shardingsphere官方文档:https://shardingsphere.apache.org/document/current/cn/overview/
【2】Mycat2权威指南:https://www.yuque.com/books/share/6606b3b6-3365-4187-94c4-e51116894695
【3】elastic-job官网:https://shardingsphere.apache.org/elasticjob/current/cn/overview/
【4】xxl-job官网:https://www.xuxueli.com/xxl-job/
【5】架构师之路:https://www.w3cschool.cn/architectroad/
【6】Caffeine 官网:https://github.com/ben-manes/caffeine
【7】caffiine其他压测参考:https://github.com/ben-manes/caffeine/wiki/Benchmarks
作者简介
赵伟冲
■ 主机厂事业部-技术部-广告技术及系统团队
■ 2019年加入汽车之家,负责之家智能关注平台、自动投放平台、AGC智能图文系统、VGC文章创建与发布系统的架构设计及研发工作