1. 程式人生 > >深入Redis客戶端(redis客戶端屬性、redis緩衝區、關閉redis客戶端)

深入Redis客戶端(redis客戶端屬性、redis緩衝區、關閉redis客戶端)

深入Redis客戶端(redis客戶端屬性、redis緩衝區、關閉redis客戶端)

Redis 資料庫採用 I/O 多路複用技術實現檔案事件處理器,伺服器採用單執行緒單程序的方式來處理多個客戶端傳送過來的命令請求,它同時與多個客戶端建立網路通訊。伺服器會為與它相連線的客戶端建立相應的 redis.h/redisClient 結構,在這個結構中儲存了當前客戶端的相關屬性及執行相關功能時的資料結構。


I/O 多路複用:linux有五類io模型 1.阻塞 2.非阻塞 3.io多路複用 4.事件驅動 5.非同步

阻塞:一次網路io時,C端發出請求,S端收到。當C端發出一個請求,進行io時,就不能進行其他操作了,需要同步的等待結果的返回。

io多路複用:有多個C端同時傳送請求,這些IO操作會被selector(epoll,kqueue)給暫時掛起,入記憶體佇列。此時S端可以自己選擇什麼時候讀取、處理這些io,也就是說S端可以同時hold住多個io。


redis的文字事件處理器


客戶端的名字、套接字、標誌和時間屬性

127.0.0.1:6379> client list
id=2 addr=127.0.0.1:53555 fd=9 name= age=6 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
  1. 名字:在預設情況下,連線到 Redis 伺服器的客戶端是沒有名字(name屬性, 如上)的

    給它設定名字

    127.0.0.1:6379> client setname "local_client"
    OK
    127.0.0.1:6379> client list
    id=2 addr=127.0.0.1:53555 fd=9 name=local_client age=110 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client

    如果沒有為客戶端設定名字,那麼這個客戶端的 name 屬性將會指向 NULL 指標;而如果為這個客戶端設定了名字,那麼這個客戶端的 name 屬性將會指向一個字串物件,這個物件中儲存了客戶端的名字

  2. 套接字:

客戶端套接字由客戶端狀態的 fd 屬性記錄

當 fd 屬性值為-1 時,表示這個客戶端是偽客戶端。偽客戶端的請求命令不是來源於網路的,而是來源於 Lua 指令碼或 AOF 檔案(後續詳細介紹)的,所以偽客戶端不需要套接字連線,它也沒有套接字描述符。當我們執行的 Lua 指令碼中含有 Redis 命令,或者使用 AOF 檔案來還原資料庫狀態時,就會用到偽客戶端。

