上周在公司裡面跟同事一起交流了一些memcache的個人看法,後來整理了一下。主要分為:網路執行緒模式、記憶體基本結構、LRU結構、hash衝突以及hash乾坤大挪移。
memcache為單進程多線程結構,通訊架構使用libevent(http://monkey.org/~provos/libevent/,對定時器、管道、socket、中斷都進行了統一的管理,功能豐富肯定帶來一些效能上面的副作用,代碼值得去閱讀學習)。
1、線程分為兩種類型:主線程(接受線程)和背景工作執行緒:
1)主線程負責完成memcache的一些初始化工作之後,啟動event_base_loop迴圈,開始監控接受new connection,接收到new connection之後,主線程通過輪訓的方式,將new connection分配到對應的線程中(當然背景工作執行緒也是由主線程啟動,不然誰來啟動背景工作執行緒);
2)背景工作執行緒從主線程接受new connection後,將new connection加入到監聽隊列中,完成後續的所有資料通訊工作,例如根據用戶端命令set、get、del資料均需要在背景工作執行緒完成(背景工作執行緒的監聽方式也是一個event_base_loop迴圈);
2、主線程與背景工作執行緒socket傳遞
主線程分配new connect的方式為:迴圈法;
關鍵代碼為:int tid = (last_thread + 1) % settings.num_threads;根據計算值選擇對應的背景工作執行緒
主線程與背景工作執行緒在傳遞socket控制代碼的時候採用一個conn_queue_item,每個背景工作執行緒都對應一個conn_queue來接受主線程分配的conn_queue_item。主線程首先從conn_queue_item空閑列表中擷取一個conn_queue_item元素,填充相應的資訊存放到背景工作執行緒的訊息佇列中,然後用管道背景工作執行緒去處理新的串連。
關鍵代碼為:
1)從空閑鏈表中擷取空元素
if (cqi_freelist) {
item = cqi_freelist;
cqi_freelist = item->next;
}else{
new cqi_freelist
}
2)添加到背景工作執行緒隊列、管道通知
if (NULL == cq->tail)
cq->head = item;
else
cq->tail->next = item;
write(pipefd)
3)背景工作執行緒處理新串連
item = cq->head;
if (NULL != item) {
cq->head = item->next;
if (NULL == cq->head)
cq->tail = NULL;
}
free(item)
新串連分配處理流程圖