redis--服務器與客戶端


初始化服務器

從啟動 Redis 服務器,到服務器可以接受外來客戶端的網絡連接這段時間,Redis 需要執行一系列初始化操作。
整個初始化過程可以分為以下六個步驟:
  •  初始化服務器全局狀態。
  •  載入配置文件。
  •  創建 daemon 進程。
  •  初始化服務器功能模塊。
  •  載入數據。
  •  開始事件循環
 
初始化服務器全局狀態
 
redis.h/redisServer結構記錄了和服務器相關的所有數據,這個結構主要包含以下信息:
  • 服務器中的所有數據庫。
  • 命令表:
    • 在執行命令時,根據字符來查找相應命令的實現函數。
  • 事件狀態。
  • 服務器的網絡連接信息:
    • 套接字地址
    • 端口,以及套接字描述符。
  • 所有已連接客戶端的信息。
  • Lua腳本的運行環境及相關選項。
  • 實現訂閱與發布(pub/sub)功能所需的數據結構。
  • 日志(log)和慢查詢日志(slowlog)的選項和相關信息。
  • 數據持久化(AOF和RDB)的配置和狀態。
  • 服務器配置選項:
    • 比如要創建多少個數據庫,
    • 是否將服務器進程作為daemon進程來運行,
    • 最大連接多少個客戶端,
    • 壓縮結構(zip structure)的實體數量,等等。
  • 統計信息:比如鍵有多少次命令、不命中,服務器的運行時間,內存占用,等等。
 
上述過程只包含=Redis服務器單機信息,不包含sentinel,cluster等功能。
當 server 變量的初始化完成之后,程序進入服務器初始化的下一步:讀入配置文件。
 
載入配置文件
 
在初始化服務器的上一步中,程序為server變量(也即是服務器狀態)的各個屬性設置了默
認值,但這些默認值有時候並不是最合適的:
  • 用戶可能想使用 AOF 持久化,而不是默認的 RDB 持久化。
  • 用戶可能想用其他端口來運行Redis,以避免端口沖突。
  • 用戶可能不想使用默認的16個數據庫,而是分配更多或更少數量的數據庫。
  • 用戶可能想對默認的內存限制措施和回收策略做調整。
 
創建daemon進程
 
Redis默認以daemon進程的方式運行。
當服務器初始化進行到這一步時,程序將創建 daemon 進程來運行 Redis ,並創建相應的 pid文件。
 
初始化服務器功能模塊
 
在這一步,初始化程序完成兩件事:
  • 為server變量的數據結構子屬性分配內存。
    • 為數據結構分配內存,並初始化這些數據結構,等同於對相應的功能進行初始化
    • 比如說,當為訂閱與發布所需的鏈表分配內存之后,訂閱與發布功能就處於就緒狀態,隨時可以為 Redis 所用了。
  • 初始化這些數據結構。
 
在這一步,程序完成的主要動作如下:
  • 初始化 Redis 進程的信號功能。
  • 初始化日志功能。
  • 初始化客戶端功能。
  • 初始化共享對象。
  • 初始化事件功能。
  • 初始化數據庫。
  • 初始化網絡連接。
  • 初始化訂閱與發布功能。
  • 初始化各個統計變量。
  • 關聯服務器常規操作(cron job)到時間事件,關聯客戶端應答處理器到文件事件。
  • 如果AOF功能已打開,那么打開或創建AOF文件。
  • 設置內存限制。
  • 初始化Lua腳本環境。
  • 初始化慢查詢功能。
  • 初始化后台操作線程。
 
完成這一步之后,服務器打印出 Redis 的 ASCII LOGO 、服務器版本等信息,表示所有功能
模塊已經就緒,可以等待被使用了:
雖然所有功能已經就緒,但這時服務器的數據庫還是一片空白,程序還需要將服務器上一次執
行時記錄的數據載入到當前服務器中,服務器的初始化才算真正完成。
 
載入數據
在這一步,程序需要將持久化在 RDB 或者 AOF 文件里的數據,載入到服務器進程里面。
 
如果服務器有啟用AOF功能的話,那么使用AOF文件來還原數據;
否則,程序使用RDB文件來還原數據。
 
