實現埋點上報方案過程當中的思考

任何一個重視數據的公司與部門,都不會忽略前端埋點數據上報的做用。javascript

從直觀上看,前端的埋點上報方案有啥難的呀?不就是將用戶行爲數據收集上來,就能夠了麼?前端

從實際開發經驗來回答:實際上並無那麼簡單。爲了可以將收集的用戶數據最終展現並從中分析得出有意義的數據,這其中涉及的細節比想象的要多。java

在本文中,咱們會從兩個方面來思考數據埋點上報的過程:數據收集與數據上報。ios

數據收集

對於數據收集的過程,咱們不會對數據上報方式作區分(如常見的埋點上報與無埋點上報),而重點思考無論哪一種上報方式都通用的技術點。重點考察以下基本的數據信息:UV、PV、應用瀏覽總時長。web

UV

針對UV的統計維度有兩個:註冊用戶與未註冊用戶。小程序

註冊用戶好說,只須要有應用中註冊用戶的惟一標識符便可。跨域

對於未註冊用戶,如何識別這是一個用戶呢?在這種狀況下,問題會轉化爲:如何識別一個設備?瀏覽器

如何識別一個設備?

最容易想到的就是利用瀏覽器的存儲功能:cookie、localStorage等。具體作法是:在用戶首次加載頁面的時候,將UUID(如隨機生成的字符串)放入到存儲中,這種方案是最簡單的,其中:bash

Cookie 有生命時長限制而且有容量大小的限制。服務器

localStorage 會永久保存,而且在容量方面比cookie大得多。

因此,使用localStorage就會比cookie更有效。可是,基於瀏覽器存儲功能的方案,有一個天生的缺陷:用戶能夠選擇主動刪除存儲

這個時候,開發者就會想:是否可以有一個真正的與設備相關的惟一標識符呢?

因而有了瀏覽器指紋追蹤技術:相似人的外貌和指紋,Web客戶端(這裏主要指瀏覽器)也有多種「外貌」信息和「指紋」信息,將這些信息綜合分析計算後,可對客戶端進行惟一性識別,進而鎖定、追蹤、瞭解網民行爲和隱私數據。

在 WA 中,引用了公司內部的另外一個基於瀏覽器指紋追蹤庫做爲識別同一個瀏覽器訪問的惟一標識符。通過諮詢該庫開發者 ,發現瀏覽器指紋技術在實際應用中的效果並很差(沒法識別的機率很高):

前提一:   只針對瀏覽器,換了瀏覽器即便同設備,id也不同。
前提二:   用ip區分用戶,不一樣ip認爲是不同的用戶。

統計結果: 18年咱們在生產環境作了測試,ios 7800個用戶中,有22%的用戶id未發生重複id。安卓15000個用戶中,有39%未發生重複id。

緣由分析: 由於瀏覽器拿不到設備底層數據,只能經過一些瀏覽器的配置參數去生成id。若是用戶手機系統 型號 瀏覽器版本和相關配置同樣,那生成的就同樣。
複製代碼
結論

通過綜合考慮,咱們放棄了瀏覽器指紋追中技術。在咱們設計的埋點方案中,非註冊用戶的UV統計方式採用的是植入UUID到localStorage的方案。

PV

當頁面發生改變時,咱們就會在PV計數上增長1。對於此,咱們想問:如何知道頁面變化了?

這個問題,能夠細分爲兩個小問題:

  1. 瀏覽器本身是否能夠徹底知道頁面發生了變化?
  2. 開發者本身是否能夠徹底知道頁面發生了變化?

第二個問題,答案是確定的。開發者固然瞭解頁面啥時候發生了變化。可是第一個問題,考慮瀏覽器是否能夠知道頁面發生了變化的時候,有趣的點就來了:

瀏覽器認爲的頁面變化是否就是咱們所須要的頁面變化呢?

對於多頁面應用,經過偵聽 window.onLoad 事件與 window.onunload 事件,或者偵聽URL的變化 onhashchange 便可。瀏覽器認爲的頁面變化就是咱們所須要的頁面變化。

對於單頁面應用呢?這可就不必定了。

由於是單頁面應用,那麼只會有一次頁面加載事件的觸發。也就是說,沒法經過偵聽 window.onLoad 事件與 window.onunload 事件來實現。

那麼,經過偵聽URL的變化可行麼?若應用中存在多層嵌套路由,當URL變化了,不必定表明着頁面發生了變化。

這個時候怎麼辦呢?咱們來看看大廠們的作法:

Google Analytics:

ga網頁瀏覽說明

GrowingIO:

企業微信截圖_13b09295-1318-4cc0-91c6-1b53ea0e289d

