說明:普通紅包是指金額每份金額固定的紅包包括群普通紅包和個人普通紅包,個人普通紅包也就是紅包個數為1的群普通紅包。
1 需求分析
一個字:錢;兩個字:消遣
1.1使用者為什麼要發紅包?
(1)逗別人玩自己開心
有些人發一些1分錢的紅包,看到大家哄搶,自己覺得很爽;有些人自己發1個0.01的自己搶和別人比拼速度,這些無聊的人追求的是娛樂性,如同黑白快、2048等,滿足無聊的人消耗時間就可以了。
(2)成為焦點人物
當你經常在群裡發紅包的時候,你就會成為「群明星」,讓更多人認識你,和你說話,你有一種自己朋友遍天下的錯覺,然而並沒有什麼卵用,人家是衝著你的錢來的。所以就有了【我發的紅包總數】【紅包被搶提醒】
(3)獲得關注賣廣告
單純發一個廣告不但沒有人看,而且會引起反感。但是你發個大紅包,群裡面的人會喜聞樂見,而且很親切的問你專案的相關內容或者幫你填寫調查問卷。不過後來小紅包廣告漸漸失去吸引力,因為大家的興奮閾值提高了,而且重複的東西是不可能讓使用者持續高潮的,如同你xxoo的時候不能總是使用一個體位一樣。因此對於服務號的搖一搖紅包和關注紅包,最少也有2元,而且還能裂變給你好友。2塊錢是什麼?官方的說法是一張彩票的價格,一份希望,2元彩票長期已經在廣大群眾心理建立了一個閾值,一份希望的閾值,『萬一實現呢?』這種心理。而且這種希望還能傳遞給別人(裂變紅包),何樂而不為呢?
(4)純粹是一種祝福
有時候是我們產品經理想太多了,人家可能僅僅是把傳統的紅包以微信紅包這種新穎的方式發出來而已。以前只有結婚的人才能發紅包,現在可以全民發,我也可以給朋友帶去一份祝福。不過包多少呢?這個很讓人糾結,太少不體面;給『上層』的人發紅包,人家閾值高,太多支付不起。於是就有了【隨機紅包這個東西了】
1.2 使用者為什麼要搶紅包?
(1)好玩刺激
這個理由還是留給那些無聊的人,不解釋,跟『為什麼要發紅包?』的第一點差不多。那個【最佳手氣】,就刺激了人們玩紅包接龍(不是某寶那個坑爹的紅包接龍…)
(2)貪婪——人類原始的慾望
在法律和道德重重的壓制下,深深地掩蓋了人的本性,當有這種合法而且又光明正大的”搶錢”,即使是0.01,也足以讓人們壓制的貪慾井噴,造就了微信紅包的繁榮。雖然官方說,搖紅包是為了讓老一輩瞭解我們的世界,瞭解我們的生活,讓我們過年回家能一起搖紅包。可是從朋友圈,從新聞,大家在群裡的反應,我一點都沒感覺到紅包讓家人團聚在一起,不知道那是否是公關說辭,這裡不作評價,而對人性的激發確是徹徹底底的。正如所有的自然科學最終都會迴歸到哲學問題上,所有的產品也終究要回歸到對人性的思考上。(廢話說太多了~~~)
(3)炫耀
證明自己單身20年,哦!不對,應該是證明自己手速快(不都是一個意思嘛,廢話真多!)有的人無聊到自己發自己搶,以此在炫耀自己的4G網路、光纖還有…麒麟臂。
(4)減少損失
很多人發了拼手氣紅包覺得自己錢包大出血,於是自己又搶了一把,希望自己搶到大一點的金額,相當於發少一點紅包。
1.3 為什麼要曬紅包?
(1)炫富心理——我發出的紅包統計頁面
(2)攀比心理——紅包結果頁面
這裡就不多作解釋了,想想你為什麼喜歡在朋友圈發東西就懂了。
2 入口
2.1錢包
在錢包新增紅包入口,是因為使用者首次使用時,一般是先收到別人的紅包,自己要取錢,那麼去哪裡取呢?肯定是錢包,錢包需要集合所有跟錢有關的概念,給使用者一個深刻的印象,類似於OmniFocis的透視功能(不知道就算了…),需要把相關內容聚合到一個入口。所以錢包聚合了和錢相關的內容,比如信用卡還款、手機充值、理財通等,使用者第一次進來收錢的時候,自然就看到微信紅包並進一步引導使用者繫結信用卡和發紅包。另外如果使用者第一次使用紅包不是收到別人紅包,而是聽說有紅包這一功能或者看到別人發紅包,他首先會想到錢相關的東西應該在紅包裡面。
2.2 聊天視窗
而這聊天視窗中加入紅包入口則是更加簡單粗暴,使用者在過年或者平常使用會經常點開”+”傳送圖片,這就很容易會見到紅包入口了,而且微信在過年的時候特意把紅包按鈕用紅色高亮顯示了,這就更加容易被使用者發現,從而提高入口轉換率。這裡還有個邏輯,在單聊和群聊所進入的紅包頁面的不同的,如下圖所示:
群紅包預設為拼手氣群紅包而不是定額紅包,為什麼要醬紫呢?先看看拼手氣群紅包的優勢:
1、金額隨機,時大時小的金額能給使用者驚喜;
2、可以看到其他使用者搶了多少,引起攀比心理(這次搶得不爽,下次一定要搶個大大噠);
3、產生很多新奇玩法,比如手氣最佳的發3倍金額;
4、由於可以看到誰搶了紅包,所以群裡面總是搶第一又不參與遊戲的人基本上是使用外掛軟體,群成員自發的要求群主踢掉這個人,這種眾包式的”反外掛”比微信自己使用技術手段去解決更加節省成本和更加有效。
至於拼手氣群紅包相對於普通紅包的劣勢就是需要大量的計算資源去計算紅包的隨機金額,不過對於騰訊那麼厲害的架構師和財力,這些計算資源算不了什麼,反而消耗這些資源去拉起微信群的活躍度和拉高同時線上人數讓財報好看點更加划算。
2.3 搖一搖
微信在春節前還額外的增加了搖一搖的紅包入口,給一直被認為”約炮神器”的搖一搖洗白(哈哈,開玩笑啦!)。加速度感測器也很具有互動性,卻一直隱藏在手機裡面,使用率不如攝像頭和話筒。搖一搖紅包很好的利用了每個人手機裡面”雪藏”的硬體。在”紅包肯定是要發的”和”搖一搖功能的程式碼本來就有”的兩大前提下,增加搖一搖紅包這功能並不會增加多少開發量和成本,因為既然紅包要發,無論用什麼形式發,後臺的負載均衡和高併發分流是肯定要做的,搖一搖的程式碼在約pao的時代就已經很完善了基本上不用改,接入紅包的邏輯就可以了,所以機會成本很低。那為什麼一定要發紅包呢?因為上一年已經帶壞頭了,示範效應導致支付寶也來分一杯羹,微信能不發嗎?
3 介面
3.1發紅包頁面
在單人聊天視窗進入的普通(定向)紅包的頁面只需要輸入紅包金額和祝福語,點選【塞錢進紅包】,如果已經繫結銀行卡,則調起對話方塊浮層【輸入密碼】;如果未繫結銀行卡則跳轉到零錢支付頁面,點選按鈕【使用零錢支付】即可,無需輸入密碼,在這個過程中,如果零錢不足,則會跳轉到輸入銀行卡號的頁面,點選【下一步】之後需要接著輸入姓名、銀行預留手機號和簡訊驗證碼,填寫完成後即可用銀行卡支付。
這裡不得不提的是一個非常人性化的設計,當點選”改完普通紅包”,從群手氣紅包切換到普通紅包的過程中,已經輸入的內容不會丟失,紅包個數不變,此時的單個金額EditView(安卓UI控制元件)中的值會由 總金額/紅包個數 得出並自動填充;當點選”改為群手氣紅包”,從普通紅包切換到群手氣紅包的過程中,已經輸入的內容不會丟失,紅包個數不變,此時的總金額EditView中的值會由 單個金額*紅包個數 計算出並自動填充,,不用使用者重新輸入,非常貼心。這也是微信”將使用者體驗做到極致”的地方之一。
3.2 紅包『搶』頁面
聊天視窗會顯示出紅包樣式的聊天訊息,點選紅包後會出現拆的頁面。
3.3 紅包『拆』頁面
點選按鈕【拆】之後,那坨黃色的東西會轉(用幾幀圖片切換形成的動畫,在IOS上比Android上執行起來更加流暢),那坨東西轉完之後頁面會跳轉到【紅包結果頁面】。值得一提的是安卓最新版本中將Html版本的紅包換成了安卓原生紅包介面,為什麼這麼做呢?
一是微信慣用的犧牲客戶端資源(CPU、記憶體、儲存卡容量)去換取伺服器端的穩定和減少資源投入的策略,頁面資源放在本地,這樣子web前端伺服器容量就可以減少投入,同時也可以減少客戶端對資源伺服器的訪問量。類似的,微信的聊天記錄是預設不儲存在伺服器端的,而是將各種圖片語音小視訊全部塞到你手機的記憶體裡面,微信表情在6.0版本之前也是不儲存到伺服器的。
二是以往基於web的紅包頁面經常會出現”媽的頁面還在loading紅包就沒了””紅包來了卻連不了網是怎樣一種體驗”等等的使用者抱怨,而原生的頁面因為放在本地不需要遠端載入,只需要傳輸簡單的紅包ID,傳送者等少量資訊即可通知客戶端顯示紅包頁面,可以減少聯網時間和降低網路狀況對搶紅包的體驗流暢度,讓使用者搶不到紅包都不會覺得是因為微信沒優化好,而是自己太幸福 (沒單身的手速慢,哈哈)。下圖為幾種紅包”拆”頁面(大家來玩找不同,嘻嘻):
那這四個頁面分別會在什麼時候出現呢?在5.2中會做詳細的介紹。
3.4 紅包結果頁面
紅包結果頁面會顯示搶到紅包的人的列表,其中金額最大的為手氣最佳。當有兩個或者以上金額相同的時候,以時間最早的一個為最佳手氣。頁面還會顯示發紅包的人極其暱稱、你自己領到的金額(如果沒領到就不會顯示),零錢入口和轉發該紅包的入口、我的紅包記錄入口。紅包結果頁面也有很多種,詳見本文的5.3部分.
3.5 搖一搖紅包
搖一搖紅包和企業紅包的隨機方法和群手氣紅包大同小異,由於沒有接觸過企業紅包的發放流程,這裡不多說。
為什麼要有剩餘紅包個數呢?
引用鵬飛在人人都是產品經理舉辦的產品經理大會廣州站上說的一句話”給使用者一個預期,現在還有沒有紅包,還有多少,而且這個數字必須準確,不能忽悠使用者。有些朋友和我說,他們就是在最後幾秒搖到的。所以,要讓使用者為希望而搖,為了希望,把手搖斷,又算什麼!”。沒錯,這個數字是”準確”的,但是他並不是實時的。因為過於頻繁重新整理的數字少量減少,不僅使用者沒有感知,不停的訪問資料庫剩餘紅包數對於伺服器也是極大壓力,所以推測微信是採用這種策略:每減少1個單位(比如說50W)的紅包數量,自動將這個值寫入快取伺服器,使用者搖紅包的時候都直接訪問快取,而且不是每次搖都訪問剩餘數,而是搖n次之後(比如搖了5次)才去請求一次剩餘紅包數,這樣就把傳遞到伺服器的壓力減少n倍。
上圖最後那個頁面你沒見過?
微信官方說,當伺服器壓力過大的時候,喚起讓使用者休息一下這個頁面。這裡我提出另外一種策略,也許微信也採用了這種策略:當使用者搖一搖請求紅包時,伺服器壓力過大,網路阻塞或者佇列已滿等異常情況下,會直接通知客戶端”你沒有搶到”,也就是直接返回那個搖紅包的頁面進行下一次的搖一搖動作,這樣子永遠也不會顯示那個”休息一下”的頁面。
4 後臺
4.1資料庫
以下關係型資料庫設計的欄位是基於少量請求下,我們模擬紅包系統的可行方案,並沒有考慮高併發、分庫分表以及快取的情況,關於這部分內容可以檢視本文4.4部分整理一些大神的回答作為了解。
(1)使用者資訊資料表user_info
userID、紅包ID、祝福語、紅包型別、紅包個數、紅包金額、超時
(2)使用者錢包資料表user_wallet
userID、money、銀行卡ID等其他欄位
(3)傳送紅包資料表red_send
紅包ID、senderID、紅包個數、紅包金額、祝福語、最佳手氣、發出時間
(4)接收紅包資料表red_receive
紅包ID、receiver、接收時間、接收金額
4.2 隨機演算法
很多人說紅包序列是預先在手機發出去的時候已經產生好隨機序列,其實這樣會產生大量的資料庫讀寫操作,記憶體讀的速度以DDR3-2400為例,能達到17G/s,寫的速度達到18G/s(參考文獻:m.it168.com/article_141…
以紅包ID為種子
red_ID = 1775509988475009
andom.seed(red_ID)
群手氣紅包的最小值為0.01,搖一搖紅包的最小值為2.00
min = 1.00
if (紅包為群手氣紅包):
min = 0.01
else(紅包為搖一搖紅包):
min = 2.00
群手氣紅包的最大值為剩餘紅包總額和個數的商的2倍(你可以在群裡不停地發紅包做迴歸,記得叫上我去拿紅包,哈哈)。
max = (remain_money/remain_num)*2
而搖一搖紅包官方給出的計算公式是剩餘金額/剩餘紅包數*n
n主觀猜測也是等於2,在這公司基礎上再人為控制概率。
方案一:
人為干擾概率的,有人拿到京東618元的紅包,動腦子想想,京東店慶是618,這個金額絕對不是隨機出來的,而是設定好金額,然後每個金額範圍都有一定的概率。
比如說2元—5元概率為85%;5元—20元概率為10%,20元—50元概率為4.99%,618元概率為0.01%。(概率僅作參考,因為樣本量太大,官方也沒提供資料,這裡只是提供其中一種可行的方案,以下程式碼也只是提供思路,與實際可執行的程式碼略有差別)
a = random.uniform(0,1)
b,_max,_min = 0
if a < 0.85:
_min = 2.00
_max = 5.00
elif a < 0.95 & a >= 0.85:
_min = 5.00
_max = 20.00
elif a < 0.9999 & a >= 0.95:
_min = 20.00
_max = 50.00
elif a > 0.9999:
_min = 618.00
_max = 618.00
random.uniform(min,max)複製程式碼
方案二:
_min = 2.00
_max = 剩餘金額/剩餘紅包數*n
人為放出618元的彩蛋紅包,並且用上述方法設定概率為0.0001%
4.3 紅包發出去那一刻發生了什麼?
這一部分由於個人的水平限制,未能給出有深度的簡介,這裡為了文章的完整性,借用胖胖的文章作為說明(胖胖的部落格為www.phppan.com)
(1)發紅包後臺操作:
在資料庫中增加一條紅包記錄,儲存到CKV,設定過期時間;
在Cache(可能是騰訊內部kv資料庫,基於記憶體,有落地,有核心態網路處理模組,以核心模組形式提供服務))中增加一條記錄,儲存搶紅包的人數N
(2)搶紅包後臺操作:
搶紅包分為搶和拆,搶操作在Cache層完成,通過原子減操作進行紅包數遞減,到0就說明搶光了,最終實際進入後臺拆操作的量不大,通過操作的分離將無效請求直接擋在Cache層外面。這裡的原子減操作並不是真正意義上的原子減操作,是其Cache層提供的CAS,通過比較版本號不斷嘗試,存在一定程度上的衝突,衝突的使用者會放行,讓其進入下一步拆的操作,這也解釋了為啥有使用者搶到了拆開發現領完了的情況。
拆紅包在資料庫完成,通過資料庫的事務操作累加已經領取的個數和金額,插入一條領取流水,入賬為非同步操作,這也解釋了為啥在春節期間紅包領取後在餘額中看不到。拆的時候會實時計算金額,其金額為1分到剩餘平均值2倍之間隨機數,一個總金額為M元的紅包,最大的紅包為 M * 2 /N(且不會超過M),當拆了紅包後會更新剩餘金額和個數。財付通按20萬筆每秒入賬準備,實際只到8萬每秒。
4.4 Q&A若干整理(這一部分是網上整理的,不知道如何分類比較好就放在一起了)
① 既然在搶的時候有原子減了就不應該出現搶到了拆開沒有的情況?
這裡的原子減並不是真正意義上的原子操作,是Cache層提供的CAS,通過比較版本號不斷嘗試。
② cache和db掛了怎麼辦?
主備 +對賬
③ 有沒有紅包個數沒了,但餘額還有情況?
沒有,程式最後會有一個take all操作以及一個非同步對賬保障。
④ 為什麼要分離搶和拆?
總思路是設定多層過濾網,層層篩選,層層減少流量和壓力。這個設計最初是因為搶操作是業務層,拆是入賬操作,一個操作太重了,而且中斷率高。 從介面層面看,第一個介面純快取操作,搞壓能力強,一個簡單查詢Cache擋住了絕大部分使用者,做了第一道篩選,所以大部分人會看到已經搶完了的提示。
⑤ 搶到紅包後再發紅包或者提現,這裡有什麼策略嗎?
大額優先入賬策略
⑥ 有沒有從資料上證明每個紅包的概率是不是均等?
不是絕對均等,就是一個簡單的拍腦袋演算法。官方已經在產品經理大會上說明這是個拍腦袋的演算法了。
⑦ 發紅包人的錢會不會凍結?
是直接實時扣掉,不是凍結。
⑧ 採用實時算出金額是出於什麼考慮?
實時效率更高,預算才效率低下。預算還要佔額外儲存。因為紅包只佔一條記錄而且有效期就幾天,所以不需要多大空間。就算壓力大時,水平擴充套件機器是。詳見本文4.2的說明。
⑨ 實時性:為什麼明明搶到紅包,點開後發現沒有?
答:2014年的紅包一點開就知道金額,分兩次操作,先搶到金額,然後再轉賬。
2015年的紅包的拆和搶是分離的,需要點兩次,因此會出現搶到紅包了,但點開後告知紅包已經被領完的狀況。進入到第一個頁面不代表搶到,只表示當時紅包還有。詳見本文Jinkey在第五部分的說明。
⑩ 紅包的設計
答:微信從財付通拉取金額資料過來,生成個數/紅包型別/金額放到redis叢集裡,app端將紅包ID的請求放入請求佇列中,如果發現超過紅包的個數,直接返回。根據紅包的邏輯處理成功得到令牌請求,則由財付通進行一致性呼叫,通過像比特幣一樣,兩邊儲存交易記錄,交易後交給第三方服務審計,如果交易過程中出現不一致就強制迴歸。
⑪ 併發性處理:紅包如何計算被搶完?
答:cache會抵抗無效請求,將無效的請求過濾掉,實際進入到後臺的量不大。cache記錄紅包個數,原子操作進行個數遞減,到0表示被搶光。財付通按照20萬筆每秒入賬準備,但實際還不到8萬每秒。
⑫ 如何保持8w每秒的寫入?
答:多主sharding,水平擴充套件機器。
⑬ 查詢紅包分配,壓力大不?
答:搶到紅包的人數和紅包都在一條cache記錄上,沒有太大的查詢壓力。
⑭ 一個紅包一個佇列?
答:沒有佇列,一個紅包一條資料,資料上有一個計數器欄位。
⑮ 每領一個紅包就更新資料麼?
答:每搶到一個紅包,就cas更新剩餘金額和紅包個數。
⑯ 紅包如何入庫入賬?
資料庫會累加已經領取的個數與金額,插入一條領取記錄。入賬則是後臺非同步操作。
⑰ 入帳出錯怎麼辦?比如紅包個數沒了,但餘額還有?
答:最後會有一個take all操作。另外還有一個對賬來保障。
5 互動
5.1前後端互動時序
###(2)收發群手氣紅包
① 發起紅包操作
② 銀行扣款邏輯,不成功則返回,成功則進行下一步
③ 請求將紅包寫入資料庫某個set,並獲取紅包ID返回客戶端
④ 長連線通知客戶端成功
⑤ 其他使用者接收到紅包訊息,點開,拆。由於使用者操作的速度遠遠低於計算機處理速度,所以這開啟和拆開的分離,相當於設定了一道緩衝。另外,點開之後,不直接獲取金額,而是先讀取紅包是否領完的快取,如果沒領完則顯示【拆】的按鈕。點選【拆】之後再次訪問快取看紅包是否領完,如果沒領完,則請求伺服器記憶體計算隨機金額並返回客戶端,然後非同步寫入資料庫。
⑥ 紅包結果會寫入LIstView(安卓的UI控制元件名稱,ios也有類似的控制元件)中,使用者可以馬上看到
⑦ 當使用者再次開啟紅包結果頁面時,會從資料庫讀取最新的結果列表並更新結果列表。
###(3)收發普通紅包
① 發起紅包操作
② 銀行扣款邏輯,不成功則返回,成功則進行下一步
③ 選擇傳送物件(若在聊天視窗中發起著跳過這一步)
④ 計算紅包均值(總額/個數),將紅包個數和均值寫入資料庫,返回紅包ID到客戶端
⑤ 其他使用者點開紅包,拆,訪問紅包個數判斷是否大於0,若為TRUE,則個數減1;若為FALSE則通知客戶端顯示【已領完】樣式。
5.2 介面互動
5.2.1 基本流程
5.2.2 拆紅包頁面顯示邏輯
對群手氣紅包、群普通紅包、普通紅包(其實就是紅包個數為1的群普通紅包)和是否領到和是否領完做3×3×3的交叉分析之後,歸納出以下結論:
5.2.3 紅包結果頁面顯示邏輯
說明:
1 代表有出現該項
“字樣”代表下圖所示區域的文字內容:
“按鈕”代表藍色文字連結,如下圖所示:
金額是指自己拿到的金額
搶到的人是指一個列表:
綠色格子代表沒有這種邏輯,可能是不出現該頁面或者其他原因。
對上表的資料進行挖掘,我們可以發現以下規則集:
(1)當領到紅包的時候,會顯示按鈕”已存入零錢,可用於發紅包”、”已存入零錢,可用於消費”、”已存入零錢,可用於轉賬”、”已存入零錢,可用於提現”的其中一個,順序或隨機出現;並顯示自己所獲得的紅包金額。
(2)當自己發的紅包沒被領完,會顯示按鈕”繼續傳送此紅包”;
(3)領到別人發的紅包時,會顯示按鈕”檢視我的紅包記錄”;
(4)對於群手氣紅包被領完時,如果紅包是自己發的會顯示字樣”n個紅包共n元,n秒被搶光”;如果是被人發的紅包則會顯示字樣”n個紅包,n秒被搶光”;對於(群)普通紅包被領完時,會顯示字樣”n個紅包共n元”;
(5)對於紅包(個數大於1)沒被領完,自己的紅包會顯示字樣”已領取x/y個,共x/y元”;別人發的紅包字樣”領取x/y個”;
(6)對於紅包(個數等於1)沒領完時,會顯示字樣”紅包金額n元,等待對方領取”;
(7)對於群手氣紅包和自己發的普通紅包都會顯示搶到紅包的人的列表;
(8)已經被領完的群手氣紅包才會顯示”最佳手氣”的標識;
從(4)-(6)的規則我們可以看出,微信做到為什麼是一個優秀的產品而不僅僅是一個及格的產品。自己發的紅包會顯示出總金額,自己發了多少錢自己心裡有數,卻不希望別人看到總的金額(雖然可以根據列表算出來,但是大部分人不會去計算每一個別人紅包的總金額),避免發紅包的使用者還要承受”面子問題”挫傷使用者發紅包的積極性。這樣去營造一種無分貴賤貧富,人人都可以發紅包的氛圍,間接提高發紅包的人數和整個平臺的活躍度。
5.2.4 搖一搖紅包
這一部分因為寫文章的時候搖一搖紅包活動已經下線了,所以只能從網上找來截圖,簡略地說明一下流程。如下圖:
僅以此文,紀念大學四年為了加入微信團隊所做出的努力。
Jinkey:原騰訊手機管家產品運營,原拍拍、微信購物產品經理。專注於社交產品、企業級產品、機器學習和 iOS 開發,公眾號 jinkey-love。