導讀
灰度很重要,灰度的策略也需要結合實際情況進行靈活的調整,本文跟大家分享了一個前些時間發現的灰度設計bug。
一、案例分享
跟大家分享一個前些時間發現的灰度設計bug,這個bug蠻有意思,看似完美方案,但因為未考慮到一些技術特性,導致存在缺陷。為更通順的說明,先進行一些名詞介紹。
灰度發布:灰度發布是指在黑與白之間,能夠平滑過渡的一種方式。AB-test就是一種灰度發布方式,讓一部分用戶繼續用A,一部分用戶開始用B,如果用戶對B沒有什么反對意見,那么逐步擴大范圍,把所有用戶都遷移到B上面來。灰度發布可以保證整體系統的穩定,在初始灰度的時候就可以發現、調整問題,以保證其影響度。(引用Wiki百科)
安全生產環境:(簡稱SPE)為保障線上穩定性提供灰度流量生產環境。發布從預發到生產環境前,需要經過SPE進行流量驗證。
1.1 案例簡述
同一個應用,需要消費同一個topic消息兩次,因不同環境的配置不一致,導致消息存在消費遺漏。
1.2 案例背景
需求的主要修改的點是將歷史各異的結算模型改成統一結算模型,新老模型相差較大。在notify消息的處理上,原本是想在同一個consumer處理類中改寫,但成本較大,且考慮到老模型后續是需要下線的,從代碼整潔的角度出現,新寫了一個消息處理類,使用另一個group組訂閱,最終的結果就是同一個topic消息會被兩個group組訂閱,并由兩個consumer類去消費。
1.3 方案描述
原灰度方案如下:設定一個時間作為灰度生效時間,再通過消息中買家的尾號作為切流條件。當一個消息被接收時,會先判斷當前時間是否大于灰度時間,若滿足大于灰度時間的條件,則判斷買家尾號是否在灰度切流范圍中,均滿足時,走新的結算流程。若兩個條件有一個條件不滿足,則走老結算流程。
1.4 方案缺陷分析
上述方案,一般而言,是一個很不錯也很完備的方案,但結合本次結算模型遷移的背景,則存在一個缺陷。缺陷的由來即是“雙消息”。那么雙消息處理是怎么引發問題的呢?原因在于灰度配置文件在安全生產和線上生產間階梯推進時,需要等待1小時,這一小時會導致部分消息被遺漏。假設當前SPE和線上生產已同步推進至10%,此時SPE的配置推進至20%,線上生產依舊保留10%,那么雙消息一條走到SPE,另一條走到安全生產時,即會出現問題,因為SPE的老group實現類只關心80%-100%的流量,而線上生產環境只關心0-10%的流量,中間存在消息遺漏。邏輯如下?圖:
1.5 案例的特殊之處
1.結算模型的升級,結合業務考慮,引入了雙消息消費的方式。
2.新老consumer無差別監聽消息進行消費,造成消費遺漏。
3.SPE安全生產停留1小時的機制。
1.6 改進策略
1.灰度時間總開關不變
2.買家尾號的灰度策略改為“尾號為K,區間生產時間為V”的KV對,其中V是一個未來時間,且“V值>系統時間+安全生產停留時間”。如下,從50%流量推至100%,100%的流量配置生效時間是一個未來時間。
[
{
"rate":5000,
"whiteList":[
],
"activeTime":1682928000000
},
{
"rate":10000,
"whiteList":[
],
"activeTime":1683795376000 //未來時間
}
]
在跟進完上述問題后,我查閱了過去一年部門故障復盤文檔,基本所有的故障和資金事件復盤,均有提及灰度手段。而像本案例中,于“不確定行為”進行灰度的控制,是新人們常見的誤區。因此,結合自己在交易線的一些經驗,談下我認知中的灰度,并將闡述我理解的MVP版灰度方案,至少需要些什么,灰度設計中需要注意什么。
二、設計一個MVP版的灰度方案
2.1灰度方案是什么?
在聊how的問題前,有必要先講一下WHAT和WHY。先聊一下WHY,假設沒有任何管控流程,代碼發布后立即全量上線,又恰巧不幸地出現系統、數據或邏輯等方面的問題,那一場災難也就隨之而來,而且業務體量越大,風險&輿情&資損的敞口也就越大,損失越不可挽回。因此,發布流程需要必要的管控和監督,將風險控制在有限范圍內,這樣的發布流程即是灰度。平滑過渡是灰度重要特征,這個特征也決定了灰度發布的作用至少有兩點:
1.降低發布帶來的風險,讓少部分用戶先使用新功能新版本,提前小范圍內發現bug或性能問題,及時做好修復,降低新功能新版本的影響。
2.通過新老版本對比,觀察新功能帶來的效果,更好起過渡效果。
2.2 MVP版的灰度方案( Minimum Viable Product最小可執行版本)
2.2.1 明確灰度維度
常見的灰度規則有用戶尾號(買家或賣家),業務單據id(如商品、訂單、結算單、運單)、黑白名單、人群圈選(如定向投放)。黑白名單和人群圈選有點類似A/B-test,能比較精準的用于線上功能測試。一般用于兩種場景:
1.風險極大,而功能測試又無法完全覆蓋的場景,可以使用白名單和人群圈選做第一步的灰度策略。
2.功能存在爭議,beta版功能測試,可以使用此法去收集反饋。
而采用用戶id或業務id作為灰度條件,是更為通用的方式,這兩種方案的流轉模式如下:?
尾號id灰度策略一般會與白名單策略組合使用,更穩妥的達到管控效果,結合我目前的項目實踐,有一種較為萬能的灰度公式分享給大家。
“1.白名單(個別用戶)--> 2.買家尾號灰度1% -->3. 尾號3% -->4. 尾號10% -->5. 尾號30% --> 6. 尾號灰度50%-->7. 尾號灰度70%-->8. 尾號灰度100%”。
多數場景下,這一套公式流程是比較適用的。也許有同學會有疑問,是否可以采用“賣家id”灰度維度,多數情況下是可以的,但以賣家id為灰度規則,更容易命中到超大商家,可能帶來的影響有兩個,1.集中的單據被影響,2.相較買家購買,賣家維度更容易命中爆品熱度的商品,引發數據庫熱點。因此,灰度維度的選擇,是一件需要深思的事,我認為有幾個原則需要遵守。
1.樣本避免以偏概全,盡量保證樣本的隨機性,近似均勻分布。
這一點很好理解,比如你想統計有多少人坐過杭州地鐵,然后你跑到杭州地鐵站去做問卷調查,除了得到100%的結果,你一定會得到更多的白眼。同樣,在灰度維度選擇上,也需要保持樣本的隨機性。如用戶id、商品id,一般就可以較好的滿足日常灰度的需要。而像業務的人群id,在篩選時就得嚴格關注一下樣本的隨機性。
2.篩選條件需要由嚴至寬,逐步放開。
舉一個例子,是去年進行某支付退款業務的中臺升級時,在完成所有驗證后,灰度策略里包含了一條,即逐步放開退款單的金額 ,從“控制退款單1元-->退款單10元-->30元-->100元-->300元-->全量放開”。這就是一種典型的風控層面的灰度策略,由嚴至寬。
2.2.2 做好過程觀察和推進
灰度是一個逐漸由白至黑的過程,在這個過程中,“灰度可觀察”和“灰度流程管理”,都需要做好。
灰度可觀察
灰度過程必須可觀察,這樣才能及時發現問題,真正發揮灰度的價值。我總結了四個點,是灰度可觀察的最小條件集,包括完備的流量日志、核對告警、反饋渠道及時觸達和性能關注。以下逐一闡述。
策略一:完備的流量日志
完備的日志對于灰度過程中的問題發現是非常有必要的,將詳細的處理邏輯,尤其是報錯和異常的日志,是灰度可監控的必要條件。一方面,技術同學可以通過觀察日志的錯誤日志,進行流量健康度的觀察,另一方面,也可以結合 sunfire的統計監控能力,對灰度過程的報錯做閾值告警。
策略二:核對告警
灰度項目中,核對也是常用的觀測策略,無論是新業務的逐步上新或是新老業務的逐漸切換。如一個重資金相關的新業務上線,在逐步的灰度過程中,使用核對是驗證非法單據的出現。這也是我日常業務跟進中經常使用的策略,比如RP3遷移灰度時,對打上新邏輯標的訂單做校驗。完備的日志是從系統處理的角度進行觀察,而監控核對是從數據的角度進行另一個維度的觀察,相輔相成不可缺少。
策略三:內部反饋渠道&輿情關注
這種策略一般用于白名單灰度的情況,且選中的白名單是與集團服務同學平時溝通較多且友好的商家,在百分比切流前,進行白名單個別商家的試點,關注異常情況。白名單內測后,切流階段,如果有不可規避的風險,需要技術同學時刻關注客服反饋,必要時需要給客服統一的話術。
策略四:關注灰度過程中的性能問題
灰度不僅應用于功能,也可用于性能觀察。灰度過程的流量是逐步增大的,新老功能的差異帶來的性能影響,也是逐步放大的。比如一次改動中,新老流量模型中,對于某個信息字段的獲取走的不同信息渠道,那么新老模型的性能差異就需要關注。此時不僅是業務的日志監控,應用系統的監控也需要安排上,尤其是灰度范圍擴大的一段時間里,尤其需要關注接口性能,包括依賴rt變高,自身rt變高,數據庫熱點等問題。
灰度流程管理
好的灰度流程,需要具備優雅、可靠的特征。我認為“有序的推進”和“可及時回退”是灰度流程中必要的考慮。
有序的推進
從0到1的灰度過程,是一個邊觀察邊推進的過程,通過日志、核對、自動化等手段做到有效觀察后,在積累一定量級的單據后,再進行逐步的放開。需要關注的是如果配置非法狀態下,是否保證老邏輯是否仍能正常消費。舉例說明,下方偽代碼是根據用戶的尾號進行判斷,在灰度配置平臺里進行尾號的數字配置,那么,這一段代碼看似正常,但實際隱藏著較大的風險:
//預期從灰度配置文件中讀取一個int型的值,但配置中grayRange設置了一個字符串型“50%”,
int grayRange = GrayHanlder.getConfig("灰度配置id").getInteger("grayRange");3
if(userId mod 100 < grayRange){
//走新邏輯
}else{
//走老邏輯
}
上述代碼在執行時,會報錯,導致新老邏輯都不會走到。當然,在實際業務需求中,很少發生這么低級的錯誤,但我的意思是,依賴于配置的灰度推進,需要確保灰度邏輯進行必要的驗證,灰度的推進也需要極其的謹慎。一般而言灰度代碼只是一個if+else,但其背后影響卻極大。
灰度流程推進的前提,一定有效的流量觀察之后,而不是形而上的依據灰度時長,請確保這一點。分享一個真實的故障案例:
案例名:某B系業務的會員子賬號id寫入錯誤導致無法處理退款。
故障原因:因修復一個會員登錄bug,更新了一個冷門字段的獲取邏輯,某B系業務使用此字段做權限控制,導致業務受到影響。
灰度過程:此案例中的發布應用,在發布過程中進行了灰度停留,但灰度時間是晚上,而被影響的B系業務特性決定了晚間時分是流量低谷期,導致灰度過程中的問題未被及時關注,灰度流程未發揮應有的效用。
灰度改進:涉及會員和商家操作的系統,灰度發布時間包含商家操作高峰期(上午8點--10點)。?
灰度回退
當上線功能的表現不符合預期時,需要考慮控制灰度回退。一般在灰度的配置文件中,需要引入邏輯開關,值為true或false,命中true后,才會進入更細粒度的灰度命中。所以當結果觀察不符合預期后,可以快速推進配置為false,必要時,走緊急審批。確保沒有新流量走到灰度流程中。
灰度回退僅能阻止問題的擴大,但已出現問題的單據需要做好妥善處理,一般而言,有兩種方案。
1.修訂數據:新寫修正接口或批量更改Db數據,但需要注意合規問題。
2.快速修復并發布新版本,利用升級來“回退”,覆蓋上次灰度發布的修改。
2.3 灰度工具
這里僅講一下服務端常用的灰度工具,一般都是輕量配置的平臺,將配置內容脫離于代碼之外,可以清晰快捷的推進,在灰度推進時,僅需做配置更新即可,不用發布代碼。配置可以簡單成一個String,也可以是一個json,舉例如下。
{
"flag": true, //總開關,true為開啟,false為關閉恢復
"buyerAccessFlow": 10, //用戶尾號控制,當前為尾號 0-9用戶進入恢復
"amountLimit": 30000 //控制金額,金額小于300元,才進入恢復
}
三、關于灰度其它常見的問題
3.1 機器分批發布不是嚴格意義上的灰度策略
許多同學對于灰度的認知還不多,認為分批發布也是一種灰度策略。其實嚴格意義上講,應用的分批發布更多的是出于對系統穩定性的考慮,而不是灰度驗證的考慮。從“可回退”的角度看,分批發布過程中,即使發現了問題,發現了臟數據,那數據也都是隨機的,數據無法通過特征來找出數據,也就無法對臟數據進行訂正或回滾,所以不是嚴格意義上的灰度策略。
3.2 灰度設計的一致性原則
灰度意味著線上存在兩套處理規則或流程,一條業務單據在兩套流程中處理的結果一般也是不一樣的,因此確保單據在灰度過程中的一致性非常有必要,否則很可能引發線上問題,這里先舉一個真實發生的例子
案例名:某電商業務增加一種退款打款渠道,灰度策略不合理導致雙渠道出賬的情況。
案例描述:RPC調用退款同意時,第一次命中尾號灰度,走進組合渠道中,但組合渠道里出現調用異常,但此時渠道會進行自身的重試。在重試期間,用戶二次點擊,此時灰度策略有變化,導致走到了另一個組合渠道,進行了打款并成功。?
案例分析:發生的原因在于打款事項的冪等被破壞。兩個渠道無法感知對方的打款行為。
案例解決:在申請退款時,即打上標,將灰度行為完全依賴于標識,后續所有的處理依標進行,避免不同調用引起的灰度不一致的行為。后續退款相關的灰度行為,都前移至了申請退款階段,此時命中灰度規則后,即會給它打上一個灰度標,后續的行為完全按標行走。
上述案例,是一個非常經典的問題,即灰度過程中數據一致性未能做好保證,導致出現兩種打款策略組,破壞了冪等。那么怎么樣保證灰度一致性原則呢?我認為有以下三個原則需要遵循:
原則一:灰度命中處理只能被一次消費
如上述案例,如果將灰度的判定放在“同意退款”,那就非常容易出現前后兩次調用時,走到不同處理流程的尷尬情況,反之,我們可以巧妙的將灰度判定前移至“申請退款”,并打上相應的標,后續“同意退款”則按標進行即可,保證灰度命中處理只被一次消息。“同意退款”是想灰度的功能,但“申請退款”才是真正的灰度對象。所以想灰度的功能跟實現灰度的對象,并不一定要一致。
原則二:確保不同環境的灰度一致性
許多帶安全生產環境的應用,從預發到線上前,需要在安全生產環境觀察1小時以上,包括應用代碼發布或配置發布。這中間的時差,極其容易引發灰度的混亂,比如開頭說的那一個case,由于notify無差別的往安全生產和線上生產環境發消息,應用在兩個環境中的配置不一致,導致消息被過濾的問題。
這種問題,就像使用原則一的方法進行預先打標處理,也是無法規避的,比較好的處理方法是,制造一個時延周期,確保線上生產和安全生產的一致性。比如推送一個未來的生效時間,且確保生效時間晚于全量發布完成后的時間,這樣即可確保兩個環境的一致性。
原則三:確保不同應用的灰度一致性
如果灰度流程涉及多個應用,那么灰度邏輯需要確保一致。簡而言之,一個形如“A-->B-->C”的鏈路中,要么保證B系統無視A系統的灰度條件,要么確保灰度邏輯僅在A系統中進行判斷。
3.4 前端灰度策略
前文提到的灰度策略,我均是從一個服務端的視角考慮,其實在前端或web端也有一些常用的灰度技巧,這里簡單聊一下。
1.CDN資源分流
前端資源放在CDN上,每次發布新版本,資源即增量的傳到CDN并指定唯一版本號。在處理請求時,依據前端策略來分流不同用戶使用不同版本的CDN,展示不同的樣式。此時,相應的后端接口,需要依據參數來控制灰度策略,區分前端不同的請求。一般在類似于賬單業務升級時,會用到這種策略。因為前后端都需要灰度,所以需要由前端控制灰度策略,后端進行參數兼容,保證賬單的多樣性。
2.客戶端分流
客戶端分流的策略與CDN資源一樣,由客戶端來控制灰度分流,根據客戶端傳來的參數和版本號,結合當前的放量策略來決定服務情況。客戶端分流的策略也會更多,如用戶設備系統、App版本號、app安裝渠道、用戶ID、設備ID。
四、寫在最后
灰度很重要,灰度的策略也需要結合實際情況進行靈活的調整。本文中提到的策略、觀點均是本人一得之見。引玉之磚,歡迎大家討論。
作者:崔劍飛(木祎)
來源:微信公眾號:阿里開發者
出處
:https://mp.weixin.qq.com/s/QPYprfHeHBnh38HQBh_K-w