當 fd 屬性值是大於-1 的整數時,表示這個客戶端是普通客戶端。普通客戶端採用相關套接字來實現與伺服器的通訊,因此伺服器會利用 fd 屬性來記錄客戶端套接字的描述符。

  1. 標誌屬性

    客戶端的標誌屬性 flags 用來記錄客戶端的角色(Role)及客戶端目前所處的狀態。

    flags 屬性的取值可以是單個標誌,也可以是多個二進位制或的組合標誌,具體如下。

    單個標誌:flags=<flag>

    組合標誌:flags=<flag1>|<flag2>|<flag3>|…

    標誌使用常量來表示。Redis 所具有的所有標誌都定義在 redis.h 檔案中。

    記錄客戶端角色的標誌有如下幾個。

    ● 在利用 Redis 主從伺服器實現複製時,主從伺服器會相互成為對方的客戶端,也就是從伺服器是主伺服器的客戶端,同時主伺服器也是從伺服器的客戶端。Redis 使用REDIS_MASTER 標誌來表示這個客戶端是主伺服器,而使用 REDIS_SLAVE 標誌來表示另一個客戶端是從伺服器。

    ● Redis 使用 REDIS_LUA_CLIENT 標誌來表示該客戶端是一個專門用於處理 Lua 指令碼的偽客戶端,它主要用於執行 Lua 指令碼中包含的 Redis 命令。

    ● Redis 使用 REDIS_PRE_PSYNC 標誌來表示該客戶端是一個低於 Redis 2.8 版本的從伺服器,此時,對應的主伺服器不能使用 PSYNC 命令實現與從伺服器的資料同步。只有當 REDIS_SLAVE 標誌處於開啟狀態時,才能使用 REDIS_PRE_PSYNC 標誌。

    記錄客戶端當前狀態的標誌有如下幾個。

    ● REDIS_ASKING 標誌表示客戶端向執行在叢集模式下的伺服器節點發送了 ASKING 命令。

    ● REDIS_CLOSE_ASAP 標誌表示客戶端的輸出緩衝區過大,超出了伺服器所允許的範圍。當伺服器在下一次執行 serverCron 函式時,會關閉這個輸出緩衝區過大的客戶端,以此來保證伺服器的穩定性不受這個客戶端影響。在關閉的時候,儲存在這個緩衝區中的資料也會被刪除,並且不會給客戶端返回任何資訊。

    ● REDIS_CLOSE_AFTER_REPLY 標誌表示客戶端給伺服器傳送的命令請求中有錯誤的協議內容,或者使用者在客戶端中執行了 CLIENT kill 命令。此時伺服器會將客戶端輸出緩衝區中儲存的所有資料內容傳送給客戶端,然後關閉這個客戶端。

    ● REDIS_DIRTY_CAS 標誌表示事務使用 WATCH 命令監視的資料庫鍵已經被修改。

    ● REDIS_DIRTY_EXEC 標誌表示事務在命令入隊時出現錯誤。

    REDIS_DIRTY_CAS 和 REDIS_DIRTY_EXEC 標誌的出現都表示 Redis 事務的安全性已被破壞。只要這兩個標誌中的任何一個被開啟,EXEC 命令都會執行失敗。而只有在客戶端打開了 REDIS_MULTI 標誌的情況下,才能使用這兩個標誌。

    ● REDIS_MULTI 標誌表示客戶端正處於執行事務的狀態中。

    ● REDIS_MONITOR 標誌表示客戶端正處於執行 MONITOR 命令的狀態中。

    ● REDIS_FORCE_AOF 標誌表示讓伺服器將當前正在執行的命令強制寫入 AOF 檔案中。在執行 PUBSUB 命令時,會使客戶端開啟 REDIS_FORCE_AOF 標誌。

    ● REDIS_FORCE_REPL 標誌表示強制讓主伺服器將當前正在執行的命令複製給所有與它連線的從伺服器。當執行 SCRIPT LOAD 命令時,會使客戶端同時開啟 REDIS_FORCE_AOF 和 REDIS_FORCE_REPL 標誌。如果要實現主從伺服器可以正確地載入 SCRIPT LOAD 命令指定的指令碼,那麼伺服器必須使用 REDIS_FORCE_REPL 標誌,讓主伺服器強制將 SCRIPT LOAD 命令分發給相應的從伺服器。

    ● REDIS_UNIX_SOCKET 標誌表示伺服器連線客戶端使用的是 UNIX 套接字。

    ● REDIS_BLOCKED 標誌表示客戶端正處於被 BRPOP、BLPOP 等命令阻塞的狀態中。

    ● REDIS_UNBLOCKED 標誌表示客戶端不再阻塞,它從 REDIS_BLOCKED 標誌的阻塞狀態中脫離出來。只有在 REDIS_BLOCKED 標誌被開啟的情況下,才能使用 REDIS_UNBLOCKED 標誌。

    ● REDIS_MASTER_FORCE_REPLY 標誌:在主從伺服器進行命令互動的過程中,從伺服器需要向主伺服器傳送 REPLICATION ACK 命令。但是,在傳送此命令之前,從伺服器必須開啟主伺服器對應的客戶端的 REDIS_MASTER_FORCE_REPLY 標誌;否則主伺服器會拒絕執行從伺服器傳送的 REPLCATION ACK 命令。

  2. 時間屬性

    ● ctime 屬性:該屬性記錄了客戶端被建立的時間。利用這個時間可以計算出這個客戶端與伺服器相連線的時間,單位為秒。在執行 CLIENT list 命令後,返回的 age 域記錄了連線秒數

    ● lastinteraction 屬性:該屬性記錄了客戶端與伺服器最後一次互動的時間。互動就是兩者之間互相傳送命令請求與返回結果。利用 lastinteraction 屬性可以計算出客戶端的空轉時間,也就是在進行最後一次互動之前過去了多少時間,單位為秒。CLIENT list 命令返回的 idle 域記錄了這個時間。當 idle 的值為 0 時,表示空轉時間為 0 秒。

    ● obuf_soft_limit_reached_time 屬性:該屬性記錄了客戶端輸出緩衝區第一次達到軟性限制的時間


redis客戶端的緩衝區

一些概念

● 輸入緩衝區:用於儲存客戶端傳送的命令請求。輸入緩衝區的大小是動態變化的,它會根據輸入的內容動態縮小或增大。1GB 是輸入緩衝區的最大大小。如果輸入緩衝區的大小超過了 1GB,那麼這個客戶端將會被關閉。

