概念:http://www.aliued.cn/2010/09/13/ab-testing-basic-concept.htmlhtml
咱們先來看一個圖:前端
(注:感謝Algo提供本圖。)apache
上圖展現了 A/B 測試的實現原理。從左到右,四條較粗的豎線表明了 A/B 測試中的四個關鍵角色:客戶端(Client)、服務器(Server)、數據層(Data)、數據倉庫(Data Warehouse)。從上到下表明瞭三種訪問形式:無 A/B 測試的普通訪問流程(Non AB test)、基於後端的 A/B 測試訪問流程(Back-end AB test)、基於前端的 A/B 測試訪問流程(Front-end AB test)。後端
通常狀況下,用戶在一次瀏覽中,會從客戶端(Client)發起一個請求,這個請求被傳到了服務器(Server),服務器的後臺程序根據計算,得出要給用戶返回什麼內容(Data),同時向數據倉庫(Data Warehouse)添加一條打點信息,記錄本次訪問的相關信息。這個過程也就是圖上橫向的流程。數據倉庫收集到足夠的數據以後,就能夠開始進行分析(Analytics)了,這也便是圖中右上角的部分。瀏覽器
A/B 測試須要將多個不一樣的版本展示給不一樣的用戶,即須要一個「分流」的環節。從上圖中咱們能夠看到,分流能夠在客戶端作,也能夠在服務器端作。傳統的 A/B 測試通常是在服務端分流的,即基於後端的 A/B 測試(Back-end AB test),當用戶的請求到達服務器時,服務器根據必定的規則,給不一樣的用戶返回不一樣的版本,同時記錄數據的工做也在服務端完成。安全
基於後端的 A/B 測試技術實現上稍微簡單一些,不過缺點是須要技術部工程資源介入,另外收集到的數據一般是比較宏觀的PV(Page View)信息,雖然能夠進行比較複雜的宏觀行爲分析,但要想知道用戶在某個版本的頁面上的具體行爲每每就無能爲力了。服務器
基於前端的 A/B 測試則能夠解決上面的問題。它的特色是,利用前端 JavaScript 方法,在客戶端進行分流,同時,能夠用 JavaScript 記錄下用戶的鼠標行爲(甚至鍵盤行爲,若是須要的話),直接發送到對應的打點服務器記錄。這樣的好處是不須要技術部(若是大家和咱們同樣,前端工程師與後端工程師分屬不一樣部門的話)參與,而且能夠比較精確地記錄下用戶在頁面上的每個行爲,甚至包括後端方法難以記錄到的無效點擊!前端工程師
下面,我將重點介紹一下咱們在基於前端的 A/B 測試上的一些實踐。性能
1、分流測試
首先遇到的問題是如何分流的問題。對於大部分需求來講,咱們但願各個版本的訪問人數平均分配。解決辦法有不少種,比較簡單的一種便是前面提到過的,根據某一個 Cookie ID 來劃分用戶,前提是你的網站上每一位訪客在第一次訪問時就要有一個不重複的 Cookie ID,好比「123.180.140.*.1267882109577.3」。而後,能夠根據這個 Cookie ID 的最後一位(在本例中是「3」)來劃分人羣,好比單數的顯示 A 版本,偶數的顯示 B 版本。
由於 Cookie ID 通常設定後不會輕易改變,基於 Cookie ID 的好處是咱們能很好地對訪客保持一致性,某個用戶若是第一次看到的是 A 版本,那他刷新後看到的仍是 A 版本,不會一下子看到 A 版本一下子看到 B 版本。但不足之處就是若是用戶瀏覽器不支持 Cookie 的話,分流就不能正常進行了。不過,現代瀏覽器默認狀況下都是支持 Cookie 的,若是真有用戶的瀏覽器不支持 Cookie ,那也應該是極少數特殊狀況,對結果的影響很是微小,對於這些特殊狀況,咱們通常能夠安全地忽略掉。
還有一點須要注意的是,A/B 測試的頁面必須有較高的 UV (Unique Visitor,獨立訪客數),由於分流帶有必定的隨機性,若是頁面 UV 過小,分到每個版本的人數就更少,結果頗有可能被一些偶然因素影響。而 UV 較大時,根據大數定理,咱們獲得的結果會接近於真實數據。就像想知道一個地方的成年人的平均身高,固然是取的樣本越大結論越可信。
2、展現
決定向當前訪問者顯示哪一個版本後,怎麼用前端的方法加載對應的版本呢?這須要分狀況處理。
通常狀況下,若是兩個版本只有一個較小的區域不同,咱們能夠同時將兩個區域的 HTML 都加載到當前頁面中,先用 CSS 把它們隱藏起來(也能夠默認顯示一個版本),等 JS 判斷出該顯示哪一個版本後,再控制對應版本的 CSS 顯示。
有時候,測試區域比較大,代碼比較多,或者須要後臺較多的計算資源,若是一開始就把兩個版本的 HTML 全加載到當前頁面中,就會須要比較大的開銷(好比帶寬、後臺計算量)。這種狀況下,咱們能夠先把測試區留空,以後再用 Ajax 的方式延遲加載。
還有的時候,測試區域很是大,幾乎佔了整個頁面,或者徹底就是不一樣的頁面,這時,用 Ajax 方式加載也不適合了,能夠將不一樣的版本作成不一樣的頁面,而後再用 JS 跳轉。不過這樣的方式並非很好,由於前端 JS 跳轉須要必定的時間,這個過程頗有可能被用戶感覺到,而且留下很差的體驗。對這個問題,彷佛沒有很好的解決辦法,至少在前端層面很難完美解決,因此並非很是推薦這種跳轉方式,若是真的須要跳轉,最好是在服務器端由後端代碼來操做。
3、數據採集
正確展現對應的版本後,就要開始採集須要的數據了。有一個可選的數據,是當前版本有多少 PV (Page Views,訪問量),若是須要記錄這個數據的話,在正確版本加載完成之時就要發送一個打點信息。不過不少需求中,具體版本的 PV 的精確數值可能不是很重要,並且要收集這個信息須要多一次打點操做,因此通常狀況下這個數據是可選的。
必須的數據是測試區域內用戶的點擊信息。當用戶在測試區域點擊了鼠標左鍵(不管這個點擊是點擊在連接、文字、圖片仍是空白處),咱們就須要發送一條對應的打點信息到打點服務器。通常來講,這個打點信息至少須要包含如下數據:
當前 A/B 測試以及版本標識
點擊事件的位置
點擊時間戳(客戶端時間)
當前點中的URL(若是點在非超連接區域,此項爲空)
用戶標識(好比 Cookie ID)
用戶瀏覽器信息
爲了儘量精確地還原用戶的點擊位置,咱們的頁面對前端有比較高的要求,要求頁面在不一樣的瀏覽器下有基本一致的表現,至少在IE六、七、8以及 Fiefox 下,頁面橫向的元素要精確一致,縱向上很難作到徹底一致,但也要儘量保持統一。另外,這樣的測試也不太適合自適應寬度的頁面,比較適合定寬的頁面,爲了不不一樣分辨率下頁面左右空白不一樣致使鼠標點擊位置的不一樣,點擊位置取的應該是相對於測試區域左上角的位置。除此以外,最好再記錄一下測試區域相對於頁面內容左上角的位置,在後面還原點擊分佈圖以及繪製熱區圖時會用到這個數據。
這一階段的流程大體以下圖所示:
數據打點該如何發送以及如何存儲呢?這要取決於你的打點服務器如何存儲信息。
4、數據存儲
咱們使用了一臺專用的服務器收集打點信息,爲了能支持儘可多儘量密集的打點請求,這臺服務器的 apache 服務網站目錄下只有兩個靜態文件,分別是 abtest.html 和 abtest.gif ,二者都是很是小的空白文件(空白圖片)。訪客端進行打點時,只須要以 GET 的方式帶上相關的參數請求兩個文件中的任意一個便可。好比:
http://abtest.xxx.com/abtest.gif?abid=1-a&clickBlockX=244&clickBlockY=372&clickBlockW=392&clickBlockH=76&clickTime=1263264082137&clickRX=233&clickRY=47&clickURL=&clickBeaconID=123.180.140.*.1267882109577.3&browserType=FireFox
這個請求能夠經過 Ajax 的方式發送,也能夠經過 JS 在頁面上建立 new Image() 對象的方式完成。
對打點服務器來講,這只是一條普通的 HTTP 請求,它會在日誌裏留下一條普通的日誌記錄,形如:
123.180.140.* – – [13/Jan/2010:15:21:15 +0800] 「GET /abtest.gif?a=123&b=456&c=789 HTTP/1.1″ 304 – 「-」 「Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.6 (KHTML, like Gecko) Chrome/4.0.266.0 Safari/532.6″
能夠看到了,除了 JS 發送給咱們的信息外,Apache 還幫咱們記錄了一些信息,好比訪客 IP 、服務器時間、用戶瀏覽器信息。
對於數據記錄和存儲來講,到這一步就足夠了。Apache 靜態文件 + 日誌的方式足夠高效,基本不用擔憂性能的問題。剩下的,就是另一個問題,如何從 Apache 日誌中讀取打點信息並加以分析,這已經和前端無關了,而且是一個比較複雜的問題,將在後續日誌中介紹。