Redis 中的客戶端


Redis 是一個客戶端服務端的程序,服務端提供數據存儲等等服務,客戶端連接服務端並通過向服務端發送命令,讀取或寫入數據,簡單來說,客戶端就是某種工具,我們通過它與 Redis 服務端進行通訊並完成數據操作。

客戶端並不是 Redis 的核心,Redis 的核心是它的服務端程序,服務端程序才是完成數據存、取,持久化等等我們使用頻繁的各種操作的執行者。但也不是說客戶端就沒什么作用,客戶端在整個 Redis 服務體系中也是非常重要的一環。本篇先來看看 Redis 客戶端的一些特性以及實現原理。

一、客戶端的基本屬性

redis 中為客戶端抽象的數據結構是,server.h/client 結構,我這里是 redis-4.0.x 版本,不同版本或許稍有不同,每一個 redis 客戶端成功的連接上服務端之后,服務端就會創建一個 client 結構實例,並以鏈表的形式鏈接所有連接成功的客戶端。

這個結構最主要作用就是存儲當前客戶端的大量屬性,套接字、名字、標志,狀態等等信息,這些信息非常的重要,當服務端為客戶端服務時,很多的信息例如當前要執行的命令、參數都會從這里獲取。我們一個一個來了解。

1、客戶端名稱

默認情況下,所有連接成功的客戶端都是沒有名字的,這一點你可以通過向服務發送 client list 命令驗證,它會返回當前服務端成功建立的客戶端以及他們的基本信息。例如:

image

可以看到,name 字段默認是空,如果你想讓你的客戶端辨識度更高,你可以向服務端發送 client setname 為你的客戶端命名,這里我就不做演示了,客戶端名稱這個信息保存在 client 結構中的 name 字段里。

typedef struct client {
    .........
    robj *name;             /* As set by CLIENT SETNAME. */           
    .........
} client;

2、標志

標志用於描述當前 redis 客戶端的一些狀態或者角色,對應的到數據結構中就是一個整型字段。

typedef struct client {
    .........
    int flags;              /* Client flags: CLIENT_* macros. */
    .........
} client;

Redis 中定義了很多的客戶端標志,

image

一個整型的 flags 字段,可以通過二進制或(|) 的方式同時存儲過個狀態,比如:

flags = 0000 0110 = CLIENT_MASTER | CLIENT_MONITOR

當然了,上面那個 flages 的值只是舉了個例子,描述了當前客戶端是一個主節點的 server(當進行主從節點復制的時候,主節點會作為客戶端連接從節點發送 RDB 文件給客戶端),又正在執行 MONITOR 命令。前者描述了客戶端角色,后者描述客戶端狀態。

總而言之,redis 客戶端 flags 字段可以描述當前客戶端的角色,也可以記錄當前客戶端各種狀態信息,是服務端了解客戶端信息的一個非常重要的字段。

3、輸入/輸出緩沖區

redis 服務端收到客戶端發來的命令請求需要很多步驟來處理和調用相關命令的實現,並最終將數據返回給客戶端,那么輸入緩沖區其實就是一小塊內存,用於存儲客戶端發送過來的命令,包括參數,這塊內存空間默認不能超過 1GB,否則 redis 服務端就會強制關閉與該客戶端的連接。

typedef struct client {
    .........
    sds querybuf;           /* Buffer we use to accumulate client queries. */
    .........
} client;

querybuf 就是客戶端緩沖區,它是一個 SDS 類型的字段,那么說明這是一個可以動態擴充輸入緩沖區。

當然我們也可以通過 client list 看看當前客戶端的的 querybuf 分配和使用情況。

image

其中 qbuf 和 qbuf-free 用於描述客戶端輸入緩沖區狀態。我這里的這個沒有寫入過大的命令,所以這里的 querybuf 只分配了 32768 個字節。

ps:盡量不要使用過大的 KEY,這樣會導致客戶端 querybuf 占用過多內存,這樣會導致 redis 服務端程序占用過高內存,如果超過 maxmemory 限制,會觸發 KEY 的 LRU 淘汰或程序異常。

除此之外,redis 客戶端還有一個輸出緩沖區,用於緩存服務端響應的回復。

輸出緩沖區有兩種,一種是固定大小的,用於存儲服務端簡單的響應,例如:OK,錯誤信息等。還有一種是非固定長度的緩沖區,它的長度是可動態擴展的,用於存儲一些較長的響應內容。

typedef struct client {
    .........
    /* Response buffer */
    int bufpos;
    char buf[PROTO_REPLY_CHUNK_BYTES];
    .........
} client;

PROTO_REPLY_CHUNK_BYTES 等於 16*1024,也就是默認固定輸出緩沖區只有 16K,bufpos 記錄當前固定緩沖區已經使用的字節數。

typedef struct client {
    .........
    list *reply;            /* List of reply objects to send to the client. */
    .........
} client;

動態緩沖區用鏈表實現,可以為我們返回較大的 key,例如一些 set、list 集合等等。我們可以通過 client list 命令查看輸出緩沖區的使用情況。

image

obl 表示固定緩沖區長度,oll 代表動態緩沖區長度,omem 表示固定緩沖區和動態緩沖區總共占用了多少字節。

ps:輸出緩沖區可以通過配置 client-output-buffer-limit 限制最大內存上限,同樣如果濫用,一樣會導致 redis 服務器內存飆升,建議盡量配置小一點的輸出緩存區大小。

二、客戶端三種類型

redis 客戶端主要分為三種,普通客戶端、發布訂閱客戶端、slave 客戶端。普通客戶端我們不用多說,也是我們用的最多的客戶端。

redis 客戶端可以訂閱任意數量的頻道,如果你訂閱了某個服務器頻道,那么你的客戶端就也是一個發布訂閱客戶端,當頻道中有新消息,服務器會向你推送,關於 redis 的發布訂閱功能,我們后續會詳細介紹。

當 redis 集群中部署了主從節點的時候,所有的從節點服務器又稱為 slave 客戶端,他們會向 master 定期拉取最新數據,詳細的內容后續介紹。

下一篇我們分析 redis 的服務端程序實現,以及神秘的 serverCron 定時函數實現。


關注公眾不迷路,一個愛分享的程序員。

公眾號回復「1024」加作者微信一起探討學習!

每篇文章用到的所有案例代碼素材都會上傳我個人 github

https://github.com/SingleYam/overview_java

歡迎來踩!

YangAM 公眾號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM