OpenResty 在馬蜂窩廣告監測中的應用

来源:https://www.cnblogs.com/mfwtech/archive/2019/12/27/12106020.html
-Advertisement-
Play Games

馬蜂窩技術原創內容,更多乾貨請訂閱公眾號:mfwtech 廣告是互聯網變現的重要手段之一。 以馬蜂窩旅游 App 為例,當用戶打開我們的應用時,有可能會在首屏或是信息流、商品列表中看到推送的廣告。如果剛好對廣告內容感興趣,用戶就可能會點擊廣告瞭解更多信息,進而完成這條廣告希望完成的後續操作,如下載廣 ...


馬蜂窩技術原創內容,更多乾貨請訂閱公眾號:mfwtech

廣告是互聯網變現的重要手段之一。

以馬蜂窩旅游 App 為例,當用戶打開我們的應用時,有可能會在首屏或是信息流、商品列表中看到推送的廣告。如果剛好對廣告內容感興趣,用戶就可能會點擊廣告瞭解更多信息,進而完成這條廣告希望完成的後續操作,如下載廣告推薦的 App 等。

廣告監測平臺的任務就是持續、準確地收集用戶在瀏覽和點擊廣告這些事件中攜帶的信息,包括來源、時間、設備、位置信息等,併進行處理和分析,來為廣告主提供付費結算以及評估廣告投放效果的依據。

因此,一個可靠、準確的監測服務非常重要。為了更好地保障平臺和廣告主雙方的權益,以及為提升馬蜂窩旅游網的廣告服務效果提供支撐,我們也在不斷地探索適合的解決方案,加強廣告監測服務的能力。

 

Part.1 初期形態

初期我們的廣告監測並沒有形成完整的服務對外開放,因此實現方式及提供的能力也比較簡單,主要分為兩部分:一是基於客戶端打點,針對事件進行上報;另一部分是針對曝光、點擊鏈接做轉碼存檔,當請求到來後解析跳轉。

但是很快,這種方式的弊端就暴露出來,主要體現在以下幾個方面:

  • 收數的準確性:數據轉發需要訪問中間件才能完成,增加了多段丟包的機率。在和第三方監測服務進行對比驗證時,Gap 差異較大;

  • 數據的處理能力:收集的數據來自於各個業務系統,缺乏統一的數據標準,數據的多種屬性導致解析起來很複雜,增加了綜合數據二次利用的難度;

  • 突發流量:當流量瞬時升高,就會遇到 Redis 記憶體消耗高、服務掉線頻繁的問題;

  • 部署複雜:隨著不同設備、不同廣告位的變更,打點趨於複雜,甚至可能會覆蓋不到;

  • 開發效率:初期的廣告監測功能單一,例如對實時性條件的計算查詢等都需要額外開發,非常影響效率。

 

Part.2 基於 OpenResty 的架構實現

在這樣的背景下,我們打造了馬蜂窩廣告數據監測平臺 ADMonitor,希望逐步將其實現成一個穩定、可靠、高可用的廣告監測服務。

2.1 設計思路

為瞭解決老系統中的各種問題,我們引入了新的監測流程。主體流程設計為:

  1. 在新的監測服務 (ADMonitor) 上生成關於每種廣告獨有的監測鏈接,同時附在原有的客戶鏈接上;

  2. 所有從服務端下發的曝光鏈接和點擊鏈接並行依賴 ADMonitor 提供的服務;

  3. 客戶端針對曝光行為進行並行請求,點擊行為會優先跳轉到 ADMonitor,由 ADMonitor 來做二段跳轉。

通過以上方式,使監測服務完全依賴 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 通信。整體結構示意圖如下:

我們將多 Woker 日誌信息以雙端隊列的方式存入 Master 共用記憶體,開啟 Worker 的 Timer 毫秒級定時器,離線解析流量。

2.3.1 數據收集 

收集部分也是主體承受流量壓力最大的部分。我們使用 Lua 來做整體檢參、過濾與推送。由於在我們的場景中,數據收集部分不需要考慮時序或對數據進行聚合處理,因此核心的推送介質選擇 Lua 共用記憶體即可,以 I/O 請求來代替訪問其他中間件所需要的網路服務,從而減少網路請求,滿足即時性的要求,如下所示:

下麵結合 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

主要流程:     

  1. 日誌經過統一格式化之後,抽取包含實際意義參數部分進行數據解析

  2. 將抽取後的數據進行過濾,針對整體字元集、IP、設備、UA、相關標簽信息等進行處理

  3. 將轉化後的數據進行重載入與日誌重定向

【例】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.4 OpenResty 其他應用場景

