二. serverCron函數 2.3 更新伺服器每秒執行命令次數 serverCron函數中的trackOperationsPerSecond函數會以每100毫秒一次的頻率執行,這個函數以抽樣計算的方式,估算並記錄伺服器在最近一秒鐘處理的命令請求數量,這個值可以通過info status命令的in ...
二. serverCron函數
2.3 更新伺服器每秒執行命令次數
serverCron函數中的trackOperationsPerSecond函數會以每100毫秒一次的頻率執行,這個函數以抽樣計算的方式,估算並記錄伺服器在最近一秒鐘處理的命令請求數量,這個值可以通過info status命令的instantaneous_ops_sec域查看:
127.0.0.1:6379> info stats # Stats total_connections_received:5 total_commands_processed:67 //伺服器在最近一秒鐘處理的命令請求數量 instantaneous_ops_per_sec:0
關於trackOperationsPerSecond函數的內部實現就不再介紹,大致是當客戶端執行INFO命令時,伺服器就會調用getOperationsPerSecond函數,根據ops_sec_samples環形數組中的抽樣結果,計算出instantaneous_ops_per_sec屬性的值。
2.4 更新伺服器記憶體
伺服器狀態redisServer結構的stat_peak_memory屬性記錄了伺服器的記憶體峰值大小,每次serverCron函數執行時,程式都會查看伺服器當前使用的記憶體數量,並與stat_peak_memory保存的數值進行比較,如果當前的記憶體數量比stat_peak_memory屬性記錄的值要大,那麼程式就將當前使用的記憶體數量記錄到stat_peak_memory屬性記錄中。INFO memory命令的used_memory_peak和used_memory_peak_human兩個域分別以兩種格式記錄了伺服器的記憶體峰值。
127.0.0.1:6379> info memory # Memory used_memory_peak:858952 used_memory_peak_human:838.82K
這兩個是域是指過去Redis記憶體使用的峰值,是反映了記憶體過去最高峰值,而不是當前使用記憶體的值。
2.5 處理sigterm信號
在啟動伺服器時,redis會為伺服器進程的singterm信號關聯處理器singtermHandler函數,這個信息處理器負責在伺服器接收到singterm信號時,打開伺服器狀態的shutdown_asap標識。 每次serverCron函數運行時,程式都會對伺服器狀態redisServer結構函數的shutdown_asap屬性進行檢查,並根據屬性的值決定是否關閉伺服器。
static void sigtermHandler(int sig){ //列印日誌 redisLogFromHandler(reids_warning,"received sigterm, scheduling shutdown..."); //打開關閉標識 server.shutdown_asap=1; }
//下麵先打開日誌功能,在redis.conf文件中設置日誌文件路徑,保存配置文件,重啟服務。 171 logfile /usr/local/redis/redis.log [root@xuegod64 redis]# redis-server redis.conf //當在客戶端執行shutdown關閉伺服器時,伺服器在接到sigterm信號之後,關閉伺服器並列印相關日誌。 127.0.0.1:6379> shutdown not connected>
日誌顯示可以看到,伺服器在關閉自身之前會進行RDB持久化操作,這也是伺服器攔截sigterm信號的原因,日誌信息如下:
43:M 06 Dec 11:07:42.562 # User requested shutdown... 42343:M 06 Dec 11:07:42.562 * Saving the final RDB snapshot before exiting. 42343:M 06 Dec 11:07:42.565 * DB saved on disk 42343:M 06 Dec 11:07:42.565 * Removing the pid file. 42343:M 06 Dec 11:07:42.565 # Redis is now ready to exit, bye bye...
2.6 管理客戶端資源
serverCron函數每次執行都會調用clinetsCron函數,該函數會對一定數量的客戶端進行檢查,包括1.如果客戶端與伺服器的連接已經超時(如很長時間沒有互動),那麼程式會釋放這個客戶端。2.如果客戶端在上一次執行命令請求之後,輸入緩衝區的大小超過了一定的長度,那麼程式會釋放客戶端當前的輸入緩衝區。
2.7 執行被延遲的bgrewriteaof
說到bgsave和bgrewriteaof二個客戶端命令,前者針對RDB持久化,後者針對AOF 持久化。在伺服器執行bgsave命令的期間,如果客戶端向伺服器發送了bgrewriteaof命令,那麼伺服器會將bgrewriteaof命令的執行時間延遲到bgsave命令執行完畢之後。每次serverCron函數執行時,都會檢查bgsave命令或者bgrewriteaof命令是否正在執行,如果這兩個命令都沒在執行,並且aof_rewrite_scheduled屬性的值為1,那麼伺服器就會執行之前被推延的bgrewriteaof命令。
struct redisServer { //如果值為1, 那麼表示有bgrewriteaof命令被延遲了 int aof_rewrite_scheduled; }
2.8 檢查持久化操作的運作狀態
伺服器狀態使用redisServer結構的rdb_child_pid屬性和aof_child_pid屬性記錄執行bgsave命令和bgrewriteaof命令的子進程的ID,這兩個屬性也可以用於檢查bgsave命令或者bgrewriteaof命令是否正在執行。
struct redisServer { //記錄執行bgsave命令的子進程的ID //如果伺服器沒有在執行bgsave,那麼這個屬性的值為-1. pid_t rdb_chlid_pid; //記錄執行bgrewriteaof命令的子進程的ID //如果伺服器沒有在執行bgrewriteaof,那麼這個屬性的值為-1. pid_t aof_chlid_pid; }
每次serverCron函數執行時,程式都會檢查rdb_chlid_pid和aof_child_pid兩個屬性值,只要其中一個屬性的值不為-1, 代表正在執行,程式就會執行一次wait3函數,檢查子進程是否有信號發給伺服器主進程。如果有信號到達:那麼對於bgsave命令來說,新的rdb文件已經生成; 對於bbgrewriteaof命令來說,AOF文件已經重寫完成; 伺服器需要進行相應命令的後續操作,比如用新的RDB文件替換現有RDB文件,或者用重寫後的AOF文件替換現有的AOF文件。如果沒有信號到達,那麼表示持久化操作未完成,程式不做動作。
三.初始化伺服器
一個Redis伺服器啟動後,需要經過一系列的初始化和設置過程。比如(3.1)初始化伺服器狀態;(3.2)接受用戶指定的伺服器配置;(3.3)創建相應的數據結構和網路連接等。
3.1 初始化伺服器狀態結構
初始化伺服器第一步就是創建一個struct redisServer類型的實例變數server作為伺服器狀態,併為結構中的各個屬性設置預設值。初始化server變數的工作由redis.c/initServerConfig函數完成。主要包括:設置伺服器運行ID(info 命令信息中的run_id);設置伺服器的預設運行頻率(hz,在18 事件篇中有講);設置伺服器的預設文件路徑(info 命令信息中的config_file) ;設置伺服器運行架構(info 命令信息中的arch_bits) ;設置伺服器的預設埠號(info 命令信息中的tcp_port) ;設置伺服器的預設RDB持久化條件和AOF持久化條件;初始化伺服器的LRU時鐘;創建命令表。 該initServerConfig函數沒有創建伺服器狀態的其他數據結構;資料庫;慢查詢日誌;LUA環境;共用對象,這些數據結構在之後的步驟才會被創建出來
127.0.0.1:6379> info # Server redis_version:4.0.6 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:836bbbcc913c7a57 redis_mode:standalone os:Linux 3.10.0-693.el7.x86_64 x86_64 arch_bits:64 multiplexing_api:epoll atomicvar_api:atomic-builtin gcc_version:4.8.5 process_id:58994 run_id:b0694f86c7e794bf851f6193f1f96d85f993f325 tcp_port:6379 uptime_in_seconds:20 uptime_in_days:0 hz:10 lru_clock:659411 executable:/usr/local/redis/redis-server config_file:/usr/local/redis/redis.conf
3.2 載入配置選項
在啟動伺服器時,用戶可以通過給定配置參數或者指定配置文件來修改伺服器的預設配置,當伺服器在用initServerConfig函數初始化完server變數之後,就會開始載入用戶給定的配置參數和配置文件,並根據用戶設定的配置,對server變數相關屬性的值進行修改。
例1:用戶在終端中指定redis伺服器埠號,預設埠從6379變為了10086,啟動服務
[root@xuegod64 redis]# redis-server --port 10086 60098:C 07 Dec 16:04:05.183 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo //下麵通過另一個客戶端,指定埠,連接到伺服器,通過info來查看,是沒有config_file的配置路徑。 [root@xuegod64 ~]# redis-cli -h 127.0.0.1 -p 10086 127.0.0.1:10086>info # Server redis_version:4.0.6 ....
例2: 用戶通過載入redis.conf配置文件,這是最常見的啟動服務。通過info來查看,這種是有config_file的配置路徑的
[root@xuegod64 redis]# redis-server redis.conf
通過上面案例知道,如果用戶為伺服器狀態屬性指定了新的值,那麼伺服器就會更新相應的屬性值,如果沒有為屬性設置新的值,那麼伺服器就用之前initServerConfig函數為屬性設置的預設值。
3.3 初始化伺服器數據結構
伺服器在載入用戶指定的配置選項,並對server狀態進行更新之後,伺服器就可以進入初始化第三個階段,此時伺服器創建的數據結構還包括:server.clients鏈表;server.db數組;保存頻道訂閱信息的server.pubsub_channels字典以及server.pubsub_patterns鏈表; 用於執行Lua腳本的server.lua; 用於保存慢日誌的server.slowlog屬性。
接著伺服器將調用initServer函數,為以上的數據結構分配記憶體,該函數設置還包括:為伺服器設置進程號處理器;創建共用對象;打開伺服器監聽埠;為serverCron函數創建時間事件;做好AOF持久化寫入準備;初始化伺服器後臺I/O模塊。當initServer函數執行完後,伺服器將用ascll字元在日誌中列印出redis的圖標,以及redis的版本號信息。
3.4 還原資料庫狀態
在完成了對伺服器狀態server變數的初始化之後,伺服器需要載入RDB文件或者AOF文件,並根據文件記錄的內容來還原伺服器的資料庫狀態。如果伺服器啟用了AOF持久化功能,那麼伺服器使用AOF文件來還原資料庫狀態;相反 如果伺服器沒有啟用AOF持久化功能,那麼伺服器使用RDB文件來還原資料庫狀態。
60183:M 07 Dec 16:06:15.343 * DB loaded from disk: 0.000 seconds