摘要:現在客戶端和服務器都準備好了,那么客戶端和服務器如何建立連接服務器又是如何響應客戶端的請求呢連接服務器客戶端和服務器進行通訊,首先應該就是建立連接。以上是客戶端發送命令給服務器的過程,在下一節中,我們再來認識服務器是如何響應客戶端請的。
上次我們通過問題“啟動服務器,程序都干了什么?”,跟著源碼,深入了解了 Redis 服務器的啟動過程。
既然啟動了 Redis 服務器,那我們就要連上 Redis 服務干些事情。這里我們可以通過 redis-cli 測試。
現在客戶端和服務器都準備好了,那么Redis 客戶端和服務器如何建立連接?服務器又是如何響應客戶端的請求呢?
1 連接服務器客戶端和服務器進行通訊,首先應該就是建立連接。接下來,我們來看下 redis-cli 與服務器的連接過程。
還記得我們上次使用 gdb 調試程序的步驟嗎?讓我們對 redis-cli 再來一次,看看源碼的執行步驟。在開始之前,記得在編輯器打開 redis-cli.c,定位到 main 函數的位置,畢竟 gdb 看代碼沒有編輯器看著舒服。
debug 步驟如下:
# bash cd /opt/redis-3.2.13 // 啟動 Redis 服務。Ctrl+c 可推出服務器啟動頁,同時保持服務器運行 ./src/redis-server --port 8379 & // 調試 redis-clli gdb ./src/redis-cli # gdb (gdb) b main (gdb) r -p 8379 (gdb) layout src (gdb) focus cmd
執行完上述步驟,我們會進入如下界面:
這時候我們就可以回到編輯器頁,看看對 main 函數中哪一行比較感興趣,就停下來研究研究。到了 2618 行,我們會看到有執行 parseOptions 這個函數,看名字,好像是初始化一些可選項。那就進去看看唄。
1.1 初始化客戶端配置函數執行步驟:main -> parseOptions -> main。
我們會看到,在執行 redis-cli 時攜帶的參數都是在這個函數中解析,比如我們啟動的時候帶著的 -p 參數,會在 996 行被解析到,同時賦值給客戶端的 hostport 配置項。如下圖:
1.2 客戶端啟動模式函數執行步驟:main。
回到 main 函數,會看到后面的代碼會出現很多 cliConnect 函數。要注意的是,這里并不表示 redis-cli 會執行多次 cliConnect 函數。實際上,每一個 if 語句塊,都代表著客戶端的一種連接模式,3.2.13 版本支持以下模式:
Latency mode:延遲模式。redis-cli --latency -p 8379 用來測試客戶端與服務器連接的延遲。還有 --history 和 --dist 可選項,用來展示不同的形式。
Slave mode:模擬從節點模式。
Get RDB mode:生成并發送 RDB 持久化文件,保存在本地。
Pipe mode:管道模式。將命令封裝成指定數據格式,批量發送給 redis 服務器執行。
Find big keys:統計 bigkey 的分布。
Stat mode:統計模式。實時展示服務器的統計信息。
Scan mode:掃描指定模式的鍵,相當于 scan 模式。
LRU test mode:實時測試 LRU 算法的命中情況。
1.3 連接服務器函數執行步驟:main -> cliConnect -> redisConnect -> redisContextInit -> redisContextConnectTcp -> _redisContextConnectTcp -> cliConnect。
我們上面沒有使用特殊模式啟動,因此,我們會看到在 2687 行真正的去調用 cliConnect 函數。跟蹤進去,讓我們看看究竟是如何和服務器進行連接的。
在 cliConnect 函數中,我們看到,根據 hostsocket 的配置項,會使用不同的連接模式。從名字上,我們大概可以猜出,一個是 TCP Socket 連接,另一個是本機 Unix Socket 連接。
如果想要使用 Unix Socket 連接,只需按格式配置 hostscoket 即可:./src/redis-cli -s /tmp/redis.sock。
我們這里使用 TCP Scoket 連接,使用 redisConnect 函數建立連接。
不斷追蹤,我們會看到上面所示的函數執行步驟,在 _redisContextConnectTcp 函數中會看到 getaddrinfo 和 connect 函數的調用,這里就是建立 TCP 連接的地方。
1.4 校驗權限及選擇數據庫函數執行步驟:cliConnect -> anetKeepAlive -> cliAuth -> cliSelect -> main。
回到 cliConnect 函數,如果正常連接上服務器后,還會將我們上面創建的 TCP 連接設置為長連接,然后校驗權限,選擇連接數據庫。
... /* Set aggressive KEEP_ALIVE socket option in the Redis context socket * in order to prevent timeouts caused by the execution of long * commands. At the same time this improves the detection of real * errors. */ anetKeepAlive(NULL, context->fd, REDIS_CLI_KEEPALIVE_INTERVAL); /* Do AUTH and select the right DB. */ if (cliAuth() != REDIS_OK) return REDIS_ERR; if (cliSelect() != REDIS_OK) return REDIS_ERR; ...
至此,我們已經跑完客戶端與服務器建立連接的全過程。感興趣的小伙伴可以嘗試連接不存在的 IP 或 端口,觀察程序拋出異常的時機,熟悉整個連接過程。
客戶端與 服務器建立連接后,就可以使用相關命令操作數據庫中的 key 了。下面我們以 SET KEY VALUE 命令為例,來看看命令的執行過程。
2 發送命令請求當用戶在客戶端鍵入一個命令請求時,客戶端會將這個命令請求按協議格式轉換,然后通過連接到服務器的套接字,將轉換后的命令請求發送給服務器,如圖 3 所示:
因此,對于我們上面的命令請求,客戶端會轉成:
"*3 $3 SET $3 KEY $5 VALUE "
然后發給服務器。
以上是客戶端發送命令給服務器的過程,在下一節中,我們再來認識服務器是如何響應客戶端請的。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/62118.html
摘要:讀取命令請求當客戶端與服務器之間的套接字因客戶端的寫入變得可讀時,服務器將調用命令請求處理器執行以下操作讀取套接字中的命令請求,并將其保存到客戶端狀態的輸入緩沖區。 繼續我們上一節的討論。服務器啟動了,客戶端也發送命令了。接下來,就要到服務器表演的時刻了。 1 服務器處理 服務器讀取到命令請求后,會進行一系列的處理。 1.1 讀取命令請求 當客戶端與服務器之間的套接字因客戶端的寫入變得...
摘要:此時服務器處于休眠狀態,并使用進行事件輪詢,等待監聽事件的發生。繼續執行被調試程序,直至下一個斷點或程序結束縮寫。服務啟動包括初始化基礎配置數據結構對外提供服務的準備工作還原數據庫執行事件循環等。 一直很羨慕那些能讀 Redis 源碼的童鞋,也一直想自己解讀一遍,但迫于 C 大魔王的壓力,解讀日期遙遙無期。 相信很多小伙伴應該也都對或曾對源碼感興趣,但一來覺得自己不會 C 語言,二來也...
閱讀 2655·2019-08-30 15:52
閱讀 3601·2019-08-29 17:02
閱讀 1849·2019-08-29 13:00
閱讀 927·2019-08-29 11:07
閱讀 3242·2019-08-27 10:53
閱讀 1774·2019-08-26 13:43
閱讀 1019·2019-08-26 10:22
閱讀 1345·2019-08-23 18:06