1. 程式人生 > >Redis---客戶端和服務端

Redis---客戶端和服務端

Redis---客戶端和服務端

 

文章轉載自:http://redisbook.readthedocs.io/en/latest/internal/redis.html

http://www.spongeliu.com/category/linux

https://blog.csdn.net/guodongxiaren/article/details/44747719

https://www.cnblogs.com/gqtcgq/p/7247057.html

 

1.初始化伺服器

從啟動 Redis 伺服器,到伺服器可以接受外來客戶端的網路連線這段時間,Redis 需要執行一系列初始化操作。
整個初始化過程可以分為以下六個步驟:

  1. 初始化伺服器全域性狀態。
  2. 載入配置檔案。
  3. 建立 daemon 程序。
  4. 初始化伺服器功能模組。
  5. 載入資料。
  6. 開始事件迴圈

1.1.初始化伺服器全域性狀態

redis.h/redisServer結構記錄了和伺服器相關的所有資料,這個結構主要包含以下資訊:

  • 伺服器中的所有資料庫。
  • 命令表:
    • 在執行命令時,根據字元來查詢相應命令的實現函式。
  • 事件狀態。
  • 伺服器的網路連線資訊:
    • 套接字地址
    • 埠,以及套接字描述符。
  • 所有已連線客戶端的資訊。
  • Lua指令碼的執行環境及相關選項。
  • 實現訂閱與釋出(pub/sub)功能所需的資料結構。
  • 日誌(log)和慢查詢日誌(slowlog)的選項和相關資訊。
  • 資料持久化(AOF和RDB)的配置和狀態。
  • 伺服器配置選項:
    • 比如要建立多少個數據庫,
    • 是否將伺服器程序作為daemon程序來執行,
    • 最大連線多少個客戶端,
    • 壓縮結構(zip structure)的實體數量,等等。
  • 統計資訊:比如鍵有多少次命令、不命中,伺服器的執行時間,記憶體佔用,等等。

上述過程只包含=Redis伺服器單機資訊,不包含sentinel,cluster等功能。
當 server 變數的初始化完成之後,程式進入伺服器初始化的下一步:讀入配置檔案。

1.2載入配置檔案

在初始化伺服器的上一步中,程式為server變數(也即是伺服器狀態)的各個屬性設定了默
認值,但這些預設值有時候並不是最合適的:
- 使用者可能想使用 AOF 持久化,而不是預設的 RDB 持久化。
- 使用者可能想用其他埠來執行Redis,以避免埠衝突。
- 使用者可能不想使用預設的16個數據庫,而是分配更多或更少數量的資料庫。
- 使用者可能想對預設的記憶體限制措施和回收策略做調整。

1.建立daemon程序

Redis預設以daemon程序的方式執行。
當伺服器初始化進行到這一步時,程式將建立 daemon 程序來執行 Redis ,並建立相應的 pid檔案。

2.初始化伺服器功能模組

在這一步,初始化程式完成兩件事:

  • 為server變數的資料結構子屬性分配記憶體。
    • 為資料結構分配記憶體,並初始化這些資料結構,等同於對相應的功能進行初始化
    • 比如說,當為訂閱與釋出所需的連結串列分配記憶體之後,訂閱與釋出功能就處於就緒狀態,隨時可以為 Redis 所用了。
  • 初始化這些資料結構。

在這一步,程式完成的主要動作如下:

  • 初始化 Redis 程序的訊號功能。
  • 初始化日誌功能。
  • 初始化客戶端功能。
  • 初始化共享物件。
  • 初始化事件功能。
  • 初始化資料庫。
  • 初始化網路連線。
  • 初始化訂閱與釋出功能。
  • 初始化各個統計變數。
  • 關聯伺服器常規操作(cron job)到時間事件,關聯客戶端應答處理器到檔案事件。
  • 如果AOF功能已開啟,那麼開啟或建立AOF檔案。
  • 設定記憶體限制。
  • 初始化Lua指令碼環境。
  • 初始化慢查詢功能。
  • 初始化後臺操作執行緒。

