網路協議之:memcached text protocol詳解

flydean 發表於 2022-06-03

簡介

用過快取系統的肯定都聽過memcached的大名,memcached是一個非常優秀的分散式記憶體快取系統,應用非常的廣泛。Memcached不僅僅是Web快取,它更是一個通用的資料快取,基本上你可以將任何東西存入memcached中,它的分散式設計具有很好的可擴充套件性和靈活性。

Memcached是一個客戶端-伺服器端的架構模式。一般來說,在伺服器上搭建好Memcached的伺服器端,接下來就可以使用Memcached的客戶端和伺服器端進行交換了。

作為客戶端和伺服器端的模型,兩者的通訊肯定是有特定的協議的,適用於memcached的協議就叫做memcached protocol。

memcached的協議有兩種,分別是text協議和binary協議。本文將會詳細講解memcached text protocol的定義。

memcached protocol介紹

memcached可以看做是一個簡單的key-value的儲存系統,客戶端通過key來請求伺服器端的資料,伺服器端通過key的hash值來查詢對應的資料,然後返回給客戶端。

memcached中的key長度一般不能超過250個字元。key不能包含控制字元或空白字元。

為了保證客戶端和伺服器端的訊息通訊順暢,一般來說都會制定特殊的客戶端和伺服器端的通訊協議,這個協議就叫做protocol。

什麼是protocol呢?protocol聽起來很高深很神祕,但是實際上protocol就是約定好的雙方互動的訊息格式。

對於memcached來說,memcached同時支援UDP和TCP協議,並且提供了兩種協議方式,分別是“文字協議”和“二進位制協議”。

其中文字協議是在第一個版本就支援的協議,而二進位制協議是在v1.4之後才支援的。

文字協議和二進位制協議都支援同樣的命令,兩者的唯一區別就是二進位制協議具有更低的效能延遲和更好的可擴充套件性,而文字協議的有點就是它的可除錯效能更好。

memcached text協議包含兩部分資料,文字行和非結構化資料。前者是來自客戶端的命令或來自伺服器的響應,後者代表客戶端訪問的資料。命令以\r\n結尾,資料可以用\r、\n或\r\n,表示資料部分的結束。

memcached支援的命令

memcached支援三種命令,分別是儲存命令,讀取命令和其他命令。

儲存命令

memcached中的儲存命令總共有6個,分別是“set”、“add”、“replace”、“append”、"prepend" 和 "cas"。

首先,客戶端傳送如下所示的命令列:

command key [flags] [exptime] length [noreply]

另外cas命令的格式和其他幾個不太一樣:

cas key [flags] [exptime] length [casunique] [noreply]

上面的命令中,command代表的是命令的名字,也就是上面的“set”、“add”、“replace”、“append”和"prepend"。

set表示給key設定一個值。

Add表示如果key不存在的話,就新增。

replace用來替換已知key的value。

append表示將提供的值附加到現有key的value之後,是一個附加操作。

prepend將當前key對應的value新增到提供的值後面。

cas是一個原子操作,只有當casunique匹配的時候,才會設定對應的值。

flags是一個非常有趣的引數,這個引數對於memcached server來說是透明的,這個引數只是用來標記客戶端命令的型別,並不會被伺服器端識別。另外flags的長度在不同的memcached版本中也有所不同,在memcached 1.2.0或者根據低階的版本中,flags是一個16-bit的整數。在memcached 1.2.1或以上的版本,flags是一個32-bit的整數。

exptime是過期時間,0表示不會過期。

length是以byte表示的value的長度,這個值並不包含value中的結束符"\r\n"。

casunique是一個64-bit的現有entry的唯一值。

noreply告訴伺服器端,這是個不需要reply的命令。

在傳送完命令列之後,客戶端還需要傳送資料塊:

<data block>\r\n

舉個例子,我們想要將jack這個值設定到student這個key上,那麼對應的命令應該如下所示:

set student 0 0 4\r\njack\r\n

對應的客戶端收到的伺服器端的返回可能有這些值:

  • "STORED\r\n",表示儲存成功。
  • "NOT_STORED\r\n" 表示資料因為某些錯誤未儲存成功。這通常意味著不滿足“add”或“replace”命令的條件。
  • "EXISTS\r\n" 表示要設定的值在上次進行cas操作之後已經被修改了。
  • "NOT_FOUND\r\n" 表示要設定的值用在cas。

讀取命令

memcached的讀取命令有4個,分別是“get”、“gets”、“gat”和“gats,這些命令的格式如下:

get <key>*\r\n
gets <key>*\r\n
gat <exptime> <key>*\r\n
gats <exptime> <key>*\r\n

memcached中的讀取命令後面不需要跟額外的資料塊。

