腾讯信鸽实时精准推送系统的演进与实践 | 2017全球架构师峰会

导语

2017年7月7日-8日,ArchSummit全球架构师峰会在深圳召开。ArchSummit全球架构师峰会是InfoQ中国团队推出的面向高端技术管理者、架构师的技术大会,展示新技术在行业应用中的最新实践。腾讯技术工程事业群(TEG)数据平台部的高级工程师甘恒通作为演讲嘉宾,发表了主题为“腾讯信鸽实时精准推送系统的演进与实践”的演讲,以下为现场演讲内容的整理稿。

演讲主题:腾讯信鸽实时精准推送系统的演进与实践

演讲嘉宾:腾讯TEG数据平台部 甘恒通

大家好,我是甘恒通,来自腾讯TEG数据平台部。我今天要分享的主题是《腾讯信鸽实时精准推送系统的演进与实践》。对于应用的用户的生命周期来说分5个阶段,即用户的获取、激活、留存、传播和收入,消息推送是触达用户,提升留存的重要途径,但在海量数据下做到实时精准的推送,有比较大的挑战。我今天主要分享的内容是这4个点,分两个部分。第一部分会介绍一下信鸽的背景和挑战,第二部分会结合一些具体的案例,讲一下信鸽推送的具体解决方案,最后再分享一下我们在运营系统上面建设的一些经验。

关于信鸽的挑战,首先信鸽是一个免费的推送平台,同时现在已经接入了大概几万款的应用。具体技术上的挑战分终端和后台两部分,终端的部分,即service的保活,这部分不在今天讨论的范围之内。对于后台的挑战,主要有这三个关键字:海量、实时和精准。海量是指,信鸽终端并发长连接是几千万的量级,日推送量也有几十亿的量级,这里面主要是因为信鸽对接了公司内部互娱的游戏,像王者荣耀、天天酷跑这些TOP的应用,每天的活跃量也是几千万量级。对于其它的一些应用来说,也有各自的推送的诉求。比如说对于一些新闻类的应用,时效性要求非常高,需要每秒千万级的推送速度,另外一些应用则需要在达到运营目标的前提下,希望尽量减少对用户的骚扰,即精准推送。

针对上面这些挑战,我们看一下信鸽推送系统整体解决方案。主要是7个部分,第一部分是终端SDK,终端SDK信鸽这边分了三部分,第一部分是Android自建共享通道,第二是Android厂商通道,第三是iOS。下一层是统一接入层,主要解决的是对海量长连接的维护,终端设备的就近接入,还有对外的数据GateWay。第三层是消息中间件,采用的是数平自研的TDBank,主要做模块间的解耦和推送高峰期的削峰。第四层是具体的业务处理层,分三部分,第一部分是实时处理,这里面包含我们一些自研的框架,还有一些开源的框架,主要解决的问题是对实时推送、实时效果统计和在线算法模型的训练;第二部分是离线部分,主要是Hadoop和Tesla,可以对海量和周期性的离线数据进行数据挖掘。第三部分是运营工具,主要是云控系统,进行终端在线配置的下发。

下一层是通用的存储层,这里我们是根据不同的应用场景,有一些不同的方案,比如说HBase、CKV、CDB等存储系统。底层是GAIA STACK资源调度平台,它是一个统一的资源调度平台,实现信鸽后台整个系统的虚拟化,还有对于一些算法模型提供一些FPGA、GPU专用集群。右边是运营平台,里面包括业务指标、日报系统、多维查询、监控告警、数据开放。

下面我们具体介绍一下,对于信鸽面临的挑战的一些解决方案。首先信鸽是一个免费的运营工具,成本是我们这边一个很大的需要考量的点:一方面,需要提高设备资源的利用率,可以通过一些虚拟化的方式来解决;第二方面,我们要深入挖掘单机的性能。对于单机性能的优化,有5个方面,从下往上看是硬件、驱动、操作系统、协议栈、架构。

先看一下在操作系统这个层级是如何对海量并发长连接的接入进行优化的。这里主要挑战是在内存,通过slabtop命令可以看到当前系统内存分配的情况。从这一栏可以看到,当我们构建了大概200万长连接的时候,整个系统的内存基本上已经满了。对于长连接的内存使用,会消耗在这6个数据结构上。