完成這一步之後,伺服器打印出 Redis 的 ASCII LOGO 、伺服器版本等資訊,表示所有功能
模組已經就緒,可以等待被使用了:
雖然所有功能已經就緒,但這時伺服器的資料庫還是一片空白,程式還需要將伺服器上一次執
行時記錄的資料載入到當前伺服器中,伺服器的初始化才算真正完成。

3.載入資料

在這一步,程式需要將持久化在 RDB 或者 AOF 檔案裡的資料,載入到伺服器程序裡面。

如果伺服器有啟用AOF功能的話,那麼使用AOF檔案來還原資料;
否則,程式使用RDB檔案來還原資料。

當執行完這一步時,伺服器打印出一段載入完成資訊:
[6717] 22 Feb 11:59:14.830 * DB loaded from disk: 0.068 seconds

4.開始事件迴圈

到了這一步,伺服器的初始化已經完成,程式開啟事件迴圈,開始接受客戶端連線。

以下是初始化完成之後,伺服器狀態和各個模組之間的關係圖:

這裡寫圖片描述

5.命令的請求,處理和結果返回

當客戶端連上伺服器之後,客戶端就可以向伺服器傳送命令請求了。
從客戶端傳送命令請求,到命令被伺服器處理、並將結果返回客戶端,整個過程有以下步驟:

  • 客戶端通過套接字向伺服器傳送命令協議資料。
  • 伺服器通過讀事件來處理傳入資料,並將資料儲存在客戶端對應 redisClient 結構的查詢快取中。
  • 根據客戶端查詢快取中的內容,程式從命令表中查詢相應命令的實現函式。
  • 程式執行命令的實現函式,修改伺服器的全域性狀態 server 變數,並將命令的執行結果儲存到客戶端 redisClient 結構的回覆快取中,然後為該客戶端的 fd 關聯寫事件。
  • 當客戶端 fd 的寫事件就緒時,將回復快取中的命令結果傳回給客戶端。至此,命令執行完畢。

6.命令請求例項:set 的執行過程

假設現在客戶端 C1 是連線到伺服器 S 的一個客戶端;
當用戶執行命令 SET YEAR 2013 時 過程:

  • 客戶端呼叫寫入函式,將協議內容 寫入連線到伺服器的套接字中。
    • 協議內容:*3\r\n$3\r\nSET\r\n$4\r\nYEAR\r\n$4\r\n2013\r\n"
  • 當S的檔案事件處理器執行時, 它會察覺到 C1 所對應的讀事件已經就緒,於是它將協議文字讀入,並儲存在查詢快取。
  • 通過對查詢快取進行分析( parse)
    • 伺服器在命令表中查詢 SET 字串所對應的命令實現函式,最終定位到 t_string.c/setCommand 函式,
    • 另外,兩個命令引數 YEAR 和 2013 也會以字串的形式儲存在客戶端結構中。
  • 接著,程式將客戶端、要執行的命令、命令引數等送入命令執行器:
    • 執行器呼叫 setCommand函式,將資料庫中 YEAR 鍵的值修改為 2013
    • 然後將命令的執行結果儲存在客戶端的回覆快取中
    • 併為客戶端 fd 關聯寫事件,用於將結果回寫給客戶端
    • 因為 YEAR 鍵的修改,其他和資料庫名稱空間相關程式
      • 比如 AOF 、 REPLICATION 還有事務安全性檢查(是否修改了被 WATCH 監視的鍵?)也會被觸發
  • 當這些後續程式也執行完畢之後,命令執行器退出,伺服器其他程式(比如時間事件處理器)繼續執行。
  • 當 C1 對應的寫事件就緒時,程式就會將儲存在客戶端結構回覆快取中的資料回寫給客戶端
  • 當客戶端接收到資料之後,它就將結果打印出來,顯示給使用者看