伺服器端會根據接收到的key進行查詢,每個key返回一條資料,格式如下:

VALUE <key> <flags> <bytes> [<cas unique>]\r\n
<data block>\r\n

在所有的資料都傳輸完畢之後,伺服器端會傳送"END\r\n"表示傳輸完畢。

這裡的key表示查詢傳入的key。

flags是儲存命令傳入的flags。

bytes是後面data block的長度。

cas unique是當前item的唯一標記,在gets或者gats命令中返回。

data block是當前item具體的返回值。

上面我們提到了4個讀取的命令,那麼他們有什麼區別呢?

首先是get和gets的區別,get 用於獲取key的value值,若key不存在,返回空。支援多個key。 gets 用於獲取key的帶有CAS令牌值的value值,若key不存在,返回空。支援多個key。 他們的區別在於gets會返回多一個cas unique值。

gat和get的區別是,gat是get+touch的命令綜合體,除了返回當前值之外,還會更新key的過期時間。

常用的其他命令

除了儲存和獲取之外,還有一些常用的其他命令。為什麼這些命令被叫做第三類命令呢?這是因為這些命令只需要一個命令列即可,並不需要向伺服器端傳入額外的資料塊。

下面是刪除命令的格式:

delete <key> [noreply]\r\n

key是要刪除的物件。

noreply表示是否需要收到伺服器的返回值。

對應的伺服器端返回值可能有兩個:

  • "DELETED\r\n" 表示刪除成功
  • "NOT_FOUND\r\n" 表示要刪除的物件並不存在。

下面是Increment/Decrement命令的格式:

incr <key> <value> [noreply]\r\n
decr <key> <value> [noreply]\r\n

key是要修改的物件。

value是要新增或者減少的值,它必須是一個64-bit無符號整數。

noreply表示是否需要收到伺服器的返回值。

伺服器端的返回可能有兩個:

  • "NOT_FOUND\r\n" 表示要修改的物件沒有找到
  • "value\r\n" 返回修改成功之後的值

還有一個常用的是修改key過期時間的touch命令:

touch <key> <exptime> [noreply]\r\n

key是要修改的物件。

exptime是過期時間。

noreply表示是否需要收到伺服器的返回值。

伺服器端的返回值有兩種:

  • "TOUCHED\r\n" 表示修改成功。
  • "NOT_FOUND\r\n" 表示要修改的物件不存在。

當然memcached支援的命令遠不止上面所講的這些。我們只是從中挑選出了最常用的一些命令進行講解。

memcached伺服器的返回值

上面在講解具體的命令的時候有提到伺服器的返回值,這裡再總結一下,memcached伺服器端的返回值有下面幾種:

返回值說明
STORED值儲存成功
NOT_STORED值儲存失敗
EXISTScas中要儲存的物件已存在
NOT_FOUND要修改的物件不存在
ERROR提交了未知的命令
CLIENT_ERROR errorstring客戶端輸入有誤,具體的錯誤資訊存放在 errorstring
SERVER_ERROR errorstring伺服器端異常
VALUE keys flags length返回要查詢的key對應的物件
DELETED物件已經被刪除
STAT name value統計資訊
END伺服器端返回結束

注意,上面所有的返回值都以"\r\n"結尾。

支援UDP協議

上面我們講的都是TCP協議的報文格式。事實上memcached還支援UDP協議。

但是因為UDP不保證可靠性的特徵,所以使用UDP的場合一般在做快取的查詢應用中,即使查詢失敗,也只是被看做是快取沒有被命中而已,並不會影響到資料的準確性。

事實上UDP的資料包和TCP的資料包格式基本一樣,只不過多了一個簡單的幀頭。並且所有的請求都必須在單個UDP資料包中完成。

注意,這裡只有請求才有這個要求,伺服器端的返回並沒有這個限制。

在UDP中幀頭長8個位元組,其中0-1個位元組表示的是請求ID,請求ID是由客戶端生成的一個單調遞增的值。伺服器端將會使用這個ID來標記是對哪個請求的響應。特別是在有伺服器端有多個響應的情況下。

2-3個位元組表示的是序列號,它的取值範圍是0到n-1,其中n是訊息中總的報文個數,也就是4-5個位元組所表示的。

最後的6-7位元組是保留位元組,以備將來使用,現在設定為0。

總結

以上就是對memcached協議的介紹,通常來說我們使用memcached都是通過memcached客戶端來進行的,如果有細心的朋友可能會發現,客戶端使用的命令和協議中的命令差別不大,這是因為客戶端就是對這些底層協議的封裝,然後暴露給使用者一個更加簡單易操作的介面。

更多內容請參考 http://www.flydean.com/23-memcached-text-protocol/

最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!