Redis服務器負責與多個客戶端建立網路通信,處理客戶端發送的命令請求,在資料庫中保存客戶端執行命令所產生的資料,并通過資源管理來維持服務器自身的運轉,
命令請求程序(以set命令為例)
1、客戶端向服務器發送命令請求 SET KEY VALUE,
Redis服務器的命令請求來自于Redis客戶端,當用戶從客戶端鍵入一個命令請求時,客戶端會將這個命令命令請求請求轉換成協議格式,然后通過連接到服務器的套接字,將協議格式的命令請求發送給服務器,

2、服務器接收并處理客戶端發送來的命令請求 SET KEY VALUE,在資料庫中進行設定操作,并產生命令回復OK,
讀取套接字中的協議格式命令請求,并將其保存在客戶端狀態的輸入緩沖區中,
對輸入緩沖區中的命令請求進行決議,提取出命令請求中包含的命令引數,以及命令引數的格式,分別將引數和引數個數保存到客戶端狀態的argv和argc屬性中
呼叫命令執行器,執行客戶端指定的命令,
命令執行器:
1、查找命令,2、執行預備操作,(檢查命令、命令引數、客戶端身份驗證、檢查服務器記憶體占用情況、事務、服務器狀態、是否監聽等)3、呼叫命令實作函式,產生命令回復函式,保存在客戶端狀態的輸出快取區4、執行后續作業:(慢查詢檢查記錄日志;根據命令耗時更新redisCommand結構的milliseconds屬性、calls計數器加1;如果開啟了AOF,如果是寫命令則寫入到AOF緩沖區;如果有其他從服務器正在復制當前的服務器,那么服務器會將剛剛執行的命令傳播給所有從服務器,)
3、服務器將命令回復OK發送為客戶端,
當客戶端套接字變為可寫狀態時,服務器會執行命令回復處理器,將保存在客戶端緩沖區的命令回復發送給客戶端,
4、客戶端接收服務器回傳的命令回復OK,并將這個回復列印給用戶觀看,客戶端收到協議格式的命令回復后,將其轉換為人類可讀的格式,并列印,

