Code Review ~
開始看另一個 open source project : Redis 。這次看的是 Github 上的 6.0-rc1 branch。
src/redis-cli.c
開始從 source code 可以發現 redis 的 client 端程式碼在 src/redis-cli.c,輕易可以找到 main 函數: 一開始先針對 config 做基本的初始化設定、呼叫 parseEnv,接下來根據不同模式來執行,像是: Cluster Manager、Latency、Latency Distribution、Slave、Pipe 等。如果都不是執行特定模式,就進入到互動模式、 呼叫 cliConnect 連接到 server、呼叫 repl 做 CLI 指令的互動。
cliConnect
函數 cliConnect 是用來連接到 Redis Server:透過額外的 flag (CC_FORCE 跟 CC_QUIET) 來連接到 server。 根據不同狀況使用 redisConnect 或者 redisConnectUnix 連接到 server 並獲得一個 redisContext 的物件、 並根據設定來呼叫 cliSecureConnection 使用 TLS 連線。等連線完成之後透過 anetKeepAlive 將 socket 設定成 SO_KEEPALIVE 並針對 Linux 環境額外設定 TCP_KEEPIDLE、TCP_KEEPINTVL 跟 TCP_KEEPCNT。
repl
函數 repl 是用來處理 CLI 的互動指令:開始呼叫 cliInitHelp 跟 cliIntegrateHelp 做初始化、做 CLI 的優化 (MultiLine、AutoComplete、Hint)、如果 stdin 屬於 tty 則額外支援 history。之後使用 cliSplitArgs 處理輸入值 (如果不是危險指令則支援 history),根據指令可能執行 cliSetPreferences、cliConnect、linenoiseClearScreen 或者 issueCommandRepeat。
在 issueCommandRepeat 函數中會呼叫 cliSendCommand 並根據結果來判斷是否需要重新連線並再試一次。呼叫 redisAppendCommandArgv 並透過 cliReadReply 讀取結果。之後透過 processItem 處理回傳的結果:根據收到的第一個 byte 來決定使用 processLineItem、processBulkItem 還是 processAggregateItem。
src/server.c
在 src/server.c 中負責處理 Redis Server 端的行為:經過一連串的初始化、判斷是否為 sentinel mode 、啟用 ACL 等模組化系統、判斷是否為 supervised mode。之後呼叫 initServer、redisSetProcTitle、redisAsciiArt、 checkTcpBacklogSettings、moduleLoadFromQueue、ACLLoadUsersAtStartup、InitServerLast、 loadDataFromDisk。最後呼叫 aeSetBeforeSleepProc、aeSetAfterSleepProc、aeMain、aeDeleteEventLoop。
在 initServer 函數中一開始先設定所有的 signal handler,之後設定 Redis Server 的全域設定,根據設定判斷開啟 TCP Server、TLS Server 還是 Unix Domain Socket。建立 Redis Database 設定、重設 Server 狀態、建立 aeCreateTimeEvent 、aeCreateFileEvent 等事件。根據設定判斷 Cluster 做初始化、呼叫 replicationScriptCacheInit、scriptingInit、 slowlogInit 跟 latencyMonitorInit。
在 loadDataFromDisk 根據是 AOF (AppendOnlyFile) 或者是 RDB (Redis DataBase) 分別呼叫 loadAppendOnlyFile 或 rdbLoad。
AOF / Append Only File
AOF 是一種檔案格式:將所有 Redis 操作儲存在 AOF 檔案當作持久性儲存。一開始判斷檔案是否存在、檔案大小為 0 時視為合法有效的 AOF 檔案,呼叫 createAOFClient 建立一個 AOF Client、呼叫 startLoadingFile 載入 AOF, 最後呼叫 moduleFireServerEvent 觸發 Event,最後讀取 AOF 檔案:透過 lookupCommand 判斷 AOF 中的指令、 如果支援 MultiCommand 則呼叫 queueMultiCommand,否則直接呼叫 command 的 proc。
RDB / Redis DataBase
透過 rdbLoad 載入 RDB:呼叫 startLoadingFile、rioInitWithFile 跟 rdbLoadRio。在 rdbLoadRio 中呼叫 rdbLoadType 讀取 RDB 的型態、rdbLoadStringObject 讀取鍵值、rdbLoadObject 讀取內容。之後根據時間, 決定是呼叫 decrRefCount 減少 DB 引用次數還是 dbAdd 將值寫入到 Redis 中。
在 dbAdd 中指定特定的 Redis Server 並且將 Key-Value 寫入到 DB 中:將 Key-Value 增加到 DB 的 dict 中、值為 LIST 或 ZSET 則呼叫 signalKeyAsReady。如果有啟用 cluster 則額外呼叫 slotToKeyAdd。
struct redisServer
在 struct redisServer 中存放了所有 Redis Server 的設定。