● 輸出緩衝區:用於儲存執行客戶端請求命令返回的結果或返回值。每個客戶端都有兩個輸出緩衝區,一個輸出緩衝區的大小是固定的,另一個輸出緩衝區的大小是可變的。

➢ 固定輸出緩衝區:用於儲存那些長度比較小的返回值,比如常見的 OK、<nil> 或者一些短字串、整數值及錯誤值等。

➢ 可變輸出緩衝區:用於儲存那些長度比較大的返回值,比如一個長度比較大的字串、大列表、大集合等。

buf 和 bufpos 屬性組成了客戶端固定大小的緩衝區。

buf 屬性是一個位元組陣列,陣列大小為 REDIS_REPLY_CHUNK_BYTES 位元組。REDIS_REPLY_CHUNK_BYTES 常量的預設值為 16×1024,即 buf 陣列的預設大小為 16KB。

bufpos 屬性記錄了 buf 陣列到目前為止已經使用的位元組數量。

當 buf 陣列已經存滿或者回復因為太大而沒有辦法存入 buf 陣列時,伺服器就會使用可變大小的緩衝區。

連結串列 reply 和一個或多個字串物件組成可變大小的輸出緩衝區。通過使用連結串列來連線多個字串物件,伺服器可以為客戶端儲存一個非常長的命令返回值,而不會受到大小的限制。如圖所示為可變大小的輸出緩衝區。

伺服器採用軟性限制(Soft Limit)和硬性限制(Hard Limit)兩種模式來限制客戶端緩衝區的大小。

  1. 軟性限制:如果軟性限制所設定的大小小於輸出緩衝區的大小,且輸出緩衝區的大小不大於硬性限制所設定的大小,那麼伺服器會使用客戶端狀態結構的 obuf_soft_limit_reached_time 屬性來記錄客戶端達到軟性限制的起始時間。之後伺服器會繼續監視客戶端,如果這個緩衝區的大小一直超出軟性限制,並且持續時間超過伺服器設定的時長,那麼伺服器將會關閉這個客戶端。相反地,如果輸出緩衝區的大小在指定時間範圍之內沒有超過軟性限制,那麼這個客戶端不會被關閉,並且 obuf_soft_limit_reached_time 屬性的值也會被設定為 0。

  2. 當輸出緩衝區的大小超過了硬性限制的大小時,這個客戶端會被立即關閉。

  3. 設定軟性限制和硬性限制

    我們可以使用 client-output-buffer-limit 選項來為普通客戶端、從伺服器客戶端或執行訊息訂閱釋出功能的客戶端設定軟性限制和硬性限制


客戶端的 authenticated 屬性

authenticated 屬性是客戶端身份驗證屬性,用於記錄客戶端是否通過了身份驗證。這個屬性的值為 0 和 1,預設值為 0。

當 authenticated 屬性值為 0 時,伺服器除執行 AUTH 命令之外,將會拒絕執行客戶端傳送過來的其他所有命令。


客戶端的 argv 和 argc 屬性

argv 屬性:這是一個數組,陣列中的每個元素都是一個字串物件,其中 argv[0]是要執行的命令,而之後的其他元素是傳給這個命令的引數。

argc 屬性:用於記錄 argv 屬性的陣列長度。

當客戶端向伺服器傳送命令時,伺服器會將接收到的命令儲存到客戶端狀態的 querybuf 屬性中。儲存之後,伺服器會分析這個命令的內容,並將分析得出的命令引數及命令引數的個數分別儲存到 argv 和 argc 屬性中


關閉客戶端

普通客戶端被關閉的幾種方式:

● 當客戶端執行了 CLIENT kill 命令時,客戶端會被關閉。

● 當客戶端程序被殺死時,客戶端將會斷開與伺服器的連線,從而客戶端被關閉。

● 當客戶端向伺服器傳送的命令是錯誤協議格式時,客戶端會被關閉。

● 當客戶端傳送的命令請求的大小超過了輸入緩衝區的限制大小時,客戶端會被關閉。

● 當傳送給客戶端的命令執行後返回結果的大小超過了輸出緩衝區的限制大小時,客戶端也會被關閉。

● 當為伺服器設定了 timeout 引數值,同時客戶端的空轉時間又超過了 timeout 引數值時,客戶端將會被關閉。而如果這個客戶端是主伺服器,而從伺服器被 BLPOP、BRPOP 等相關命令阻塞,或者從伺服器正在執行與訂閱釋出相關的命令,此時就算客戶端的空轉時間超過了 timeout 引數值,這個客戶端也不會被關閉