我们深入分析这6个来自epoll模型和内核TCP协议栈的数据结构。对于epoll模型,底层实现是一个特殊的文件系统,epoll文件的内容主要是保存了一棵红黑树和就绪的被侦听文件列表rdllist,红黑树的节点是被侦听文件对象。epoll主要提供了三个API:epoll_wait、epoll_create、epoll_ctl。当我们通过epoll_ctl把一个文件加入到epoll的时候,会创建两个数据结构epitem、eppool_entry,epitem关联到被侦听的文件fd,eppool_entry保存了事件就绪时设置rdllist的回调函数ep_poll_callback。我们从另外一个链路看一下网卡的收包流程,当数据包过来的时候,网卡接收完数据会产生一个硬中断,数据通过DMA拷贝到内存。系统通过软中断守护进程,调用网卡的驱动来完成数据包的解析并最终调用poll方法。poll方法会回调设置好的ep_poll_callback,即把fd挂到rdllist中,当调用的epoll_wait时候,即可获取到就绪的fd集合。

对于海量的并发连接,主要通过调整操作系统的配置来解决。信鸽使用了公司自研的tlinux 2.0,系统对于内存这一块也做了比较多的优化,具体的配置参数如下,可以看到,主要也是一些epoll的限制和TCP的内存的限制。

看完操作系统层级的优化,我们再来看一下另一个主要性能优化的点——server框架。信鸽对于关键的接入层server使用C++语言进行开发。好的server框架主要有两个要点,1是对底层RPC通信的良好封装,使得开发只需要关注业务逻辑代码的开发,2是高性能,框架对消息处理CPU消耗尽可能少。通过Intel® VTune™ Amplifier这个工具我们可以对server框架进行性能评测,这里列了两个框架。左边这个框架可以看到把很多CPU都浪费在消息的内存拷贝,同时对消息的处理进行了过多的封装。对于右边的框架就做得比较好,对于框架的问题就不存在了,主要是操作系统本身的消耗,还有协议栈的消耗。这部分对于有更高的要求来说,还可以进行深度优化。这是我们后台选用的框架,主要实现的要点有三个,第一是池化技术、第二是协程模型、第三是无锁框架。这里的Proxy主要用于接入网络的请求,具体的业务逻辑代码是放在Work进程里面处理。整个框架做了一个通用的抽象,将一次数据请求主要分为数据接收、数据路由、最后是具体的数据处理。数据接入进程和具体数据处理进程,通过无锁的共享内存来通信,这样就避免了很多不必要内存拷贝和消息编解码操作。

对于处理业务逻辑的工作进程,比较常见的有三种模型,第一种是进程同步模型,数据进来以后,由独立的线程进行处理,优点是代码实现比较直观,缺点是处理海量并发用户请求的时候,操作系统对多线程的切换会成为一个主要的瓶颈。第二种用得比较多的是异步事件驱动模型,由epoll进行事件的侦听,通过一些状态机来驱动业务逻辑代码的执行,优点是性能较高,缺点是当业务逻辑分支多的时候,代码非常难维护。我们这边是采用协程模型,协程和线程和类似,它跟线程不同的是,线程的切换和调度需要自己来实现,协程模型可以采用同步的思维进行代码开发,又具有异步模型的高性能,协程切换优化的点是,通过汇编的方式,直接把相关寄存器中的值保存在内存,来进行上下文的切换。

下面我们看一下协议栈的优化。这里的协议栈主要是指linux内核TCP协议栈。linux内核的协议栈主要问题是相关Socket的方法都是系统调用,需要从用户态切换到内核态,数据从内核空间到用户空间也需要进行额外的内存拷贝操作。业界有一些开源的用户态TCP协议栈,可以解决这两个问题。使用用户态的TCP协议栈需要优化的点是相关的接口和系统自带的Socket接口命名不一致,直接使用涉及到大量代码的修改,我们怎么样做到在不修改代码的情况下用TCP协议栈呢?这里是通过Hook机制,拦截相关Socket系统调用,dlsym符号解析返回自定义的Socket方法。

下面我们再看一下单机性能优化的最后一个点——对于硬件性能的挖掘。硬件性能的挖掘,主要是善于利用Intel CPU的指令集。可以拿我们经常使用的CRC32操作来举例。一般的CRC32实现是根据算法定义来执行字节转码操作,而Intel提供了一套SSE4.2的指令集,它里面包含CRC32的方法。我们可以看一下35W次计算两者的耗时对比,直接用Intel的指令集比一般的实现方法快6倍。

刚才前半部分主要是对于信鸽后台单机性能的挖掘,下面我们再来看一下对于实时精准推送系统,我们是如何构建的。