當執行完這一步時,服務器打印出一段載入完成信息:
[6717] 22 Feb 11:59:14.830 * DB loaded from disk: 0.068 seconds
 
開始事件循環
到了這一步,服務器的初始化已經完成,程序打開事件循環,開始接受客戶端連接。
 
以下是初始化完成之后,服務器狀態和各個模塊之間的關系圖:

 

 
客戶端連接到服務器

當 Redis 服務器完成初始化之后,它就准備好可以接受外來客戶端的連接了。
 
當一個客戶端通過套接字函數 connect 到服務器時,服務器執行以下步驟:
 
  • 服務器通過文件事件無阻塞地 accept 客戶端連接,並返回一個套接字描述符 fd 。
  • 服務器為 fd 創建一個對應的 redis.h/redisClient 結構實例,並將該實例加入到服務器的已連接客戶端的鏈表中。
  • 服務器在事件處理器為該 fd 關聯讀文件事件。
 
完成這三步之后,服務器就可以等待客戶端發來命令請求了。
 
Redis 以多路復用的方式來處理多個客戶端,為了讓多個客戶端之間獨立分開、不互相干擾,
服務器為每個已連接客戶端維持一個 redisClient 結構,從而單獨保存該客戶端的狀態信息。
 
redisClient 結構主要包含以下信息:
  • 套接字描述符。
  • 客戶端正在使用的數據庫指針和數據庫號碼。
  • 客戶端的查詢緩存( query buffer)和回復緩存( reply buffer)。
  • 一個指向命令函數的指針,以及字符串形式的命令、命令參數和命令個數,這些屬性會在命令執行時使用。
  • 客戶端狀態:
    • 記錄了客戶端是否處於
    • SLAVE 
    • MONITOR
    • 或者事務狀態。
  • 實現事務功能(比如 MULTI 和 WATCH)所需的數據結構。
  • 實現阻塞功能(比如 BLPOP 和 BRPOPLPUSH)所需的數據結構。
  • 實現訂閱與發布功能(比如PUBLISH和SUBSCRIBE)所需的數據結構。
  • 統計數據和選項:客戶端創建的時間,客戶端和服務器最后交互的時間,緩存的大小,等等。
 
注意:上面列出的客戶端結構信息不包含復制相關屬性;
 
命令的請求,處理和結果返回
當客戶端連上服務器之后,客戶端就可以向服務器發送命令請求了。
從客戶端發送命令請求,到命令被服務器處理、並將結果返回客戶端,整個過程有以下步驟:
  • 客戶端通過套接字向服務器傳送命令協議數據。
  • 服務器通過讀事件來處理傳入數據,並將數據保存在客戶端對應 redisClient 結構的查詢緩存中。
  • 根據客戶端查詢緩存中的內容,程序從命令表中查找相應命令的實現函數。
  • 程序執行命令的實現函數,修改服務器的全局狀態 server 變量,並將命令的執行結果保存到客戶端 redisClient 結構的回復緩存中,然后為該客戶端的 fd 關聯寫事件。
  • 當客戶端 fd 的寫事件就緒時,將回復緩存中的命令結果傳回給客戶端。至此,命令執行完畢。
 
 
命令請求實例: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 對應的寫事件就緒時,程序就會將保存在客戶端結構回復緩存中的數據回寫給客戶端
  • 當客戶端接收到數據之后,它就將結果打印出來,顯示給用戶看
 
小結
• 服務器經過初始化之后,才能開始接受命令。
• 服務器初始化可以分為六個步驟:
  1. 初始化服務器全局狀態。
  2. 載入配置文件。
  3. 創建 daemon 進程。
  4. 初始化服務器功能模塊。
  5. 載入數據。
  6. 開始事件循環。
• 服務器為每個已連接的客戶端維持一個客戶端結構,這個結構保存了這個客戶端的所有
狀態信息。
• 客戶端向服務器發送命令,服務器接受命令然后將命令傳給命令執行器,執行器執行給
定命令的實現函數,執行完成之后,將結果保存在緩存,最后回傳給客戶端。


免責聲明!

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



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