目錄
- 前言
- 1. 命令請求的執行程序
- 1.1 發送命令請求
- 1.2 讀取命令請求
- 1.3 命令執行器(1):查找命令實作
- 1.4 命令執行器(2):執行預備操作
- 1.5 命令執行器(3):呼叫命令的實作函式
- 1.6 命令執行器(4):執行后續作業
- 1.7 將命令回復發送給客戶端
- 1.8 客戶端接收并列印命令回復
- 2. serverCron 函式
- 3. 初始化服務器
- 3.1 初始化服務器狀態結構
- 3.2 載入配置選項
- 3.3 初始化服務器資料結構
- 3.4 還原資料庫狀態
- 3.5 執行事件回圈
- 最后
前言
參考資料:《Redis設計與實作 第二版》;
第二部分為單機資料庫的實作,主要由以下模塊組成:資料庫、持久化、事件、客戶端與服務器;
本篇將介紹 Redis 的服務器端,從服務器接收客戶端的命令請求、serverCron 函式以及初始化服務器三個角度介紹;
與本章相關的 Redis 命令總結在下篇文章,歡迎點擊收藏,本篇將不再重復:
《Redis常用命令及示例總結(API)》:https://www.cnblogs.com/dlhjw/p/15639773.html
1. 命令請求的執行程序
1.1 發送命令請求
- 用戶在客戶端鍵入一個命令請求 { SET KEY VALUE };
- 客戶端將命令請求轉換成協議格式 { *3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n };
- 通過連接到服務器的套接字,將協議格式的命令請求發送給服務器;

1.2 讀取命令請求
- 讀取套接字中協議格式的命令請求,并將其保存到客戶端狀態的輸入緩沖區里面;
- 分析協議格式的命令請求,提取引數命令以及引數個數,存進客戶端狀態的
argv與argc屬性; - 呼叫命令執行器,執行客戶端指定命令;


1.3 命令執行器(1):查找命令實作
- 根據客戶端的
argv[0]引數,在命令表中查找引數所指定的命令,并將找到的命令redisCommand保存到客戶端狀態的cmd屬性里;


redisCommand的主要引數:name(名字),proc(函式指標),arity(命令引數個數),sflags(命令屬性標識),flags(sflags的二進制標識),calls(服務器總共執行了多少次這個命令),milliseconds(執行該命令的總耗時);

1.4 命令執行器(2):執行預備操作
-
檢查客戶端狀態的
cmd指標是否指向NULL,是則回傳錯誤; -
根據
cmd指向的redisCommand結構的arity屬性檢查引數個數是否正確,不正確則回傳錯誤; -
檢查客戶端是否通過身份驗證,未通過的只能執行 AUTH 命令;
-
若服務器打開了
maxmemory功能,則在執行命令之前先檢查服務器記憶體占用情況,在需要時回收記憶體,記憶體回收失敗回傳錯誤; -
等等……
1.5 命令執行器(3):呼叫命令的實作函式
- 執行以下陳述句:
client->cmd->proc(client); - 被呼叫的命令實作函式執行指定操作,回傳相應命令回復;


1.6 命令執行器(4):執行后續作業
- 如果服務器開啟了慢查詢功能,慢查詢日志模塊會檢查是否需要為剛剛執行完的命令請求添加一條新的慢查詢日志;
- 根據執行命令耗費時長更新
redisCommand結構的milliseconds屬性,同時將calls屬性加一; - 如果服務器開啟了 AOF 持久化功能,會將命令寫入 AOF 緩沖期里;
- 如果有從服務器正則復制當前服務器,服務器會將命令傳播給所有從服務器;
1.7 將命令回復發送給客戶端
-
當客戶端套接字變為可寫狀態時,服務器會執行命令回復處理器,將保存在客戶端輸出緩沖區中的命令回復發送給客戶端 {+OK\r\n};
-
當命令發送完畢后,回復處理器會清空客戶端狀態的輸出緩沖區,為處理下一個命令請求最準備;
1.8 客戶端接收并列印命令回復
- 客戶端接收協議格式的命令回復后,會將回復轉成易讀格式;