信鸽的推送系统主要分为三部分,第一部分是数据、第二部分是具体的系统实现、第三部分是具体的应用。数据源有5种,第一部分是应用自定义的数据,如应用自定义的人群标签,如果是一个游戏,可能是一些高付费的玩家。第二部分的数据是我们腾讯体系内部的用户画像。第三部分是我们公司内有合作的产品数据。第四和第五部分是我们的两个产品,MTA和XG,MTA的数据和XG的数据可以打通,也是系统的两个最主要的数据源。具体的系统包括离线系统和实时系统,离线系统主要解决的问题是对于一些周期性的大规模数据进行用户属性和兴趣特征挖掘;实时系统,包含实时数据采集,解决的问题是对用户实时行为的分析、对推送效果的实时跟踪、在线模型的训练等等。

具体的应用有这三个:实时推送、推送助手、ABTest。推送助手和ABTest的作用是更好地帮助用户使用消息推送来进行产品的运营。比如推送助手,对于很多运营人员来说,可能没有相关的运营经验,对内容的管理,他可能只大概知道需要推送的目标群体,但是他对推送文案不知道如果编写会更好,这个时候我们后台会对历史的推送进行数据的收集,对文案和推送的效果进行关联性的分析,当他选择一个推送场景的时候,我们就会把一些文案的样式和关键词给到他,让他自己组织出一个更好的推送的文案。另外一个是ABTest,用户自己手里有几个文案或者目标推送人群,当他不确定的哪个更合适的时候,我们给他提供ABTest的能力,从目标推送人群中抽取部分测试用户,多个文案经过实时推送,在几分钟的时间里,把推送的效果即点击率反馈给用户,系统自动或者由用户选择一个最佳的推送文案或者是一个最佳的目标人群对剩下的用户进行推送。

关于数据这部分,再展开介绍一下,主要有MTA、信鸽和用户画像。MTA主要有两种类型的数据,一类是自定义事件,对于一个购物类的应用,可以将添加购物车或者下单等用户点击的行为当成自定义事件上报;第二类是一些用户在应用内进行页面浏览的行为数据。对于信鸽来说有一些推送的数据,也有一些平常的注册或者是卸载的数据。推送的数据主要有展示、点击、清除。对于用户画像来说,我们用的是腾讯内部自建的一套用户画像,它里面包含两大类,第一大类是基础属性、第二类是行为属性。基础属性又分为人口属性和兴趣属性,人口属性主要包括年龄、性别等等,兴趣属性包含女装、女鞋等等。对于行为属性来说,主要有自定义标签和用户状态,标签比如宝马和安踏鞋等等。用户状态主要是育儿、毕业等等状态。

下面看一下系统的实现,主要分实时和离线。离线部分主要是用来进行人群的挖掘。对于精准推送来说,它的核心是根据应用运营的目标将消息推送给匹配的目标人群。应用的运营的目标一般有提升用户活跃度,潜在流失用户挽回,提升应用收入等,不同人群的挖掘方法可能不尽相同,但流程基本一致,一般会分为数据的准备、模型的构建、预测结果、输出。

我们这里举一个实际的潜在流失用户人群挖掘的例子。我们想预测一下这个应用里面可能有哪些人是会流失的,这样应用可以针对这部分人群做一些挽留的推送。原始数据保存在HDFS或者TDW里面,经过数据加工以后,把用户可能流失的一些特征给提取出来,比如说有首次注册的时间、每日启动的次数、每日的活跃状态和最近登录的时间。特征提取后有两条路径,一是要构建一个潜在流失用户预测的模型,二是在模型的基础上进行潜在流失用户的预测。对于模型构建部分,需要结合真实的流失情况和特征,作为训练的样本,经过算法模型的训练之后,我们就得到了一个潜在流失用户预测的模型。对于每天新增的数据,我们也是经过特征提取之后,把数据输入这个模型里面,就会得到一个具体的预测结果即可能会流失的人群集合。对于算法模型,我们这边主要用了一些机器学习里面的统计学习的方法,没有用到一些现在比较火的深度学习的方法。因为我们在实际进行训练的时候发现当我们的数据样本非常大的时候,用一般的比如C4.5决策树模型,通过一些判定规则,就能够知道用户是否流失,或者是贝叶斯,通过特征类别的条件概率分布预测是否会流失,采用这些模型召回率可以达到0.76和0.89的水平,已经满足我们的应用场景,所以没有采用计算复杂度更高的深度学习的模型。

下面再来看一下关于系统的部分,分两种不同的场景。第一个场景是实时推送,第二个场景是要做实时的多维分析,这两套系统实现的核心是对我们的数据构建倒排索引,倒排索引在业界用得比较多的是在搜索引擎里面文档的全文检索,但是对于我们的系统,倒排索引是根据我们实现的应用场景进行构建。比如说,对于一些画像的信息,男性用户,可以做一个倒排索引,保存成bitmap的数据格式;信鸽里面一个应用具体的设备标识,做一个文本转换数字的操作后,经过倒排索引之后,保存成 bitmap的数据格式。对于实时推送部分,要求数据秒级更新,我们用了storm进行倒排索引,数据经过转换之后,存储占用很小,这样我们把它放在内存里面,可以进行实时的更新和检索。

