官網下載zip:
本文即是文件創建時間時候的版本~
文章目錄
- 目錄結構
- /src
- int main()
- 服務端 server
- 足夠的熵值 entropy
- umask掩碼
- 系統初始化*
- 重啟機制:保存執行數據 以便后續重啟服務
- 哨兵模式 sentinel
- rdb aof
- 解析命令行參數
- 聲明實現的位置
目錄結構
目錄/文件 | 說明 |
---|---|
src/ | ?核心源代碼目錄,大多數邏輯都在這里 |
deps/ | 依賴的第三方庫,如 jemalloc、hiredis |
tests/ | 單元測試和集成測試代碼 |
redis.conf | 默認的 Redis 配置文件 |
Makefile | 編譯入口,可通過 make 編譯 redis-server 等 |
README.md | 項目簡介 |
utils/ | 一些工具腳本,比如 create-cluster |
/src
int main()
程序 | 執行命令 | 功能 |
---|---|---|
redis-server | ./src/redis-server | 啟動 Redis 【服務端】 |
redis-cli | ./src/redis-cli | 連接 Redis 的【命令行客戶端】 |
redis-benchmark | ./src/redis-benchmark | 對 Redis 做壓力測試 |
setproctitle | 開發/調試輔助工具 | 設置 Linux 進程名用的工具模塊 |
服務端 server
從上往下讀讀注釋:
足夠的熵值 entropy
為了確保獲取到的隨機數具有足夠的熵值(entropy),我們不能僅依賴 time() 和 getpid()。
因為在 容器(如 Docker) 中運行多個 Redis 實例時:
- 它們的 time()(當前時間戳 - 秒級)
- 和 getpid()(進程號)
可能是一樣的!
導致它們生成的隨機數是一樣的。
而 微秒tv_usec 幾乎不可能重復 —— 一秒中可以有 100 萬 個不同的微秒值(0~999999)
struct timeval tv;.../* To achieve entropy, in case of containers, their time() and getpid() can* be the same. But value of tv_usec is fast enough to make the difference */gettimeofday(&tv,NULL); //獲取微秒級時間戳srand(time(NULL)^getpid()^tv.tv_usec); //種隨機數種子srandom(time(NULL)^getpid()^tv.tv_usec);init_genrand64(((long long) tv.tv_sec * 1000000 + tv.tv_usec) ^ getpid()); //【初始化 Redis 自己實現的 Mersenne Twister 64 位隨機數發生器】【梅森旋轉算法】crc64_init(); //初始化 【CRC64 校驗表】;Redis 中使用 CRC64 進行數據校驗
umask掩碼
存儲 umask 值。因為 umask(2) 只提供設置并返回舊值的接口,
所以我們必須先設置一次再恢復它。
我們在啟動早期執行這一步,是為了避免與可能正在創建文件或目錄的線程之間產生競態條件(race condition)。
umask 是 Unix 系統下用于設置默認新建文件權限的掩碼(默認文件0666/目錄0777,當 umask = 0022時(2 = 010),文件0644/目錄0755)
(0777二進制: 0 111 111 111)
沒有純獲取 umask 的接口,而 set 時可以返回之前的值
所以可以通過 umask(umask(0)) 的方式獲取~
系統初始化*
ASAP:as soon as possible 盡快
sentinel mode: 哨兵模式 后面才初始化
strrchr() C標準庫函數【string reverse chracter】:從右向左查找字符串中最后一次出現 ‘/’ 的位置,返回該位置的指針。
這里只想要執行的文件名
uint8_t hashseed[16];getRandomBytes(hashseed,sizeof(hashseed));//Redis 自己的隨機字節生成函數dictSetHashFunctionSeed(hashseed);// 初始化全局字典等結構中的哈希行為char *exec_name = strrchr(argv[0], '/');if (exec_name == NULL) exec_name = argv[0];server.sentinel_mode = checkForSentinelMode(argc,argv, exec_name);// 判斷是否是 sentinel 模式,這是 Redis 支持的一種高可用模式。initServerConfig();// 初始化服務配置(比如監聽端口、最大連接數、緩存設置等)//初始化 ACL(訪問控制列表)子系統 【Redis 的權限控制系統】ACLInit(); /* The ACL subsystem must be initialized ASAP because thebasic networking code and client creation depends on it. */ // 后面的網絡服務和客戶端創建依賴它moduleInitModulesSystem();// 初始化模塊管理框架 // Redis 支持以插件形式加載模塊,比如 RedisGraph、RedisAI 等connTypeInitialize();// 初始化連接類型 // 用于注冊不同類型的連接(TCP、Unix socket、TLS 等)
重啟機制:保存執行數據 以便后續重啟服務
“將可執行文件路徑和參數安全地保存下來,以便之后能夠重新啟動服務器。”
/* Store the executable path and arguments in a safe place in order* to be able to restart the server later. */server.executable = getAbsolutePath(argv[0]); // 獲取當前進程的可執行文件的絕對路徑:argv[0] 通常是執行程序的名稱或者路徑server.exec_argv = zmalloc(sizeof(char*)*(argc+1)); // 為參數數組 exec_argv 分配內存server.exec_argv[argc] = NULL;for (j = 0; j < argc; j++) server.exec_argv[j] = zstrdup(argv[j]); // 將原始 argv 中的參數逐個復制(深拷貝)到 server.exec_argv 中
redis 的優雅重啟機制:一旦重啟時,可以直接調用 execv(server.executable, server.exec_argv) 實現“就地重啟”。
哨兵模式 sentinel
我們現在就需要初始化 Sentinel,
因為在 Sentinel 模式下解析配置文件的過程中,會將需要監控的主節點信息填充到 Sentinel 的數據結構中。(所以new出來’數據結構’)
/* We need to init sentinel right now as parsing the configuration file* in sentinel mode will have the effect of populating the sentinel* data structures with master nodes to monitor. */if (server.sentinel_mode) { // 檢查當前 Redis 是否以哨兵模式運行(通常通過命令行參數或配置文件設置 --sentinel)initSentinelConfig(); // 初始化哨兵【配置解析】相關內容。[哨兵配置文件會描述要監控的主節點(master),以及配置項如 down-after-milliseconds、quorum 等。]initSentinel(); // 初始化哨兵運行時的核心數據結構(如監控的 master 列表等)。(結合上面的 initSentinelConfig(),最終目的是:在加載配置文件時,把其中的哨兵監控項正確寫入內存結構,準備后續運行。)}
哨兵模式介紹:
哨兵模式下,Redis 的行為和普通的主從模式不同,它主要用于自動:
- 監控(Monitoring):持續【檢查】主節點和從節點是否可用;(哨兵模式是基于主從結構進行監控和故障轉移的。)
- 通知(Notification):當某個節點不可達時,【通知】管理員或其他系統;
(哨兵周期性地通過 PING 命令檢查主從節點的可用性。無響應就判斷為節點“主觀下線” subjectively down) - 自動故障轉移(Automatic Failover):主節點宕機后,自動將某個從節點【升級】為主節點;
(如果大多數哨兵都認為某個主節點不可達,稱為“客觀下線”(objectively down))(選舉后,哨兵更新集群配置,并通知客戶端。) - 服務發現(Configuration Provider):客戶端可以通過哨兵【獲取】當前的主節點地址。
哨兵的部署架構
通常使用【多個哨兵(>=3 個)組成一個哨兵集群 + 【一個主節點(master)】 + 【多個從節點(slave)】:
在 sentinel.conf
中配置以告知哨兵誰是主節點:
sentinel monitor <master-name> <ip> <port> <quorum><master-name>:主節點的邏輯名稱,客戶端通過這個名字向 Sentinel 查詢主節點地址
<quorum>:判定主節點下線需要多少哨兵確認
主從模式并不一定必須帶上哨兵,但哨兵是 Redis 官方推薦的高可用方案之一。
主從模式本身沒有自動故障轉移能力。
rdb aof
檢查是否需要以 redis-check-rdb 或 redis-check-aof 模式啟動。※
我們只是執行對應程序的主函數。
但是這兩個程序是 Redis 可執行文件的一部分,
因此可以方便地在加載出錯時執行 RDB 文件檢查。
就是看執不執行,如果執行,就進入相應的“檢查模式”,專門去檢測 RDB 或 AOF 文件有沒有問題。【官方的檢查工具】
/* Check if we need to start in redis-check-rdb/aof mode. We just execute* the program main. However the program is part of the Redis executable* so that we can easily execute an RDB check on loading errors. */if (strstr(exec_name,"redis-check-rdb") != NULL)redis_check_rdb_main(argc,argv,NULL);else if (strstr(exec_name,"redis-check-aof") != NULL)redis_check_aof_main(argc,argv);
strstr(str1, str2) (string.h) 檢查str1里有沒有str2
解析命令行參數
聲明實現的位置
zsetAdd 等,聲明在server.h,實現不在server.c,而是在 t_zset.c :