Retry
集群中有client、server1, server2三台機器,client需要向server請求數據,如果server1響應超時,則請求server2。server1、server2互為備份,包含同樣的數據。
client:
for (id = 0; id < 2; id++)
{
if (TIMEOUT == do_request(server[id], timeout, req) )
{
continue; // retry next server
}
}
timeout是外部傳給client的總超時數。上面的代碼有一個問題:當網絡阻塞或者server十分繁忙的時候,do_request會超時,一旦超時,則總超時時間都被耗盡,根本沒有剩余時間去重試下一個server。
因此,正確的retry設計需要考慮到“網絡阻塞或者server十分繁忙”的情況,分給每個server的timeout時間只能是總timeout的一部分,代碼改寫如下:
for (id = 0; id < 2; id++)
{
if (TIMEOUT == do_request(server[id], timeout * get_timeout_percent(), req) )
{
continue; // retry next server
}
}
不過,對於server掛掉的情況(socket層面無法連接)do_request請求這個server會立即失敗,可以不設置timeout_percent。
Backup Request
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
- client收到過期的結果直接丟棄
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且有完善和修改