spymemcached原始碼中Reactor模式分析
簡介
spymemcached 是一個 memcached 的客戶端, 使用 NIO 實現。採用Reactor模式實現,單執行緒,高效能Memcached客戶端。
spymemcached原始碼分析:http://my.oschina.net/astute/blog/93492
Reactor模式介紹
所謂reactor模式,其實是event-driven pattern在網路服務設計中的應用,以平衡CPU與IO速率,最大化CPU資源與IO資源的利用率;
先來看看經典的伺服器設計:
經典網路服務接受客戶端請求,響應請求過程可以抽象為以下步驟:
- 讀取請求資料 : read
- 解碼資料: decode
- 計算:compute
- 編碼資料:encode
- 傳送資料:send
網路服務所有動作被抽象為這個五個步驟的handler;可能每個handler有單獨執行緒執行,或者由一個執行緒順序執行;
這種經典設計中存在以下問題:
- 每個連線分配一個執行緒,而每個連線傳送請求資料較少,導致大量空閒執行緒;
- 大量執行緒上下文切換和鎖競爭
為了解決上述問題,採用兩個方法實現:
- 採用分治的思想,將連線劃分為更小粒度的非阻塞任務:將使用者連線劃分為 多個使用者請求,每個請求一個執行緒處理;減少 請求間空閒時間佔用執行緒
- 採用事件處理模式,分派可執行任務;通過IO 事件觸發 handler 處理
採用事件處理模式的單執行緒Reactor模式:
單執行緒Reactor 處理 連線請求,所有使用者請求都在同一個執行緒中;通過IO事件觸發相應的操作;IO事件觸發機制可參考java nio機制;
單執行緒版本的Reactor模式,還有以下問題:
- 觸發事件處理 與 處理事件 在同一個執行緒中;減緩了Reactor的事件觸發及時性
- 所有請求必須等待之前請求中IO操作之外的處理過程
- 無法解決CPU與IO速率不一致問題
- 不能有效利用多核優勢
為解決上述問題,採用多執行緒設計方案的Reactor模式:
多執行緒版本的Reactor模式,有以下優勢:
- Reactor可以快速觸發handler執行
- IO操作以外操作由執行緒池中執行緒獨立處理
- Reactor 執行緒滿負荷 IO操作
- 平衡CPU 與 IO 速率
reactor模式就介紹到此,具體看下spymemcached中reactor模式的應用吧
spymemcached中Reactor模式實現
spymemcached中reactor模式設計到以下概念:
- IO執行緒:MemcachedConnection 負責處理
- 工作執行緒:呼叫spymemcached操作的執行緒;通常是應用執行緒,例如tomcat執行緒等等;
工作執行緒職責:
- 通過MemcachedClient提交操作請求給MemcachedConnection
- MemachedClient提交操作過程:例項化非同步操作回執OperationFuture(該回執都會例項化一個CountDownLatch(1));通過OperationFactory 生成定製Callback操作的Operation物件;將Operation操作加入對應ShardedNode 的inputQ;返回非同步結果回執OperationFuture物件
- 呼叫OperationFuture物件的get操作阻塞在CountDownLatch,等待後臺IO執行緒,呼叫countDown;
IO執行緒職責:
- 由MemcachedClient初始化啟動後臺MemcachedConnection執行緒,接受工作執行緒提交操作:從inputQ拷貝到writableQ;傳送操作請求到Memcached伺服器;新增操作到readQ,並把操作從writableQ刪除;修改監聽IO事件
- 執行select操作,獲取感興趣IO事件
- 執行handleIO處理:根據SelectKey發生事件型別:執行讀取操作 or 寫入操作;
- 當IO操作完成後,呼叫Operation Callback物件的receivedStatus設定回執結果;呼叫countDown釋放阻塞的工作執行緒;
由此可見,spymemcached Reactor模式實現中,工作執行緒相當於客戶端請求; IO執行緒相當於單執行緒Reactor設計中的Reactor負責接收請求,處理請求;spymemcached對資料解碼部分可以擴充套件實現執行緒池方式提供解碼計算,無需佔用Reactor執行緒資源,使得Reactor執行緒滿負荷IO操作和事件觸發;
在spymemcached的Reactor設計中:
MemcachedClient負責接收請求
MemcachedConnection負責處理IO請求
同時還可以擴充套件支援執行緒池TranscodeService對解碼計算提供非同步執行緒支援; 這也是OperationFuture.get()返回值仍未一個Futrue,內部再次呼叫future.get返回最終資料的原因。
轉載於:https://my.oschina.net/yychao/blog/99873