1. 程式人生 > >memcached的學習(9)

memcached的學習(9)

2018.6.14
之前學習了memcached的網路模型,主要是服務於多個客戶端TCP連線的網路模型建立,也是CS架構上我們值得學習的一個模型,在設計網路模型的時候可以學習這種框架。今天來學習memcached的命令解析,也就是接收到了網路傳來的資料包,按照不同的命令解析,並呼叫不同的功能模組。

————————————————————————————————————————————
我們回顧上一章發現Memcached會分成主執行緒和N個工作執行緒。主執行緒主要用於監聽accpet客戶端的Socket連線,而工作執行緒主要用於接管具體的客戶端連線。

主執行緒和工作執行緒之間主要通過基於Libevent的pipe的讀寫事件來監聽,當有連線練上來的時候,主執行緒會將連線交個某一個工作執行緒去接管,後期客戶端和服務端的讀寫工作都會在這個工作執行緒中進行。

工作執行緒也是基於Libevent的事件的,當有讀或者寫的事件進來的時候,就會觸發事件的回撥函式。那麼Memcached是如何來解析客戶端上傳的命令資料報文的呢?這一章我們詳細講解命令的解析過程?

Memcached的命令解析原始碼分析

conn資料結構

每一個連線都會有自己的一個conn資料結構。這個結構主要儲存每個連線的基本資訊。
這一章中用到的幾個比較重要的引數:

char * rbuf:用於儲存客戶端資料報文中的命令。
int rsize:rbuf的大小。
char * rcurr:未解析的命令的字元指標。
int rbytes:未解析的命令的長度。

資料結構含義如下:
在這裡插入圖片描述

整體流程

  1. 當客戶端和Memcached建立TCP連線後,Memcached會基於Libevent的event事件來監聽客戶端是否有可以讀取的資料。
  2. 當客戶端有命令資料報文上報的時候,就會觸發drive_machine方法中的conn_read這個Case。
  3. memcached通過try_read_network方法讀取客戶端的報文。如果讀取失敗,則返回conn_closing,去關閉客戶端的連線;如果沒有讀取到任何資料,則會返回conn_waiting,繼續等待客戶端的事件到來,並且退出drive_machine的迴圈;如果資料讀取成功,則會將狀態轉交給conn_parse_cmd處理,讀取到的資料會儲存在c->rbuf容器中。
  4. conn_parse_cmd主要的工作就是用來解析命令。主要通過try_read_command這個方法來讀取c->rbuf中的命令資料,通過\n來分隔資料報文的命令。如果c->buf記憶體塊中的資料匹配不到\n,則返回繼續等待客戶端的命令資料報文到來conn_waiting;否則就會轉交給process_command方法,來處理具體的命令(命令解析會通過\0符號來分隔)。
  5. process_command主要用來處理具體的命令。其中tokenize_command這個方法非常重要,將命令拆解成多個元素(KEY的最大長度250)。例如我們以get命令為例,最終會跳轉到process_get_command這個命令 process_*_command這一系列就是處理具體的命令邏輯的。
  6. 我們進入process_get_command,當獲取資料處理完畢之後,會轉交到conn_mwrite這個狀態。如果獲取資料失敗,則關閉連線。
  7. 進入conn_mwrite後,主要是通過transmit方法來向客戶端提交資料。如果寫資料失敗,則關閉連線或退出drive_machine迴圈;如果寫入成功,則又轉交到conn_new_cmd這個狀態。
  8. conn_new_cmd這個狀態主要是處理c->rbuf中剩餘的命令。主要看一下reset_cmd_handler這個方法,這個方法回去判斷c->rbytes中是否還有剩餘的報文沒處理,如果未處理,則轉交到conn_parse_cmd(第四步)繼續解析剩餘命令;如果已經處理了,則轉交到conn_waiting,等待新的事件到來。在轉交之前,每次都會執行一次conn_shrink方法。
  9. conn_shrink方法主要用來處理命令報文容器c->rbuf和輸出內容的容器是否資料滿了?是否需要擴大buffer的大小,是否需要移動記憶體塊。接受命令報文的初始化記憶體塊大小2048,最大8192。

狀態機的整理變換過程如下:
在這裡插入圖片描述

命令rbuf資料結構變化圖:

1.讀取客戶端的資料
在這裡插入圖片描述
2.解析buf中的命令。如果遇到\n,則表明是一個命令語句的結尾識別符號。
在這裡插入圖片描述
3.命令拆分。命令解析出來之後,對命令進行分解,分解是通過空格來分離的。第一個引數一般為操作方法,第二個引數一般為KEY。
在這裡插入圖片描述

4.記憶體塊重設定。如果rbuf記憶體塊使用空間不足,或者大於8k,則需要進行重新分配記憶體塊。
在這裡插入圖片描述
————————————————————————————————————————————

具體分析:

從上面的分析流程來看,通過event記錄的回撥函式來捕捉讀寫事件的觸發,下面就以一個get命令,分析,是如何接收到命令,解析,執行,並返回給客戶端的過程。
在這裡插入圖片描述
這裡只是分析了從客戶端傳送的命令,進行取命令,解析命令,執行命令,打包返回結果,再次取新命令的執行過程,而打包返回結果在傳送給客戶端是memcached的迴應訊息的過程,這一過程也比較複雜,我們在之後在分析。

思考與分析:

這裡我們可以學習到的是,memcached的解析命令並處理的過程,以及對於不合適命令,不符合格式命令的處理,對於多客戶端連線、緩衝大小限制的處理。涉及到命令處理的過程,都可以很好的借鑑memcached的處理方式。