服務端

命令請求的執(zhí)行過程
  • 客戶端發(fā)送一個set命令到收到回復期間的步驟如下
  • 1.客戶端向服務端發(fā)生命令請求SET KEY VALUE
  • 2.服務器接受并處理客戶端發(fā)來的命令請求,并在相應的數(shù)據(jù)庫中執(zhí)行操作,并產(chǎn)生命令回復OK
  • 3.服務器將命令回復OK發(fā)送給客戶端
  • 4.客戶端接收到服務器返回的命令回復ok,并將這個回復打印給客戶觀看
發(fā)送命令請求
  • 用戶輸入命令請求,客戶端將請求轉(zhuǎn)換成協(xié)議格式然后發(fā)送到服務器


    image.png
讀取命令請求
  • 當客戶端與服務器之間的鏈接socket因為客戶端的寫入而變得可讀時,服務器將調(diào)用命令請求處理器來執(zhí)行下列操作:
  • 讀取socket中協(xié)議格式的命令請求,并將其保存到客戶端狀態(tài)(客戶端狀態(tài)保存在服務端)的輸入緩沖區(qū)
  • 服務器對輸入緩沖區(qū)中的命令請求進行分析,提取出命令請求中包含的命令參數(shù),以及命令參數(shù)的個數(shù),然后分別將其保存到客戶端狀態(tài)的argv屬性和argc屬性
  • 調(diào)用命令執(zhí)行器,執(zhí)行客戶端指定的命令
查找命令實現(xiàn)
  • 命令執(zhí)行器要做的第一件事就是根據(jù)客戶端狀態(tài)的argv[0]參數(shù)命令去命令表里面獲取對應的命令,并把該命令保存到客戶端狀態(tài)的cmd屬性里面
  • 命令表如下


    image.png
  • 其中sflags如下:


    image.png
  • 命令表如下:


    image.png
  • 客戶端裝狀態(tài)的cmd指針


    image.png
執(zhí)行命令的預備操作
  • 1.檢測客戶端狀態(tài)的cmd指針是否指向Null,如果是說明找不到響應命令直接返回錯誤
  • 2.根據(jù)redisCommand結(jié)構(gòu)的arity屬性檢測命令請求所給定的參數(shù)個數(shù)是否正確,正確就繼續(xù)執(zhí)行,否則直接返回,值為負數(shù)代表大于等于該值,值為整數(shù)代表等于
  • 3.檢測客戶端是否已經(jīng)通過了身份驗證,未通過身份驗證的客戶端只能執(zhí)行AUTH命令
  • 4.如果服務器還打開了maxmemory功能,那么在執(zhí)行命令之前,先檢查服務器的內(nèi)存占用情況,并在有需要時候先進行內(nèi)存回收,然后在執(zhí)行命令。
  • 5.如果上一次執(zhí)行了BGSAVE命令時出錯,且服務器打開了stop-writes-on-bgsave-error功能,且服務器要執(zhí)行的命令是一個寫命令,那么服務器將拒絕執(zhí)行這個命令
  • 6.如果客戶端當前正在使用SUBSCRIBE命令訂閱頻道,或者使用PSUBSCRIBE命令訂閱模式,那么服務器只會執(zhí)行客戶端發(fā)來的SUBSCRIBE,和PSUBSCRIBE,UNSUBSCRIBE,PUNSUBSCRIBE命令。其他命令都會被拒絕
  • 7.如果服務器正在進行數(shù)據(jù)載入,那么客戶端發(fā)送的命令必須帶有l(wèi)標識(比如info,shutdown,publish)才會被服務器執(zhí)行,其他都會被拒絕
  • 8.如果服務器因為執(zhí)行l(wèi)ua腳本而超時并進入阻塞狀態(tài),那么服務器只會執(zhí)行客戶端的SHUTDOWN命令和SCRIPT KILL命令,其他命令會被拒絕
  • 9.如果客戶端正在執(zhí)行事務,那么服務器只會執(zhí)行客戶端發(fā)來的EXEC,DISCARD,MULTI,WATCH四個命令,其他命令都會被放入事務隊列中
  • 10.如果服務器端打開了監(jiān)視器功能,那么服務器會將要執(zhí)行的命令和參數(shù)等信息發(fā)送給監(jiān)視器,當完成了以上預備操作之后才會真正執(zhí)行命令
