redis 系列20 伺服器下
二. 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 [[email protected] 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,啟動服務
[[email protected] redis]# redis-server --port 10086 60098:C 07 Dec 16:04:05.183 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo //下面通過另一個客戶端,指定埠,連線到伺服器,通過info來檢視,是沒有config_file的配置路徑。 [[email protected] ~]# 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的配置路徑的
[[email protected] 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