OpenResty 在我們的廣告數據監測服務全流程中均發揮著重要作用:

  • init_worker_by_lua階段:負責服務配置業務

  • access_by_lua階段:負責CC防護、許可權準入、流量時序監控等業務

  • content_by_lua階段:負責實現限速器、分流器、WebAPI、流量採集等業務

  • log_by_lua階段:負責日誌落盤等業務

重點解讀以下兩個應用的實現方式。

2.4.1  分流器業務

NodeJS 服務向 OpenResty 網關上報當前伺服器 CPU 和記憶體使用情況;Lua 腳本調用 RedisCluster 獲取時間視窗內 NodeJS 集群使用情況後,計算出負載較高的 NodeJS 機器;OpenResty 對 NodeJS 集群流量進行熔斷、降級、限流等邏輯處理;將監控數據同步 InfluxDB,進行時序監測。

2.4.2 小型 WEB 防火牆

使用第三方開源 lua_resty_waf 類庫實現,支持 IP 白名單和黑名單、URL 白名單、UA 過濾、CC 攻擊防護功能。我們在此基礎上增加了 WAF 對 InfluxDB 的支持,進行時序監控和服務預警。

2.5 小結

總結來看,基於 OpenResty 實現的廣告監測服務 ADMonitor 具備以下特點:

  • 高可用:依賴 OpenResty 做 Gateway, 多節點做 HA

  • 立即返回:解析數據後利用 I/O 請求做數據非同步處理,避免非必要的網路通信

  • 解耦功能模塊:對請求、數據處理和轉發實現解耦,縮減單請求串列處理耗時

  • 服務保障:  針對重要的數據結果利用第三方組件單獨存儲

完整的技術方案示意如下:

 

Part.3 總結

目前,ADMonitor 已經接入公司的廣告服務體系,總體運行情況比較理想:

1. 性能效果

  • 達到了高吞吐、低延遲的標準

  • 轉發成功率高,曝光計數成功率>99.9%,點擊成功率>99.8%

2. 業務效果

  • 與主流第三方監測機構進行數據對比:曝光數據 GAP < 1%,點擊數據GAP < 3%

  • 可提供實時檢索與聚合服務

未來我們將結合業務發展和服務場景不斷完善,期待和大家多多交流。

本文作者:江明輝,馬蜂窩旅游網品牌廣告數據服務端組研發工程師。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JS高級 三種創建對象的方式 字面量的方式 (實例對象) 調用系統的構造函數 自定義構造函數方式 //創建對象 >實例化一個對象,的同時對屬性進行初始化 var per=new Person("小紅",20); 自動逸構造函數創建對象做的事情: 1.開闢空間存儲對象 2.把this設置為當前的對象 ...
  • 前言 關於Hook的定義官方文檔是這麼說的: Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性。 簡單來說,就是在使用函數式組件時能用上state,還有一些生命周期函數等其他的特性。 如果想瞭解Hook怎麼用, " ...
  • HTTP 400 錯誤 復現錯誤 ajax請求後臺數據時有時會報 HTTP 400 錯誤 - 請求無效 (Bad request);出現這個請求無效報錯說明請求沒有進入到後臺服務里;原因:1)前端提交數據的欄位名稱或者是欄位類型和後臺的實體類不一致,導致無法封裝; 2)前端提交的到後臺的數據應該是j ...
  • HTML DOM 允許 JavaScript 更改 HTML 元素的樣式。 改變 HTML 樣式 如需更改 HTML 元素的樣式,請使用此語法: document.getElementById(id).style.property = new style 下麵的例子更改了 <p> 元素的樣式: 實例 ...
  • 轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。 原文出處:https://blog.bitsrc.io/do-your-buttons-lead-or-mislead-your-users-d5d83531238b 按鈕是UI/UX最關鍵的組件之一,在不同 ...
  • ​API網關我的分析中會用到以下三種場景:1、Open API,2、微服務網關,3、API服務管理平臺 ...
  • 1.代碼生成器: [正反雙向](單表、主表、明細表、樹形表,快速開發利器)freemaker模版技術 ,0個代碼不用寫,生成完整的一個模塊,帶頁面、建表sql腳本、處理類、service等完整模塊2.多數據源:(支持同時連接無數個資料庫,可以不同的模塊連接不同數的據庫)支持N個數據源3.阿裡資料庫連 ...
  • 以Java為例: 餓漢: 懶漢: 先來看單例模式原理及要求,保證這個類在記憶體中只有一個對象,那麼就不能隨便給別人new,所以必須把構造函數改為private,然後整一個公共靜態方法供外部統一獲取實例。 再來看餓漢以及懶漢定義(原理)以及區別: 餓漢:一開始就吧吃的找好(對象new出來),隨時可以吃 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...