執(zhí)行命令的實現(xiàn)函數(shù)
  • 真正的執(zhí)行如下,根據(jù)client指針選擇cmd,然后調(diào)用proc函數(shù)傳入client對象本身
  • 產(chǎn)生的回復會被保存在客戶端對象的輸出緩沖區(qū),之后實現(xiàn)函數(shù)還會為客戶端的socket關(guān)聯(lián)命令回復處理器,該處理器會將命令回復返回給客戶端


    image.png
執(zhí)行命令的后續(xù)操作
  • 1.如果開啟了慢查詢,那么慢查詢?nèi)罩灸K會檢查是否需要為剛剛執(zhí)行完的命令請求添加一條新的查詢?nèi)罩?/li>
  • 2.更新rediscommand結(jié)構(gòu)的milliseconds屬性(記錄耗時),更新calls+1
  • 3.如果服務器開啟了AOF持久化,那么AOF持久化模塊會將剛執(zhí)行的命令請求寫入到AOF緩沖區(qū)
  • 4.如果有其他從服務器正在復制當前這個服務器,那么服務器將會吧剛剛執(zhí)行的命令傳播給所有從服務器
將命令回復發(fā)送給客戶端
  • 把回復放入輸出緩沖區(qū),并為客戶端的socket管理一個命令回復處理器,當客戶的socket變?yōu)榭蓪?,服務器就會?zhí)行命令回復處理器,將緩沖區(qū)內(nèi)容發(fā)送給客戶端
  • 發(fā)送完畢后輸出緩沖區(qū)即被情況
