OpenResty 在马蜂窝广告监测中的应用

广告是互联网变现的重要手段之一。

以马蜂窝旅游 App 为例,当用户打开我们的应用时,有可能会在首屏或是信息流、商品列表中看到推送的广告。如果刚好对广告内容感兴趣,用户就可能会点击广告了解更多信息,进而完成这条广告希望完成的后续操作,如下载广告推荐的 App 等。

广告监测平台的任务就是持续、准确地收集用户在浏览和点击广告这些事件中携带的信息,包括来源、时间、设备、位置信息等,并进行处理和分析,来为广告主提供付费结算以及评估广告投放效果的依据。

因此,一个可靠、准确的监测服务非常重要。为了更好地保障平台和广告主双方的权益,以及为提升马蜂窝旅游网的广告服务效果提供支撑,我们也在不断地探索适合的解决方案,加强广告监测服务的能力。

 

Part.1 初期形态

初期我们的广告监测并没有形成完整的服务对外开放,因此实现方式及提供的能力也比较简单,主要分为两部分:一是基于客户端打点,针对事件进行上报;另一部分是针对曝光、点击链接做转码存档,当请求到来后解析跳转。

但是很快,这种方式的弊端就暴露出来,主要体现在以下几个方面:

  • 收数的准确性:数据转发需要访问中间件才能完成,增加了多段丢包的机率。在和第三方监测服务进行对比验证时,Gap 差异较大;

  • 数据的处理能力:收集的数据来自于各个业务系统,缺乏统一的数据标准,数据的多种属性导致解析起来很复杂,增加了综合数据二次利用的难度;

  • 突发流量:当流量瞬时升高,就会遇到 Redis 内存消耗高、服务掉线频繁的问题;

  • 部署复杂:随着不同设备、不同广告位的变更,打点趋于复杂,甚至可能会覆盖不到;

  • 开发效率:初期的广告监测功能单一,例如对实时性条件的计算查询等都需要额外开发,非常影响效率。

 

Part.2 基于 OpenResty 的架构实现

在这样的背景下,我们打造了马蜂窝广告数据监测平台 ADMonitor,希望逐步将其实现成一个稳定、可靠、高可用的广告监测服务。

2.1 设计思路

为了解决老系统中的各种问题,我们引入了新的监测流程。主体流程设计为:

  1. 在新的监测服务 (ADMonitor) 上生成关于每种广告独有的监测链接,同时附在原有的客户链接上;

  2. 所有从服务端下发的曝光链接和点击链接并行依赖 ADMonitor 提供的服务;

  3. 客户端针对曝光行为进行并行请求,点击行为会优先跳转到 ADMonitor,由 ADMonitor 来做二段跳转。

OpenResty 在马蜂窝广告监测中的应用_第1张图片

通过以上方式,使监测服务完全依赖 ADMonitor,极大地增加了监测部署的灵活性及整体服务的性能;同时为了进一步验证数据的准确性,我们保留了打点的方式进行对比。

2.2 技术选型

为了使上述流程落地,广告监测的流量入口必须要具备高可用、高并发的能力,尽量减少非必要的网络请求。考虑到内部多个系统都需要流量,为了降低系统对接的人力成本,以及避免由于系统迭代对线上服务造成干扰,我们首先要做的就是把流量网关独立出来。

关于 C10K 编程相关的技术业内有很多解决方案,比如 OpenResty、JavaNetty、Golang、NodeJS 等。它们共同的特点是使用一个进程或线程可以同时处理多个请求,基于线程池、基于多协程、基于事件驱动+回调、实现 I/O 非阻塞。

我们最终选择基于 OpenResty 构建广告监测平台,主要是对以下方面的考虑:

第一,OpenResty 工作在网络的 7 层之上,依托于比 HAProxy 更为强大和灵活的正则规则,可以针对 HTTP 应用的域名、目录结构做一些分流、转发的策略,既能做负载又能做反向代理;

第二,OpenResty 具有 Lua协程+Nginx 事件驱动的「事件循环回调机制」,也就是 Openresty 的核心 Cosoket,对远程后端诸如 MySQL、Memcached、Redis 等都可以实现同步写代码的方式实现非阻塞 I/O;

第三,依托于 LuaJit,即时编译器会将频繁执行的代码编译成机器码缓存起来,当下次调用时将直接执行机器码,相比原生逐条执行虚拟机指令效率更高,而对于那些只执行一次的代码仍然可以逐条执行。

2.3 架构实现

整体方案依托于 OpenResty 的处理机制,在服务器内部进行定制开发,主要划分为数据收集、数据处理与数据归档三大部分,实现异步拆分请求与 I/O 通信。整体结构示意图如下:

OpenResty 在马蜂窝广告监测中的应用_第2张图片

我们将多 Woker 日志信息以双端队列的方式存入 Master 共享内存,开启 Worker 的 Timer 毫秒级定时器,离线解析流量。

2.3.1 数据收集 

收集部分也是主体承受流量压力最大的部分。我们使用 Lua 来做整体检参、过滤与推送。由于在我们的场景中,数据收集部分不需要考虑时序或对数据进行聚合处理,因此核心的推送介质选择 Lua 共享内存即可,以 I/O 请求来代替访问其他中间件所需要的网络服务,从而减少网络请求,满足即时性的要求,如下所示:

OpenResty 在马蜂窝广告监测中的应用_第3张图片

下面结合 OpenResty 配置,介绍一些我们对服务器节点进行的优化:

  1. 设置 lua 缓存-lua_code_cache:

    (1)开启后会将 Lua 文件缓存到内存中,加速访问,但修改 Lua 代码需要 reload

    (2)尽量避免全局变量的产生

    (3)关闭后会依赖 Woker 进程中生成自己新的 LVM

  2. 设置 Resolver 对于网络请求、好的 DNS 节点或者自建的 DNS 节点在网络请求很高的情况下会很有帮助:

    (1)增加公司的 DNS 服务节点与补偿的公网节点

    (2)使用 shared 来减少 Worker 查询次数

  3. 设置 epoll (multi_accept/accept_mutex/worker_connections):

    (1)设置 I/O 模型、防止惊群

    (2)避免服务节点浪费资源做无用处理而影响整体流转等

  4. 设置 keepalive:

    (1)包含链接时长与请求上限等

配置优化一方面是要符合当前请求场景,另一方面要配合 Lua 发挥更好的性能。设置 Nginx 服务器参数基础是根据不同操作系统环境进行调优,比如 Linux 中一切皆文件、调整文件打开数、设置 TCP Buckets、设置 TIME_WAIT  等。

2.3.2 数据处理

这部分流程是将收集到的数据先通过 ETL,之后创建内部的日志 location,结合 Lua 自定义 log_format,利用 Nginx 子请求特性离线完成数据落盘,同时保证数据延迟时长在毫秒级。

对被解析的数据处理要进行两部分工作,一部分是 ETL,另一部分是 Count。

(1)ETL

OpenResty 在马蜂窝广告监测中的应用_第4张图片

主要流程:     

  1. 日志经过统一格式化之后,抽取包含实际意义参数部分进行数据解析

  2. 将抽取后的数据进行过滤,针对整体字符集、IP、设备、UA、相关标签信息等进行处理

  3. 将转化后的数据进行重加载与日志重定向


  4. 9

你可能感兴趣的:(OpenResty 在马蜂窝广告监测中的应用)