serverCron函式
Redis服務器中的serverCron函式每100毫秒執行一次,這個函式負責管理服務器的資源,并保持服務器自身的良好運轉,
1、更新服務器快取時間,為減少系統呼叫獲取當前時間的次數,服務器狀態中的unixtime和mstime屬性被用作當前時間的快取:
struct redisServer{ //保存了秒級精度的當前unix時間戳 time_t unixtime; //保存了毫秒級精度的系統當前unix時間戳 long long mstime; //默認每10秒更新一次事件快取,用于計算鍵的空轉時間, unsigned lruclock:22 }
服務器只會在列印日志、更新服務器LRU始終、決定是否執行持久化任務、計算服務器上線時間這類對事件精度不高的功能上用;對于為鍵設定過期事件、添加慢日志這種需要高精度事件的功能來說,服務器還是會再次執行系統呼叫,從而獲得更準確的系統當前事件,
每個Redis物件都有一個lru屬性,這個lru屬性保存了物件最后一次被命令訪問的時間:
typedef struct redisObject{ unsigned lru:22' } robj;
serverCron函式中的trackOperationsPerSecond函式會以每100毫秒一次的頻率執行,這個函式的功能以抽樣計算的方式,估算并記錄服務器在最近一秒鐘處理的命令請求數量,這個值通過INFO status命令的instantaneous_ops_per_sec域查看,
2、更新服務器記憶體峰值記錄:服務器狀態中的stat_peak_memory屬性記錄服務器的記憶體峰值大小:
struct redisServer{ //已使用記憶體峰值 size_t stat_peak_memory; }
3、處理sigterm信號
static void sigtermHandler(int sig){ //列印日志 redisLogFromHandler(REDIS_WARNING,"received sigterm,scheduling shutdown..."); //打開關閉標識 server.shutdown_asap=1; }
struct redisServer{ //serverCron函式運行時,程式灰度服務器狀態的shutdown_asap屬性進行檢查,并根據屬性值決定是否關閉服務器, //關閉服務器的標識 //值為1時,關閉服務器 //值為0時,不做動作 int shutdown_asap; }
4、管理客戶端資源
serverCron函式每次執行都會呼叫clientCron函式,clientsCron函式會對一定數量的客戶端進行檢查:如果客戶端與服務器之間的連接已經超時,那么程式釋放這個客戶端;如果客戶端在上一次執行命令請求后,輸入緩沖區的大小超過了一定的長度,那么程式會釋放客戶端當前的輸入緩沖區,重新創建一個迷人大小的輸入緩沖區,防止客戶端的輸入緩沖區耗費過多的資源,
5、管理資料庫資源 :洗掉過期鍵,如有需要,對字典進行收縮操作,
6、執行被延遲的BGREWRITEAOF
如果BGSAVE執行期間,客戶端發來BGREWRITEAOF命令,則需要延遲到BGSAVE命令執行完成后,
7、檢查持久化操作運行狀態
服務器狀態使用rdb_child_pid\aof_child_pid屬性記錄執行BGSAVe命令和BGREWRITEAOF命令的子行程ID,用于檢查命令是否正在執行
struct redisServer{ //記錄執行BGSAVE命令的紫禁城,如果沒有執行則為-1 pid_t rdb_child_pid; //記錄執行BGREWRITEAOF命令的子行程ID,沒執行則為-1 pid_t aof_child_pid; }

8、將AOF緩沖區中的內容寫入AOF檔案
9、關閉異步客戶端(檢查輸出緩沖區大小)
10、增加cronloops計數器的值(沒執行serverCron函式N次就執行一次指定的代碼)
初始化服務器
1、初始化服務器狀態結構,創建一個struct redisServer型別的實體變數server作為服務器的狀態,
initServerConfig函式完成的主要作業:
設定服務器運行ID
設定服務器的默認運行頻率
設定服務器的默認組態檔路徑
設定服務器的運行架構
設定服務器的默認埠號
設定服務器默認RDB持久化條件和APF持久化條件
初始化服務器LRU時鐘
創建命令表
2、載入配置項修改默認的配置,
3、初始化服務器資料結構
在執行initServerConfig函式初始化server狀態時,程式只創建命令表一個資料結構,服務器在次數初始化其他資料結構:
server.clients鏈表,記錄所有與服務器相連接的客戶端狀態
erver.db陣列,包含服務器的所有資料庫,
保存頻道訂閱資訊的server.pubsub_channels字典以及保存模式訂閱資訊的server_publsub_patterns鏈表,
執行Lua腳本的Lua環境 server.lua;
用于保存慢查詢日志的server.slwlog鏈表
除了初始化資料結構之外,initServer還進行了一些非常重要的設定操作:
為服務器設定行程信號處理器,
創建共享物件,
打開服務器的監聽端,并未監聽套接字關聯連接應答事件處理時,等待服務器正式運行時接收客戶端連接,
為serverCron函式創建時間事件,等待服務器正式運行時,執行serverCron函式,
如果AOF持久化功能打開,那么打開現有的AOF檔案,如果檔案不存在則創建新的AOF檔案
初始化服務器的后臺I/O模塊,為將來的I/O操作做準備,
執行完以上內容那么你就看到了熟悉的畫面:

4、還原資料庫裝填
在完成對服務器狀態server變數的初始化后,服務器需要載入RDB檔案或者AOF檔案,并根據檔案記錄的內容還原資料庫狀態
5、執行事件回圈
到此服務器的初始化作業圓滿完成,服務器現在開始可以接收客戶端的連接請求,并處理客戶端發來的命令請求,
寫到這里,非常有沖動寫一下.net 程式初始化以及運行程序服務端做了那些事情,著手準備!
每天學一點,總會有識訓,
說明:尊重作者知識產權,文中內容參考《Redis設計與實作》,僅在此做學習與大家分享,

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/2463.html
標籤:NoSQL
上一篇:Redis學習筆記(十) 客戶端
下一篇:Cassandra配置JMX
