天天看點

程式中如何設計backup request功能

叢集中有client、server1, server2三台機器,client需要向server請求資料,如果server1響應逾時,則請求server2。server1、server2互為備份,包含同樣的資料。

client:

timeout是外部傳給client的總逾時數。上面的代碼有一個問題:當網絡阻塞或者server十分繁忙的時候,do_request會逾時,一旦逾時,則總逾時時間都被耗盡,根本沒有剩餘時間去重試下一個server。

是以,正确的retry設計需要考慮到“網絡阻塞或者server十分繁忙”的情況,分給每個server的timeout時間隻能是總timeout的一部分,代碼改寫如下:

不過,對于server挂掉的情況(socket層面無法連接配接)do_request請求這個server會立即失敗,可以不設定timeout_percent。

Jeff Dean的一篇論文中介紹了Google利用backup request來大幅度降低響應延遲的問題,在論文中他将這種請求成為Tied Request。他在Achieving Rapid Response Times in Large Online Services這篇ppt中對此進行了專門的論述。

其原理很簡單,用一個例子來簡單闡述:

叢集中有client、server1, server2三台機器,client需要向server請求資料,如果server1響應逾時,則請求server2。server1、server2互為備份,包含同樣的資料。client收到任意響應資料後立即通知其他請求過的server取消操作。

有兩種設計方案:

client向server1發出req請求,req在server1任務隊列中排隊

server1開始執行req,在執行前給client發一個quick response

client如果在逾時時間内收到quick response則不發起backup task,否則client一旦逾時,就立即發起backup task

client收到任意server的結果時,立即給其它所有發給過請求的server發cancel request

client先行退出

對于執行任務的server,它如果及時收到了cancel request,則直接取消任務,如果收到不及時,任務已經開始,則還是老老實實做任務。任務做完後丢棄結果。

client收到過期的結果直接丢棄

client向server1發出req請求

client等待逾時,則立即向server2發req請求

任意server傳回了req結果,則發cancel request給其它相關server

note:無論方案1還是方案二,實作這樣的異步系統的時候都要很小心,一要防止記憶體洩露,一要方式提前析構導緻野指針。

建議選擇p99或者p95,因為backup request操作是用來實作消除長尾的,并不是提升性能的。如果将該值設定過低,則會由于backup request的請求量過大而導緻叢集壓力增大(假設選擇p50作為其延時,這樣便會有50%的請求向server2發送請求,系統負載便會增大50%)。

如果逾時設定的是p999時間,大約1000個請求裡隻有1個請求會發送backup request,是以額外請求量(也就是開啟backup request的額外開銷)比例在0.1%左右。依此類推,若想要降低P99時延,則可以将逾時設定為P99延遲,由此會增加1%的額外讀流量。

設計Backup request的關鍵是要防止伺服器繁忙時期的請求風暴。在伺服器繁忙時期client容易發生等待逾時,傾向于發送backup request。大量的backup request會進一步讓伺服器更繁忙,于是請求風暴誕生了。防止請求風暴的要點是區分普通逾時和風暴期逾時。

從統計的角度看,普通逾時的模式與風暴期逾時的模式肯定有很大差別,這是一個入手點。

從使用方式上,區分scan和get也可以一定程度防止請求風暴。在OceanBase中,get請求是對延遲敏感的,scan請求則要求低一些。而恰好get請求對系統的壓力也小很多。是以,在OceanBase中可以隻對get請求使用backup request。

對于非幂等性操作慎用backup request!!!

retry和backup request之前的差別在于retry不會給server發送cancel request,也不會等待多次請求使用最先傳回的響應。

轉載CSDN且有完善和修改