本文主要介紹了 Redis 處理客戶端連接的一些內部實現機制,包括連接處理、超時、緩沖區等一系列內容。
注:本文所述內容基於 Redis2.6 及以上版本。
Redis 通過監聽一個 TCP 端口或者 Unix socket 的方式來接收來自客戶端的連接,當一個連接建立後,Redis 內部會進行以下一些操作:
當客戶端連接被初始化後,Redis 會查看目前的連接數,然後對比配置好的 maxclients 值,如果目前連接數已經達到最大連接數 maxclients 了,那麼說明這個連接不能再接收,Redis 會直接返回客戶端一個連接錯誤,並馬上關閉掉這個連接。
如果有多個客戶端連接上 Redis,並且都向 Redis 發送命令,那麼 Redis 服務端會先處理哪個客戶端的請求呢?答案其實並不確定,主要與兩個因素有關,一是客戶端對應的 socket 對應的數字的大小,二是 kernal 報告各個客戶端事件的先後順序。
Redis 處理一個客戶端傳來數據的步驟如下:
在 Redis2.4 中,最大連接數是被直接硬編碼在代碼裡面的,而在2.6版本中這個值變成可配置的。maxclients 的默認值是 10000,你也可以在 redis.conf 中對這個值進行修改。
當然,這個值只是 Redis 一廂情願的值,Redis 還會照顧到系統本身對進程使用的文件描述符數量的限制。在啟動時 Redis 會檢查系統的 soft limit,以查看打開文件描述符的個數上限。如果系統設置的數字,小於咱們希望的最大連接數加32,那麼這個 maxclients 的設置將不起作用,Redis 會按系統要求的來設置這個值。(加32是因為 Redis 內部會使用最多32個文件描述符,所以連接能使用的相當於所有能用的描述符號減32)。
當上面說的這種情況發生時(maxclients 設置後不起作用的情況),Redis 的啟動過程中將會有相應的日志記錄。比如下面命令希望設置最大客戶端數量為100000,所以 Redis 需要 100000+32 個文件描述符,而系統的最大文件描述符號設置為10144,所以 Redis 只能將 maxclients 設置為 10144 – 32 = 10112。
$ ./redis-server --maxclients 100000 [41422] 23 Jan 11:28:33.179 # Unable to set the max number of files limit to 100032 (Invalid argument), setting the max clients configuration to 10112.
所以說當你想設置 maxclients 值時,最好順便修改一下你的系統設置,當然,養成看日志的好習慣也能發現這個問題。
具體的設置方法就看你個人的需求了,你可以只修改此次會話的限制,也可以直接通過sysctl 修改系統的默認設置。如:
ulimit -Sn 100000 # This will only work if hard limit is big enough.
sysctl -w fs.file-max=100000
對於 Redis 的輸出(也就是命令的返回值)來說,其大小經常是不可控的,可能是一個簡單的命令,能夠產生體積龐大的返回數據。另外也有可能因為執行命令太多,產生的返 回數據的速率超過了往客戶端發送的速率,這時也會產生消息堆積,從而造成輸出緩沖區越來越大,占用過多內存,甚至導致系統崩潰。
所以 Redis 設置了一些保護機制來避免這種情況的出現,這些機制作用於不同種類的客戶端,有不同的輸出緩沖區大小限制,限制方式有兩種:
對於不同客戶端的策略如下:
上面三種規則都是可配置的。可以通過 CONFIG SET 命令或者修改 redis.conf 文件來配置。
Redis 對輸入緩沖區大小的限制比較暴力,當客戶端傳輸的請求大小超過1G時,服務端會直接關閉連接。這種方式可以有效防止一些客戶端或服務端 bug 導致的輸入緩沖區過大的問題。
對當前的 Redis 版本來說,服務端默認是不會關閉長期空閒的客戶端的。但是你可以修改默認配置來設置你希望的超時時間。比如客戶端超過多長時間無交互,就直接關閉。同理,這也可以通過 CONFIG SET 命令或者修改 redis.conf 文件來配置。
值得注意的是,超時時間的設置,只對普通客戶端起作用,對 Pub/Sub 客戶端來說,長期空閒狀態是正常的。
另外,實際的超時時間可能不會像設定的那樣精確,這是因為 Redis 並不會采用計時器或者輪訓遍歷的方法來檢測客戶端超時,而是通過一種漸近式的方式來完成,每次檢查一部分。所以導致的結果就是,可能你設置的超時時間是 10s,但是真實執行的時間是超時12s後客戶端才被關閉。
Redis 的 CLIENT 命令能夠實現三種功能:檢查連接的狀態,殺掉某個連接以及為連接設置名字。
CLIENT LIST 命令能夠獲取當前所有客戶端的狀態,使用方法如下:
redis 127.0.0.1:6379> client list addr=127.0.0.1:52555 fd=5 name= age=855 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client addr=127.0.0.1:52787 fd=6 name= age=6 idle=5 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping
如上面命令的輸出可知,目前此 Redis 有兩個客戶端連接,每一行表示一個連接的各項信息:
你可以查看 CLIENT LIST 的文檔來具體查看所有輸出的含義。
當你通過上面命令獲取到客戶端列表後,就可以通過 CLIENT KILL 命令來殺死指定的連接了。CLIENT KILL 的參數就是上面的 addr 值。
如上面提到的 CLIENT SETNAME 和 CLIENT GETNAME 可以用來為一個連接設置一個名字。