易觀方舟:

企業微信截圖_52c8297c-f8e4-457c-9933-95ab1e806215

針對單頁面應用時的PV統計,基本都提供了這樣的方式:

1.配置信息:是否啓動基於URL變化的自動頁面收集方式。

2.手動上報:提供接口主動上報頁面變化。

因此,在咱們的實現中,也基於這樣的方案來實現。

應用瀏覽總時長

在應用瀏覽時長的統計階段,關鍵的細節在於如何準確瞭解應用被關閉了。咱們來看正常狀況和非正常狀況。

正常:在正常應用關閉的操做中,咱們能夠偵聽 window.onunload 事件便可。

非正常:那麼非正常狀況呢?好比這些:

  • 瀏覽器閃退

  • 用戶很長時間未操做的狀況下(好比看視頻),瀏覽器忽然被閃退

  • H5 頁面/小程序的客戶端閃退/被殺死

這些異常狀況下,該如何處理呢?

一種推薦的方案是:WebSocket。前端SDK與後臺創建一個webSocket持久性鏈接。webSocket鏈接中的心跳檢測是必備選項,用於應對網絡不穩定。當應用被關閉的時候,服務器端能夠知道這個關閉事件。缺點:WebSocket方案有必定的兼容性問題。

(ps: webSocket的更多細節原理並未親自實現)

在咱們的應用中,是基於 WA 上報數據。它並無這樣一個webSocket的持久性鏈接提供出來。該怎麼辦呢?

現階段是這麼作的:

思考:在客戶端被殺死或者崩潰退出的狀況下,web 頁面自己並無合適的事件可以精確監聽。

那麼,採用近似的思路來趨近:在現有的方案中經過設置一個合適時長的按期上報事件(定時心跳上報),經過獲取本次會話的最後上報時刻,可近似理解爲是用戶關閉頁面(最大偏差爲所設置的心跳上報間隔)。

對於這種方案的實現,須要數據統計端的配合:在計算應用總時長的時候,需考慮心跳上報因素。

數據上報

對於一個埋點方案來講,數據上報有兩個點須要着重考慮:

  • 對跨域作特殊處理。
  • 頁面銷燬後,如何還可以將未上傳的埋點數據成功上報?

傳統的XHR發送數據請求的方式,對上面提到的兩個點都無能爲力。在數據上報過程當中,較爲經常使用的有兩種方式:

  • 動態建立img標籤,在 img.src 中拼接url的方式發送請求
  • 調用 sendBeacon 接口發送數據

Major tracking upgrade - sendBeacon(), etc! - Clicky Blog

Img 標籤

利用img標籤的src屬性發送請求的方式,具體執行方案以下:

let _img = new Image();
_img.src = `${url}?${util.spliceParam(params)}`;
_img.onload = _img.onerror = function() {}
複製代碼

它很是契合埋點數據上報這個應用場景:

① 只上報的數據不須要接收響應;

② img的src屬性自然地不存在跨域問題。

這種使用方式也存在缺陷。首先對於src 中的URL內容是有大小限制的,太大的數據量不適用。詳細看這裏。其次,在頁面卸載的時候,若存在數據未發送的狀況,會先將對應的數據發送完,再執行頁面卸載。這種狀況下,會在體驗上給使用者帶來不方便。

此時的 sendBeacon方法就是來解決上面提到的缺陷的。

sendBeacon

sendBeacon方法是一個異步、非阻塞的數據傳輸方法。具體使用方式以下:

window.navigator.sendBeacon && window.navigator.sendBeacon(url, params)
複製代碼

它的特色是:

  • Beacon請求是Post方式。
  • Beacon請求優先避免與關鍵操做和更高優先級的網絡請求競爭。
  • Beacon請求能夠有效地合併,以優化移動設備上的能量使用。
  • Beacon保證頁面卸載以前啓動信標請求,並容許運行完成且不會阻塞請求或阻塞處理用戶交互事件的任務。
  • 返回值:sendBeacon 方法被執行後返回一個布爾值,true表明用戶代理成功地將信標請求加入到隊列中,不然返回false

對於sendBeacon方法,它的侷限性體如今:

  • 不能跨域,須要服務端設置。
  • 新特性接口,兼容性存在問題。

所以,在實際的應用過程當中,須要針對實際狀況,結合 Img 標籤 與 sendBeacon 的方式一塊兒使用。

總結

在本文中,咱們分別討論了在埋點上報階段的兩個方面:數據收集與數據上報。這其中,對於數據收集過程當中的一些值得留意的狀況,咱們作了分析。同時,與你分享了兩種經常使用的數據上報方式。但願對你有用!

相關文章
相關標籤/搜索