客戶端接受并打印命令回復
image.png
serverCron函數(shù)
  • 服務器中的該函數(shù)默認是每隔100毫秒執(zhí)行一次,其具體功能如下:

  • 1.更新服務器時間緩存:該函數(shù)每隔一定時間更新unixtime(秒級別的時間戳)和mstime(毫秒級別的unix時間戳),這兩個精度并不是很準

  • 2.對于打印日志,更新服務器的LRU時鐘,決定是否指向持久化任務,計算服務器上線時間等對精度要求不高的可以直接使用redisServer中的這兩個屬性

    1. 對于給key設置過期時間,添加慢查詢?nèi)罩镜雀呔裙δ軄碚f,使用的時候還是需要讓服務器再次執(zhí)行系統(tǒng)調(diào)用獲取最新時間
  • 4.redisServer中的lruclock保存了服務器的LRU時鐘,這個屬性和上面介紹的unitime,mstime屬性一樣是服務器時間緩存的一種

  • 5.lruclock默認每10秒更新一次(serverCron執(zhí)行),其用來計算鍵的空轉(zhuǎn)時間

  • 6.每個Redis對象都有一個lru屬性,這個lru屬性保存了對象最后一次被命令訪問的時間

  • 7.計算空轉(zhuǎn)時間主要是lruclock減去對象的lru屬性記錄的時間

  • 8.更新服務器每秒執(zhí)行命令的次數(shù),該函數(shù)通過會以100毫秒一次的頻率去抽樣計算,并估算服務器在最近一秒鐘處理的命令請求數(shù)量

  • 9.更新服務器內(nèi)存峰值記錄,redisServer中的stat_peak_memorty記錄了內(nèi)存峰值大小,每次serverCron執(zhí)行的時候都查詢服務器當前的內(nèi)存數(shù)量與stat_peak_memorty進行對比,保存大值。

  • 10.處理sigterm信號:在服務器啟動的時候會為sigterm信號關(guān)聯(lián)處理器sigtermhandler函數(shù),該函數(shù)負責處理服務器在接收到sigterm時候打開服務器狀態(tài)的shutdown_asap標識


    image.png
  • 11.每次serverCron執(zhí)行都會檢測shutdown_asap屬性,并根據(jù)值決定是否關(guān)閉服務器,1關(guān)閉,0不做處理

  • 12.每次關(guān)閉前會進行RDB持久化操作,所以這也是我們?yōu)槭裁匆獢r截sigterm信號的原因,不攔截就直接關(guān)閉了

  • 13.serverCron每次執(zhí)行都會調(diào)用clientsCron函數(shù),該函數(shù)主要是檢查客戶端與服務器之間的鏈接是否超時,超時就釋放這個客戶端。如果客戶端上一次執(zhí)行命令請求后輸入緩沖區(qū)的大小超過一定長度,那么程序會是否客戶端當親的輸入緩沖區(qū),并重新創(chuàng)建一個新的輸入緩沖區(qū)。

  • 14.管理數(shù)據(jù)庫資源:serverCron每次執(zhí)行都會調(diào)用databaseCron函數(shù),該函數(shù)會刪除過期鍵,并對字典進行收縮操作

  • 15.執(zhí)行被延遲的BGREWRITEAOF,一般我們重新命令會被BGSAVE命令給延遲

  • 16.檢測持久化操作的運行狀態(tài):服務器使用rdb_child_pid屬性和aof_child_pid屬性來記錄執(zhí)行bgsave和bgrewriteaof命令(AOF就是同步的)的子進程ID

  • 17.只要上述兩個pid有一個屬性值不等于-1,那么程序就會執(zhí)行一次wait3函數(shù),改函數(shù)檢測子進程是否有信號發(fā)來服務器進程。

  • 18如果有信號到達,說明RDB文件以及生產(chǎn)完畢,或者AOF文件以及重寫完畢,服務器要進行相應命令的后續(xù)操作,如更換RDB或者AOF文件

  • 19.如果沒有達到就不進行處理,另一方面pid都為-1代表沒有持久化操作,那么程序需要執(zhí)行以下三個檢測:查看是否有AOF重寫被延遲,如果有就重新啟動一個新的AOF重新操作;檢測服務器的自動保存條件是否滿足,如果滿足且此時沒有執(zhí)行其他持久化操作,那么服務器開始一次新的BGSAVE操作(這里不執(zhí)行AOF重寫操作,是因為我們AOF操作有可能被阻塞了,所以這邊的檢測也包含被阻塞的AOF重寫操作);檢測服務器設置的AOF重寫條件是否滿足,如果滿足,且沒有執(zhí)行其他持久化操作,那么服務器開始執(zhí)行AOF重寫


    image.png
image.png
  • 20 .serverCron會將AOF緩沖區(qū)的內(nèi)容吸入到AOF文件
    1. serverCron會關(guān)閉那些輸出緩沖區(qū)大小超出限制的客戶端
  • 21.增加cronloop是的計數(shù)器值(記錄了serverCron執(zhí)行次數(shù)),該值目前主要作用就是在執(zhí)行N此該函數(shù)后執(zhí)行一次指定代碼
初始化服務器
  • 1,初始化服務器狀態(tài)結(jié)構(gòu)redisServer,具體是由函數(shù)initServerConfig函數(shù)執(zhí)行


    image.png
  • 改函數(shù)的主要目的是


    image.png
初始化服務器只會就是載入配置選項
  • 比如修改服務器的運行端口號
  • 修改其他的屬性參數(shù)
初始化服務器數(shù)據(jù)結(jié)構(gòu)
  • 我們還需要初始化server.clients
  • server.db數(shù)組
  • server.pibsub_channels字典用于保存頻道訂閱信息
  • 初始化用于執(zhí)行l(wèi)ua腳本的lua環(huán)境 server.lua
  • 初始化用于保存慢查詢?nèi)罩镜膶傩詓erver.showlog
還原數(shù)據(jù)庫狀態(tài)
  • 看看是否是執(zhí)行RDB還是aof來還原
  • 優(yōu)先使用AOF
執(zhí)行事件循環(huán)也就是主方法類似于netty的nioeventloop的run方法
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容