对于另外一个场景是,我们需要对历史数据、原始数据进行实时的多维分析,比如需要分析最近几天之内大盘所有用户的点击情况。这里面可能涉及的数据量就会非常大,需要采用离线的方式进行索引的构建。多维分析的索引和实时推送的索引是不一样的,它主要实现的方式是对所有的数据进行规范化的处理,每个字段的格式基本上保持一致。对于每一条记录会根据预置的规则进行数据的抽取,需要构建倒排索引的属性字段会进行规格化和映射的转换,数据记录根据设备标识进行分区,每个分区内的记录进行独立的编号,倒排索引即为属性字段对应的记录的编号,离线处理的最后是落地成统一的索引文件,保存在HDFS中。当一个分析的需求过来的时候,会有一些pull节点,把这些索引的数据拉到本地并加载到内存,经过查询规则解析后执行相关索引文件的检索、分组和聚合函数的运算,最后由一些汇总节点将结果进行合并后返回给查询者。

我们这里举一个具体的场景来说一下倒排索引是如何应用到实时推送中的。比如一个应用对在广东的男性用户进行一次推送,会经过一个这样通用的推送流程:一个推送的任务到达一个调度的节点,决定是否下发推送任务,如果下发,则会经过目标人群的检索,筛选出男且是广东的用户,接下来进行通道检索,信鸽这边既有自建的通道,也有厂商的通道,用户在推送的时候,他可能觉得厂商的通道更加稳定、更加高效,这个时候他可以选择对于华为设备走华为通道、小米设备就走小米通道,对于其它的设备就走信鸽自建的通道。下一步就是地域检索,信鸽这边的设备是就近接入的,我们在全国部署有这几个集群,分别是深圳集群、上海集群、天津集群和海外的HK集群。这里选择了广东的用户,所以选择深圳集群。对于一个传统的系统来说,一次推送可以这样实现,一个应用下的N个用户的推送,转换成N次RPC节点间调用,人群信息、通道信息、地域信息分别保存在Mysql或者Nosql数据库中,每个RPC调用,需要到数据库里面检查一下他是否是男的,是否是广东的用户,再看一下是不是华为的设备或者小米的设备,判定完之后要进行地域的检索,看看接入的是哪个集群。整个流程下来之后,要经过大量的数据库操作,才能完成一次推送。

但是经过倒排索引的构建之后,所有的数据都可以放到内存中。比如男性人群,可以构建一个bitmap,小米的通道也是一个bitmap,华为的通道也是一个bitmap,不同的地域分别是不同的bitmap。当一次任务下发的时候,对bitmap进行一些&操作,就可以把最终需要推送的人群下发到相应的接入机中,整个过程可以做到ms级别。对于推送下发整个流程,检索是一方面,另一方面还需要查询路由信息找到终端TCP长连接的接入机,这些路由信息包含接入机的编号、进程的编号、长连接socket fd的编号,用一个long数据类型即可保存,所有的路由信息保存在分布式的Nosql内存数据库中,整个推送流程只有接入机的路由需要进行数据库的查询,结合本地的缓存,可以做到kw/s的推送下发速度。

信鸽的接入Server,实现了docker化,由GAIA STACK进行资源管理,当某个节点发生down机的时候,GAIA STACK可以进行自动切换,同时可以根据现网负载情况,快速扩缩容。同时虚拟化也使得信鸽的VIP通道和普通通道变得很容易实现:对于VIP通道,分配的设备资源会更多,支持的标签类型也更加的丰富。

下面再看一下信鸽如何对推送进行实时的效果评估。一般推送的效果评估主要看CTR,但是CTR可能对于一些用户来说,不能反映他的运营目标,他可能更关注的效果是我做了一次推送,用户有没有到达我的目标页面,或者说推送了正在进行促销的通知,有没有更多人进行下单购买。这些场景可以通过在前台给用户管理推送效果跟踪配置的方式来实现。用户每次推送之前,可以指定推送效果如何评估,这里可能是一些目标页面集合的浏览pv或者自定义事件的uv,系统把它转化成一个配置文件。当他进行推送的时候,我们就会把配置文件下发到统计系统里去。当执行推送完之后,应用的用户就会产生点击、注册、浏览的行为。这些数据到进入到系统的时候,主要会做两部分的操作,第一部分是根据推送文件指定的方法进行数据的抽取、过滤、转换、加载并更新相关的倒排索引数据,当真正需要指标输出的时候,我们会有一个管理的节点,进行计算规则的下发,比如说点击的用户里面有多少是男的,检索以后进行一次bitmap的&操作,就能统计出有多少用户了。数据会实时刷新到Redis存储中,应用开发者在前台查询结果的时候,我们就会把这个结果反馈给用户。