2.客戶端

redis 伺服器是典型的一對多伺服器程式:一個伺服器可以和多個客戶端建立網路連線,每個客戶端可以向伺服器傳送命令請求,而伺服器則接收並處理客戶端傳送的請求,並向客戶端返回相應的資訊。

redis 伺服器通過使用 I/O 多路複用技術實現的檔案處理系統,Redis 伺服器使用單執行緒單程序的方式處理命令請求,並與多個客戶端進行網路通訊。

對於每一個與伺服器進行連線的客戶端,伺服器都為這些客戶端建立相應的 redis.h/redisClient 結構,這個結構儲存了客戶端當前的狀態資訊,以及執行相關功能時需要用到的資料結構。

Redis 通過監聽一個 TCP 埠或者 Unix socket 的方式來接收來自客戶端的連線,當一個連線建立後,Redis 內部會進行以下一些操作:
首先,客戶端 socket 會被設定為非阻塞模式,因為 Redis 在網路事件處理上採用的是非阻塞多路複用模型。
然後為這個 socket 設定 TCP_NODELAY 屬性,禁用 Nagle 演算法
然後建立一個可讀的檔案事件用於監聽這個客戶端 socket 的資料傳送

2.1.客戶端連線到伺服器

當 Redis 伺服器完成初始化之後,它就準備好可以接受外來客戶端的連線了。

當一個客戶端通過套接字函式 connect 到伺服器時,伺服器執行以下步驟:

  • 伺服器通過檔案事件無阻塞地 accept 客戶端連線,並返回一個套接字描述符 fd 。
  • 伺服器為 fd 建立一個對應的 redis.h/redisClient 結構例項,並將該例項加入到伺服器的已連線客戶端的連結串列中。
  • 伺服器在事件處理器為該 fd 關聯讀檔案事件。

完成這三步之後,伺服器就可以等待客戶端發來命令請求了。

Redis 以多路複用的方式來處理多個客戶端,為了讓多個客戶端之間獨立分開、不互相干擾,
伺服器為每個已連線客戶端維持一個 redisClient 結構,從而單獨儲存該客戶端的狀態資訊。

redisClient 結構主要包含以下資訊:

  • 套接字描述符。
  • 客戶端正在使用的資料庫指標和資料庫號碼。
  • 客戶端的查詢快取( query buffer)和回覆快取( reply buffer)。
  • 一個指向命令函式的指標,以及字串形式的命令、命令引數和命令個數,這些屬性會在命令執行時使用。
  • 客戶端狀態:
    • 記錄了客戶端是否處於
    • SLAVE
    • MONITOR
    • 或者事務狀態。
  • 實現事務功能(比如 MULTI 和 WATCH)所需的資料結構。
  • 實現阻塞功能(比如 BLPOP 和 BRPOPLPUSH)所需的資料結構。
  • 實現訂閱與釋出功能(比如PUBLISH和SUBSCRIBE)所需的資料結構。
  • 統計資料和選項:客戶端建立的時間,客戶端和伺服器最後互動的時間,快取的大小,等等。

注意:上面列出的客戶端結構資訊不包含複製相關屬性;

3.小結

• 伺服器經過初始化之後,才能開始接受命令。
• 伺服器初始化可以分為六個步驟:
  1. 初始化伺服器全域性狀態。
  2. 載入配置檔案。
  3. 建立 daemon 程序。
  4. 初始化伺服器功能模組。
  5. 載入資料。
  6. 開始事件迴圈。
• 伺服器為每個已連線的客戶端維持一個客戶端結構,這個結構儲存了這個客戶端的所有
狀態資訊。
• 客戶端向伺服器傳送命令,伺服器接受命令然後將命令傳給命令執行器,執行器執行給
定命令的實現函式,執行完成之後,將結果儲存在快取,最後回傳給客戶端。