OpenResty在马蜂窝广告监控中的应用以马蜂窝旅游App为例,当用户打开我们的app时,可能会在首屏或信息流、商品列表中看到推送的广告。如果恰好对广告的内容感兴趣,用户可能会点击广告了解更多信息,然后完成广告希望完成的后续动作,比如下载广告推荐的应用。广告监测平台的任务是持续准确地收集用户在浏览和点击广告事件中携带的信息,包括来源、时间、设备、位置信息等,并进行处理和分析,为广告主提供支付结算和评价广告效果的依据。因此,可靠、准确的监测服务非常重要。为了更好地保护平台和广告主双方的权益,为提升马蜂窝旅游网的广告服务效果提供支持,我们也在不断探索合适的解决方案,加强广告监测服务能力。Part.1初步形态在早期,我们的广告监测并没有形成一个完整的对外开放的服务,所以实现方式和提供的能力都比较简单。主要分为两部分:一是基于客户端管理,上报事件;另一部分用于转码存档用于曝光和点击链接,当请求到达时,解析并跳转。但很快,这种方式的弊端暴露出来,主要体现在以下几个方面:数据采集的准确性:数据转发需要接入中间件来完成,增加了多段丢包的概率。在与第三方监控服务进行对比验证时,Gap差异较大;数据处理能力:采集的数据来自各个业务系统,缺乏统一的数据标准,数据的多重属性使得分析非常复杂,增加了数据二次利用的综合难度;突发流量:当流量瞬间增大时,会遇到Redis内存消耗大,服务频繁断线的问题;部署复杂:随着不同设备、不同广告位的变化,管理趋于复杂,甚至可能无法覆盖;开发效率:初期广告监测功能单一,如实时情况的计算查询等,需要额外开发,极大影响效率。Part.2基于OpenResty架构的实现在此背景下,我们创建了马蜂窝广告数据监控平台ADMonitor,希望能逐步将其实现为稳定、可靠、高可用的广告监控服务。2.1设计思路为了解决老系统存在的各种问题,我们引入了新的监控流程。主要流程设计为:在新的监控服务(ADMonitor)上为每个广告生成一个唯一的监控链接,同时附加到原来的客户链接上;所有服务器下发的曝光链接和点击链接都依赖ADMonitor提供并行服务;客户端对曝光行为进行并行请求,点击行为会先跳转到ADMonitor,ADMonitor再做二次跳转。通过以上方式,监控服务完全依赖于ADMonitor,大大增加了监控部署的灵活性和整体服务的性能;同时,为了进一步验证数据的准确性,我们保留管理方式进行对比。2.2技术选择为了实现上述过程,广告监测的流量入口必须具备高可用和高并发的能力,尽量减少不必要的网络请求。考虑到内部多个系统都需要流量,为了降低系统互联的人力成本,避免系统迭代对在线业务造成干扰,我们首先要做的就是分离流量网关。C10K编程相关技术在业界有很多解决方案,如OpenResty、JavaNetty、Golang、NodeJS等,它们的共同特点是使用一个进程或线程可以同时处理多个请求,基于线程池,基于多协程,基于事件驱动+回调,实现I/O非阻塞。我们最终选择基于OpenResty搭建广告监控平台,主要是出于以下考虑:首先,OpenResty工作在网络的第7层,依赖于比HAProxy更强大和灵活的正则规则,可以针对域名HTTP应用的实现1.目录结构做了一些分发和转发策略,既可以作为负载也可以作为反向代理;第二,OpenResty有一个Lua协程+Nginx事件驱动的“事件循环回调机制”,这是Openresty的核心Cosoket,对于MySQL、Memcached、Redis等远程后端可以通过同步编写代码实现非阻塞I/O;第三,实时编译器依托LuaJit,将频繁执行的代码编译成机器码并缓存起来。调用的时候会直接执行机器码,比原生的虚拟机指令一条一条执行效率更高,而且那些只执行一次的代码还是可以一条一条执行的。2.3架构实现整体方案依托于OpenResty的处理机制,在服务器内部定制开发。主要分为数据采集、数据处理和数据归档三个部分,实现请求的异步拆分和I/O通信。整体结构示意图如下:我们将多个Woker的日志信息以双端队列的形式存储在Master的共享内存中,并启用Worker的Timer的毫秒级定时器来分析流量离线。2.3.1数据采集采集部分也是被测者承受流量压力最大的部分。我们使用Lua进行整体参数检查、过滤和推送。在我们的场景中,数据采集部分不需要考虑时序和聚合数据,所以核心推送介质可以选择Lua共享内存,用I/O请求来代替访问其他中间件所需的网络服务。为了减少网络请求,满足即时性的要求,如下图:结合OpenResty的配置,我们对服务器节点进行一些优化:设置lua缓存-lua_code_cache:(1)打开后会缓存Lua文件在内存中,Accelerateaccess,但是修改Lua代码需要reload(2)尽量避免全局变量的生成(3)关闭后会依赖Woker进程生成自己新的LVM设置Resolver,用于网络请求,goodDNS节点或者自建DNS节点在网络请求高的时候会有很大的帮助:(1)增加公司的DNS服务节点和有偿的公网节点(2)使用shared减少Worker查询次数设置epoll(multi_accept/accept_mutex/worker_connections):(1)设置I/O模型,防止拥挤(2)避免服务节点浪费资源做无用处理影响整体流量等设置keepalive:(1)配置优化包括linkduration和requestupperlimit,一方面要符合当前的请求场景,另一方面需要配合Lua才能达到更好的性能。Nginx服务器参数设置的基础是根据不同的操作系统环境进行调优,比如Linux下一切皆文件、调整打开文件数、设置TCPBuckets、设置TIME_WAIT等。2.3.2数据处理这部分流程是先将采集到的数据通过ETL传递,然后创建内部日志位置,结合Lua自定义的log_format,利用Nginx的分请求特性完成数据离线,同时保证数据延迟在毫秒级。解析后的数据处理分为两部分,一部分是ETL,另一部分是Count。(1)ETL的主要流程:日志统一格式化后,提取包含实际意义参数的部分进行数据分析,对提取的数据进行过滤,得到整体的字符集、IP、设备、UA、相关标签信息等进行处理和转换最终数据重新加载和日志重定向【示例】Lua使用FFI解析“ip!”通过IP库。使用C将IP库拷贝到内存中,Lua进行毫秒级查询:(2)Count针对广告数据,大部分业务需求来自于数据统计。这里直接使用Redis+FluxDB存储数据,关键技术点有几个:RDS结合Lua设置连接时间,配置连接池增加连接复用RDS集群服务实现中心化,分散节点压力,增加AOF和延时入库保证可靠性FluxDB保证数据日志可以顺序检查,聚合统计和实时报表表现更好2.3.3数据归档数据归档需要全量数据入表,会涉及过滤一些无效数据。公司的大数据系统在这里被整合为一个整体,流程分为线上处理和线下处理两部分,数据可追溯。使用的解决方案是线上的Flink和离线的Hive,需要注意:ES索引和数据定期维护Kafka消费对于出现故障的机器,使用脚本自动重启和告警等实时数据源:数据采集服务→Filebeat→Kafka→Flink→ES离线数据源:HDFS→Spark→Hive→ES解析后的数据复用:解析后的数据已经具有复用的价值。我们主要的应用场景有两大块。第一个是OLAP,根据业务场景和数据表现,分析访问广告的人的属性和标签的变化,包括地域、设备、人口分布和增长等;同时,预测未来人口库存的比例,最终影响实际交付优劣。另一部分在OLTP中。主要场景有:判断用户是否属于广告受众区域,分析UA信息,获取终端信息,判断是否属于标记低级爬虫流量设备号,从Redis获取实时用户画像,执行实时标记等2.4OpenResty其他应用场景OpenResty在我们广告数据监控服务的整个过程中扮演着重要的角色:init_worker_by_lua阶段:负责服务配置业务access_by_lua阶段:负责CC保护,权限访问,流量时序监控等服务content_by_lua阶段:负责实现限速路由器、分离器、WebAPI、流量采集等服务的log_by_lua阶段:负责日志放置等服务。着重讲解下面两个应用的实现方法。2.4.1Splitter业务NodeJS服务向OpenResty网关上报当前服务器CPU和内存使用情况;Lua脚本调用RedisCluster获取时间窗口内的NodeJS集群使用情况,计算负载高的NodeJS机器;OpenResty融合了NodeJS集群的流量、降级、限流等逻辑处理;监控数据与InfluxDB同步,进行定时监控。2.4.2小WEB防火墙使用第三方开源lua_resty_waf类库实现,支持IP白名单和黑名单、URL白名单、UA过滤、CC攻击防护等功能。在此基础上,我们增加了对InfluxDB的WAF支持,用于时序监控和服务预警。2.5小结综上所述,基于OpenResty的广告监控服务ADMonitor具有以下特点:高可用:依赖OpenResty作为Gateway,多节点作为HA,立即返回:解析数据后,使用I/O请求异步数据处理,避免不必要的网络通信解耦功能模块:将请求、数据处理和转发解耦,减少单个请求串行处理的耗时服务保障:使用第三方组件将重要数据结果单独存储完整技术方案如下图:Part.3总结目前,ADMonitor已经接入公司的广告服务系统,整体运行情况比较理想:1、表现效果达到了高吞吐量、低延迟的标准。转发成功率高,曝光统计成功率>99.9%,点击成功率>99.8%2。业务效果与主流第三方监测机构数据对比:曝光数据GAP<1%,点击数据GAP<3%,可提供实时检索聚合服务。未来,我们将结合业务发展和服务场景不断完善,期待与您多多交流。本文作者:蒋明辉,马蜂窝旅游网品牌广告数据服务器组研发工程师。
