ZT: 海量並發的無鎖編程 (lock free programming)
分享: |
![]() |
文章評論
webdriver | 無題 2014-10-21 22:36:05 | 引用 |
無題 先想一個比較粗暴的辦法:使用一個set記錄未返回的request 的id,然後在接到響應時,查看這個set有沒有這個id,如果有,刪除它,並且響應client;第二個以後的響應達到時,由於在set已經沒有這個id了,因此這些請求將被丟棄。
這個裡邊涉及到對set的讀和寫操作,這個需要加鎖;如果這個set是進程內可見的,那麼這個鎖就是進程級別的(或者說該進程或者說是線程的子線程都是可見的),加鎖時很多線程都會等待該鎖。這樣的話對性能會有很大損耗。 這個方法對於每秒幾百次請求是沒有問題的。但是如果達到千這個級別,那麼鎖的使用會達到數千次(比如1000個請求,發送3個請求到後台,那麼每次寫set加一次鎖,3個請求回來都會加一次鎖,因此相當於一個真實的請求會加鎖4次,1000個請求就是4000次,想想都恐怖,1s要加鎖4000次,鎖的代價再小也很恐怖吧,別說set的插入和查詢,刪除也有不可忽略的性能損耗)。 那麼可不可以加線程級別的鎖?線程級別的鎖會減少對其他線程的影響。但是,set如果也是線程級別的,那麼得保證異步回調的借口也得是在同一個線程才可以。否則這個線程發出的請求,被其他的線程得到,那麼上述的邏輯是不通的,因為set是線程級別的,對於其他線程來說是不可見的。這樣的話如果架構能夠保證一個異步請求的返回,也是在同一個線程處理就好了。那麼,如果架構可以這麼保證,那麼你根本不需要鎖,為什麼呢?因為一個線程都是順序執行的,不會有資源的競爭,因此讀寫set都是安全的,因此不需要加鎖。 2014-10-21 22:36:27 | 引用 |
webdriver |
webdriver | 無題 那麼問題來了,架構如何支持這個異步回調也是走到相同的線程裡?
一個實現就是實現一個線程池,對於特定的request id,基於一定的規則將他調度給一個工作線程;等到異步返回時,再通過這個request id調度給相同的線程處理。 那麼如何實現一個線程池?boost 裡有; 如果調度,boost 支持調度給哪個線程。問題解決。 睡覺。 2014-10-21 22:36:53 | 引用 |
當然了在線和離線架構的相同和區別談起來完全是一個大文章。本文主要關注在處理高並發請求的鎖的使用上。幾個原則吧:
不要使用全局鎖。使用全局鎖代表在需要請求鎖時,其他為得到鎖的線程都會等待,這將導致服務能力急劇下降。
一定要注意鎖的作用范圍,一定要保證鎖作用於足夠小的范圍。一定不要在鎖定區域有等待操作,比如IO調用。
盡量的考慮修改架構,避免加鎖。
試想一個場景,為了服務質量,我們可能發送多個請求到後台,以達到:
高可用行,後台的某個節點掛了,有其他的backup request會被請求。如果節點的SLA是99%(很低了),那麼發送2個請求到後台,SLA可以達到99.99%;如果單個節點的SLA是99.9%的話,SLA可以達到99.9999了,即百萬次請求至多一次失敗。
低延時,第一個回來的請求會響應,這樣的話能夠保證某些慢的節點不會影響系統整體的延時。
那麼如何判斷第一個請求是第一個達到的呢?