最后一点再介绍一下硬件的虚拟化,前面介绍信鸽整体系统时提到的虚拟化,主要是软件的虚拟化,硬件的虚拟化主要是指网卡这一块。我们信鸽采用的网卡是Intel的82576,有一个特性是SR-IOV,SR-IOV是一块物理网卡中,有一个管理的通道和多个相互独立的收发通道。管理通道称为PF,收发通道称为VF。比如说对于Intel8576来说,它就会有8个VF,每个VF都会有独立的配置寄存器,独立的收发队列,独立的硬件中断,和一块具体的网卡几乎没有区别。VF可以通过PF的功能模块进行控制,SR-IOV具体的应用是当启动了一个容器的时候,我们通过PF控制VF赋值到容器的命名空间,这样就相当容器和具体的VF关联起来了。

使用SR-IOV可以提升容器网络处理的能力,这里有两个测试的数据,第一是TCP短连接、第二是长连接,在不同的容器数量和不同数据包大小的测试条件下,和一般的docker-host模式docker-fixedip模式相比,docker-sriov的吞吐量有明显的提升。使用SR-IOV另外一个额外的配置是通过isolcpus和cpuset,将容器独立到特定cpu上,充分利用cpu多核并行工作的特性。

以上是我演讲的内容,谢谢各位的聆听!

原文发布于微信公众号 - 腾讯技术工程官方号(Tencent_TEG)

原文发表时间:2017-07-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏服务端技术杂谈

架构细节 | 看看 Medium 的开发团队用了哪些技术?

image.png 说到底,Medium是个社交网络,人们可以在这里分享有意思的故事和想法。据统计,目前累积的用户阅读时间已经超过14亿分钟,合两千六百年。 ...

4206
来自专栏FreeBuf

看我如何绕过限制访问到Google内部管理系统(价值$13337)

大家好,我是印度一个电子工程专业大三学生和一名漏洞挖掘新手。最近,我发现了谷歌(Google)一个验证绕过漏洞,利用该漏洞可以直接访问到谷歌的Youtube 卫...

4624
来自专栏程序人生

闲扯code review

今天早上要开会,所以文章早点放出来。 如果说git终于让工程师在合作撰写代码的过程中找回了丢失已久的乐趣,那么,code review的过程还是让人相当地抓狂。...

3225
来自专栏新智元

微软谷歌再曝 CPU 新漏洞,Intel、AMD、Arm 全部遭殃

1662
来自专栏腾讯Bugly的专栏

《广研Android卡顿监控系统》

实现背景 应用的使用流畅度,是衡量用户体验的重要标准之一。Android 由于机型配置和系统的不同,项目复杂App场景丰富,代码多人参与迭代历史较久,代码可能会...

1.1K4
来自专栏AI科技大本营的专栏

周末大料|CMU研发数据库调优AI,水平超DBA老炮

这个周末,最不开心的应该是优秀的数据库管理员了。 这些优秀的数据库管理员(以下简称数据库管理员为DBA),原本可以靠自己的本事,享受高薪,可是,好景不长了,因为...

4136
来自专栏数据小魔方

ggplot2又添新神器——ggthemr助你制作惊艳美图

今天在浏览ggplot扩展插件目录的时候,又发现了一款神器——ggthemr。 这是继ggplot的ggtheme包、RColorBrewer包之后(不算ggt...

3545
来自专栏企鹅号快讯

Python 开源项目 Top30 值得收藏

编译 | AI科技大本营 参与 | SuiSui 继推出2017年机器学习开源项目Top 30榜单后,Mybridge AI又推出了一个Python开源项目To...

41310
来自专栏水击三千

数据库设计(一) 需求分析

目前,大多数的应用系统都属于数据库应用程序,都离不开数据库的支持。数据库设计方案的优劣对于应用程序的运行至关重要。数据库设计过程就是针对具体的应用环境,设计优化...

31210
来自专栏AI科技大本营的专栏

从15000个Python开源项目中精选的Top30,Github平均star为3707,赶紧收藏!

翻译 | AI科技大本营(ID:rgznai100) 参与 | SuiSui 继推出2017年机器学习开源项目Top 30榜单后,Mybridge AI又推出了...

4436

扫码关注云+社区

领取腾讯云代金券