2. serverCron 函式
-
serverCron函式默認每隔 100ms 執行一次,負責管理服務器資源與保持服務器自身良好運轉; -
下面是
serverCron函式所做操作的介紹:-
更新服務器時間快取:更新服務器狀態的
unixtime(秒級) 屬性和mstime(毫秒級) 屬性,精度不高,用于:列印日志、更新 LRU 時鐘、決定是否執行持久化任務、服務器上線時間等; -
更新 LRU 時鐘:更新服務器狀態的
lruclock(10秒更新一次) 和lru屬性,前者用于計算鍵的空轉時間,后者保存了物件最后一次被命令訪問的時間,空轉時間=lruclock-lru; -
更新服務器每秒執行命令次數:呼叫
trackOperationsPerSecond函式,以抽樣計算的方式,估算并記錄服務器在最近 1s 處理的命令請求數量; -
更新服務器記憶體峰值函式:更新
stat_peak_memory屬性,該屬性記錄了服務器的記憶體峰值大小; -
處理 SIGTERM 信號:在啟動服務器時,Redis 會為服務器行程的 SIGTERM 信號關聯處理器 sigtermHandler 函式,信號處理器負責在服務器接到 SIGTERM 信號時,打開服務器狀態的 shutdown_asap 標識;
-
管理客戶端資源:呼叫
clientsCron函式,如果客戶端與服務器之間的連接已經超時,則釋放客戶端,如果客戶端輸入緩沖區大小超過一定長度,則釋放客戶端當前的輸入緩沖區,并創建一個默認大小的輸入緩沖區; -
管理資料庫資源:呼叫
databasesCron函式,洗掉過期鍵,對字典進行收縮操作; -
執行被延遲的 BGREWRITEAOF:檢查在執行 BGSAVE 命令期間,是否有 BGREWRITEAOF 命令被延遲執行;
-
檢查持久化操作的運行狀態:檢查
rdb_child_pid(記錄 BGSAVE 命令的子行程 ID) 屬性與aof_child_pid(記錄執行 BGREWRITEAOF 命令的子行程 ID) 屬性,值為 -1 說明服務器沒有進行持久化操作;
-

-
將 AOF 緩沖區中的內容寫入 AOF 檔案:如果服務開啟 AOF 功能,并且 AOF 緩沖區里有待寫入資料,則將 AOF 緩沖區中的內容寫入 AOF 檔案里;
-
關閉異步客戶端:關閉輸出緩沖區大小超過限制的客戶端;
-
增加 cronloops 計數器的值:對服務器狀態的
cronloops屬性增 1;
3. 初始化服務器
3.1 初始化服務器狀態結構
- 即創建使用默認值一個
struct redisServer結構體; - 負責初始化一般屬性;
- 初始化的作業由
redis.c/initServerConfig函式完成,該函式的主要作業有:- 設定服務器的運行 ID;
- 設定服務器的默認運行頻率;
- 設定服務器的默認組態檔路徑;
- 設定服務器的運行架構;
- 設定服務器的默認埠號;
- 設定服務器的默認 RDB 持久化條件和 AOF 持久化條件;
- 初始化服務器的 LRU 時鐘;
- 創建命令表;
3.2 載入配置選項
- 載入用戶給定的配置引數和組態檔,并對
server變數相關屬性的值進行修改; - 指定埠號:
redis-server --port 10086; - 修改資料庫數量與 RDB 檔案壓縮功能:
redis-server redis.conf,并且redis.conf檔案里包含以下內容:# 將資料庫數量設定為32個 database 32 # 關閉 RDB 檔案的壓縮功能 rdbcompression no
3.3 初始化服務器資料結構
-
負責初始化資料結構;
- 呼叫
initServer函式,初始化下列資料庫: - 設定資料庫:
server.clients鏈表(存客戶端)、server.db陣列(存資料庫)、server.pubsub_channels字典(保存頻道訂閱資訊)、server.lua(用于執行 Lua 腳本的 Lua 環境)、server.slowlog(用于保存慢查詢日志)
- 呼叫
-
進行一些重要設定:
- 為服務器設定行程信號處理器;
- 創建共享物件(如 OK、整數 0-9999);
- 打開服務器的監聽埠,為監聽套接字關聯連接應答事件處理器,等待服務器正式運行時接受客戶端的連接;
- 為
serverCron函式創建時間事件; - 當 AOF 持久化功能打開時,打開現有 AOF 檔案或創建并打開一個新的 AOF 檔案,為 AOF 寫入做準備;
- 初始化服務器的后臺 I/O 模塊,為將來 I/O 操作做準備;
3.4 還原資料庫狀態
- 載入 RDB 檔案或 AOF 檔案(優先),并根據檔案記錄的內容還原服務器的資料庫狀態;
- 成功還原的日志資訊:
[8040] 01 Dec 20:12:41.758 * DB loaded from disk: 0.001 seconds
3.5 執行事件回圈
- 在列印下列日志后執行事件回圈(loop函式);
[8040] 01 Dec 20:12:41.758 * The server is now ready to accept connections on port 6379;
最后

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/374455.html
標籤:其他
