日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

redis(12)--事件,客户端,服务器

發(fā)布時(shí)間:2024/4/13 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 redis(12)--事件,客户端,服务器 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

?

事件

文件事件

讀事件

寫(xiě)事件

同時(shí)關(guān)聯(lián)寫(xiě)事件和讀事件

時(shí)間事件

實(shí)現(xiàn)

服務(wù)器常規(guī)操作

事件的執(zhí)行與調(diào)度

客戶端

客戶端屬性

套接字描述符

名字

標(biāo)志

輸入緩沖區(qū)

命令與命令參數(shù)

命令的實(shí)現(xiàn)函數(shù)

輸出緩沖區(qū)

身份驗(yàn)證

時(shí)間

客戶端的創(chuàng)建與關(guān)閉

創(chuàng)建客戶端

關(guān)閉客戶端

Lua腳本的偽客戶端

AOF文件偽客戶端

服務(wù)器

初始化服務(wù)器

初始化服務(wù)器全局狀態(tài)。

載入配置文件。

創(chuàng)建 daemon 進(jìn)程。

初始化服務(wù)器功能模塊。

載入數(shù)據(jù)。

開(kāi)始事件循環(huán)。

客戶端連接到服務(wù)器

命令的請(qǐng)求、處理和結(jié)果返回


事件

事件是 Redis 服務(wù)器的核心, 它處理兩項(xiàng)重要的任務(wù):

  • 處理文件事件(file event):在多個(gè)客戶端中實(shí)現(xiàn)多路復(fù)用,接受它們發(fā)來(lái)的命令請(qǐng)求,并將命令的執(zhí)行結(jié)果返回給客戶端。
  • 時(shí)間事件(time event):實(shí)現(xiàn)服務(wù)器常規(guī)操作(server cron job)。
  • 文件事件

    Redis 服務(wù)器通過(guò)在多個(gè)客戶端之間進(jìn)行多路復(fù)用, 從而實(shí)現(xiàn)高效的命令請(qǐng)求處理: 多個(gè)客戶端通過(guò)套接字連接到 Redis 服務(wù)器中, 但只有在套接字可以無(wú)阻塞地進(jìn)行讀或者寫(xiě)時(shí), 服務(wù)器才會(huì)和這些客戶端進(jìn)行交互。

    Redis 將這類因?yàn)?strong>對(duì)套接字進(jìn)行多路復(fù)用而產(chǎn)生的事件稱為文件事件(file event), 文件事件可以分為讀事件和寫(xiě)事件兩類。

    多路復(fù)用產(chǎn)生的套接字都放到一個(gè)隊(duì)列中。以有序,同步方式向文件事件分派器發(fā)送套接字

    讀事件

    讀事件標(biāo)志著客戶端命令請(qǐng)求的發(fā)送狀態(tài)

    當(dāng)一個(gè)新的客戶端連接到服務(wù)器時(shí), 服務(wù)器會(huì)給為該客戶端綁定讀事件, 直到客戶端斷開(kāi)連接之后, 這個(gè)讀事件才會(huì)被移除。

    讀事件在整個(gè)網(wǎng)絡(luò)連接的生命期內(nèi), 都會(huì)在等待和就緒兩種狀態(tài)之間切換:

    • 當(dāng)客戶端只是連接到服務(wù)器,但并沒(méi)有向服務(wù)器發(fā)送命令時(shí),該客戶端的讀事件就處于等待狀態(tài)。
    • 當(dāng)客戶端給服務(wù)器發(fā)送命令請(qǐng)求,并且請(qǐng)求已到達(dá)時(shí)(相應(yīng)的套接字可以無(wú)阻塞地執(zhí)行讀操作),該客戶端的讀事件處于就緒狀態(tài)。

    當(dāng)事件處理器被執(zhí)行時(shí), 就緒的文件事件會(huì)被識(shí)別到, 相應(yīng)的命令請(qǐng)求會(huì)被發(fā)送到命令執(zhí)行器, 并對(duì)命令進(jìn)行求值。

    寫(xiě)事件

    寫(xiě)事件標(biāo)志著客戶端對(duì)命令結(jié)果的接收狀態(tài)。

    和客戶端自始至終都關(guān)聯(lián)著讀事件不同, 服務(wù)器只會(huì)在有命令結(jié)果要傳回給客戶端時(shí), 才會(huì)為客戶端關(guān)聯(lián)寫(xiě)事件, 并且在命令結(jié)果傳送完畢之后, 客戶端和寫(xiě)事件的關(guān)聯(lián)就會(huì)被移除。

    一個(gè)寫(xiě)事件會(huì)在兩種狀態(tài)之間切換:

    • 當(dāng)服務(wù)器有命令結(jié)果需要返回給客戶端,但客戶端還未能執(zhí)行無(wú)阻塞寫(xiě),那么寫(xiě)事件處于等待狀態(tài)。
    • 當(dāng)服務(wù)器有命令結(jié)果需要返回給客戶端,并且客戶端可以進(jìn)行無(wú)阻塞寫(xiě),那么寫(xiě)事件處于就緒狀態(tài)。

    當(dāng)客戶端向服務(wù)器發(fā)送命令請(qǐng)求, 并且請(qǐng)求被接受并執(zhí)行之后, 服務(wù)器就需要將保存在緩存內(nèi)的命令執(zhí)行結(jié)果返回給客戶端, 這時(shí)服務(wù)器就會(huì)為客戶端關(guān)聯(lián)寫(xiě)事件。

    同時(shí)關(guān)聯(lián)寫(xiě)事件和讀事件

    前面提到過(guò),讀事件只有在客戶端斷開(kāi)和服務(wù)器的連接時(shí),才會(huì)被移除

    這也就是說(shuō),當(dāng)客戶端關(guān)聯(lián)寫(xiě)事件的時(shí)候,實(shí)際上它在同時(shí)關(guān)聯(lián)讀/寫(xiě)兩種事件。

    因?yàn)樵谕淮挝募录幚砥鞯恼{(diào)用中, 單個(gè)客戶端只能執(zhí)行其中一種事件(要么讀,要么寫(xiě),但不能又讀又寫(xiě)), 當(dāng)出現(xiàn)讀事件和寫(xiě)事件同時(shí)就緒的情況時(shí), 事件處理器優(yōu)先處理讀事件。這也就是說(shuō), 當(dāng)服務(wù)器有命令結(jié)果要返回客戶端, 而客戶端又有新命令請(qǐng)求進(jìn)入時(shí), 服務(wù)器先處理新命令請(qǐng)求。

    ?

    時(shí)間事件

    時(shí)間事件記錄著那些要在指定時(shí)間點(diǎn)運(yùn)行的事件, 多個(gè)時(shí)間事件以無(wú)序鏈表的形式保存在服務(wù)器狀態(tài)中。

    redis時(shí)間事件分為2類:

    • 定時(shí)事件:在指定時(shí)間之后,執(zhí)行一次。
    • 周期性事件:每隔一段時(shí)間就執(zhí)行一次。

    每個(gè)時(shí)間事件主要由以下幾個(gè)屬性組成:

    • id:全局唯一ID,新事件ID號(hào)比舊事件ID號(hào)大。
    • when :以毫秒格式的 UNIX 時(shí)間戳為單位,記錄了應(yīng)該在什么時(shí)間點(diǎn)執(zhí)行事件處理函數(shù)。
    • timeProc :事件處理函數(shù)。
    • next 指向下一個(gè)時(shí)間事件,形成鏈表。

    根據(jù) timeProc 函數(shù)的返回值,可以將時(shí)間事件劃分為兩類:

    • 如果事件處理函數(shù)返回 ae.h/AE_NOMORE ,那么這個(gè)事件為單次執(zhí)行事件:該事件會(huì)在指定的時(shí)間被處理一次,之后該事件就會(huì)被刪除,不再執(zhí)行。
    • 如果事件處理函數(shù)返回一個(gè)非 AE_NOMORE 的整數(shù)值,那么這個(gè)事件為循環(huán)執(zhí)行事件:該事件會(huì)在指定的時(shí)間被處理,之后它會(huì)按照事件處理函數(shù)的返回值,更新事件的 when 屬性,讓這個(gè)事件在之后的某個(gè)時(shí)間點(diǎn)再次運(yùn)行,并以這種方式一直更新并運(yùn)行下去。

    實(shí)現(xiàn)

    服務(wù)器將所有時(shí)間事件都放在一個(gè)無(wú)序列表中,每當(dāng)時(shí)間事件執(zhí)行器運(yùn)行時(shí),就遍歷列表,查找所有已到達(dá)時(shí)間事件,并調(diào)用相應(yīng)事件處理器。無(wú)序列表不是指不按ID排序,是指不按when排序

    無(wú)序鏈表并不影響時(shí)間事件處理器的性能

    在目前的版本中, 正常模式下的 Redis 只帶有 serverCron 一個(gè)時(shí)間事件, 而在 benchmark 模式下, Redis 也只使用兩個(gè)時(shí)間事件。

    在這種情況下, 程序幾乎是將無(wú)序鏈表退化成一個(gè)指針來(lái)使用, 所以使用無(wú)序鏈表來(lái)保存時(shí)間事件, 并不影響事件處理器的性能。

    服務(wù)器常規(guī)操作

    對(duì)于持續(xù)運(yùn)行的服務(wù)器來(lái)說(shuō), 服務(wù)器需要定期對(duì)自身的資源和狀態(tài)進(jìn)行必要的檢查和整理, 從而讓服務(wù)器維持在一個(gè)健康穩(wěn)定的狀態(tài), 這類操作被統(tǒng)稱為常規(guī)操作(cron job)。

    在 Redis 中, 常規(guī)操作由 redis.c/serverCron 實(shí)現(xiàn), 它主要執(zhí)行以下操作:

    • 更新服務(wù)器的各類統(tǒng)計(jì)信息,比如時(shí)間、內(nèi)存占用、數(shù)據(jù)庫(kù)占用情況等。
    • 清理數(shù)據(jù)庫(kù)中的過(guò)期鍵值對(duì)。
    • 對(duì)不合理的數(shù)據(jù)庫(kù)進(jìn)行大小調(diào)整。
    • 關(guān)閉和清理連接失效的客戶端。
    • 嘗試進(jìn)行 AOF 或 RDB 持久化操作。
    • 如果服務(wù)器是主節(jié)點(diǎn)的話,對(duì)附屬節(jié)點(diǎn)進(jìn)行定期同步。
    • 如果處于集群模式的話,對(duì)集群進(jìn)行定期同步和連接測(cè)試。

    Redis 將 serverCron 作為時(shí)間事件來(lái)運(yùn)行, 從而確保它每隔一段時(shí)間就會(huì)自動(dòng)運(yùn)行一次, 又因?yàn)?serverCron 需要在 Redis 服務(wù)器運(yùn)行期間一直定期運(yùn)行, 所以它是一個(gè)循環(huán)時(shí)間事件: serverCron 會(huì)一直定期執(zhí)行,直到服務(wù)器關(guān)閉為止。

    在 Redis 2.6 版本中, 程序規(guī)定 serverCron 每秒運(yùn)行 10 次, 平均每 100 毫秒運(yùn)行一次。 從 Redis 2.8 開(kāi)始, 用戶可以通過(guò)修改 hz 選項(xiàng)來(lái)調(diào)整 serverCron 的每秒執(zhí)行次數(shù), 具體信息請(qǐng)參考 redis.conf 文件中關(guān)于 hz 選項(xiàng)的說(shuō)明。

    事件的執(zhí)行與調(diào)度

    既然 Redis 里面既有文件事件, 又有時(shí)間事件, 那么如何調(diào)度這兩種事件就成了一個(gè)關(guān)鍵問(wèn)題。

    簡(jiǎn)單地說(shuō), Redis 里面的兩種事件呈合作關(guān)系, 它們之間包含以下三種屬性:

  • 一種事件會(huì)等待另一種事件執(zhí)行完畢之后,才開(kāi)始執(zhí)行,事件之間不會(huì)出現(xiàn)搶占
  • 事件處理器先處理文件事件(處理命令請(qǐng)求),再執(zhí)行時(shí)間事件(調(diào)用 serverCron)
  • 文件事件的等待時(shí)間(類 poll 函數(shù)的最大阻塞時(shí)間),由距離到達(dá)時(shí)間最短的時(shí)間事件決定。
  • 這些屬性表明, 實(shí)際處理時(shí)間事件的時(shí)間, 通常會(huì)比時(shí)間事件所預(yù)定的時(shí)間要晚, 至于延遲的時(shí)間有多長(zhǎng), 取決于時(shí)間事件執(zhí)行之前, 執(zhí)行文件事件所消耗的時(shí)間。

    ?

    客戶端

    Redis服務(wù)器是一對(duì)多服務(wù)器程序,對(duì)于每個(gè)與服務(wù)器連接的客戶端,服務(wù)器都為這些客戶端建立了相應(yīng)的redisClient結(jié)構(gòu),保存了客戶端的狀態(tài)信息。

    客戶端屬性

    客戶端狀態(tài)包含的屬性可以分為兩類:

    • 一類是比較通用的屬性, 這些屬性很少與特定功能相關(guān), 無(wú)論客戶端執(zhí)行的是什么工作, 它們都要用到這些屬性。
    • 另外一類是和特定功能相關(guān)的屬性, 比如操作數(shù)據(jù)庫(kù)時(shí)需要用到的 db 屬性和 dictid 屬性, 執(zhí)行事務(wù)時(shí)需要用到的 mstate 屬性, 以及執(zhí)行 WATCH 命令時(shí)需要用到的 watched_keys 屬性, 等等。

    套接字描述符

    定義:int fd

    • -1:偽客戶端
    • >-1:普通客戶端

    查看

    redis> CLIENT list addr=127.0.0.1:53428 fd=6 name= age=1242 idle=0 ... addr=127.0.0.1:53469 fd=7 name= age=4 idle=4 ...

    名字

    在默認(rèn)情況下, 一個(gè)連接到服務(wù)器的客戶端是沒(méi)有名字的。相反地, 如果客戶端為自己設(shè)置了名字, 那么 name 屬性將指向一個(gè)字符串對(duì)象, 而該對(duì)象就保存著客戶端的名字。

    定義:robj *name;

    查看

    redis> CLIENT list addr=127.0.0.1:53428 fd=6 name= age=1242 idle=0 ... addr=127.0.0.1:53469 fd=7 name= age=4 idle=4 ...

    標(biāo)志

    客戶端的標(biāo)志屬性 flags 記錄了客戶端的角色(role), 以及客戶端目前所處的狀態(tài):

    定義:int flags;

    輸入緩沖區(qū)

    客戶端狀態(tài)的輸入緩沖區(qū)用于保存客戶端發(fā)送的命令請(qǐng)求:

    定義:sds querybuf;

    命令與命令參數(shù)

    在服務(wù)器將客戶端發(fā)送的命令請(qǐng)求保存到客戶端狀態(tài)的 querybuf 屬性之后, 服務(wù)器將對(duì)命令請(qǐng)求的內(nèi)容進(jìn)行分析, 并將得出的命令參數(shù)以及命令參數(shù)的個(gè)數(shù)分別保存到客戶端狀態(tài)的 argv 屬性和 argc 屬性:

    定義:robj **argv; int argc;

    命令的實(shí)現(xiàn)函數(shù)

    當(dāng)服務(wù)器從協(xié)議內(nèi)容中分析并得出 argv 屬性和 argc 屬性的值之后, 服務(wù)器將根據(jù)項(xiàng) argv[0] 的值, 在命令表中查找命令所對(duì)應(yīng)的命令實(shí)現(xiàn)函數(shù)。

    定義:struct redisCommand *cmd;

    輸出緩沖區(qū)

    執(zhí)行命令所得的命令回復(fù)會(huì)被保存在客戶端狀態(tài)的輸出緩沖區(qū)里面, 每個(gè)客戶端都有兩個(gè)輸出緩沖區(qū)可用, 一個(gè)緩沖區(qū)的大小是固定的, 另一個(gè)緩沖區(qū)的大小是可變的:

    • 固定大小的緩沖區(qū)用于保存那些長(zhǎng)度比較小的回復(fù), 比如 OK 、簡(jiǎn)短的字符串值、整數(shù)值、錯(cuò)誤回復(fù),等等。
    • 可變大小的緩沖區(qū)用于保存那些長(zhǎng)度比較大的回復(fù), 比如一個(gè)非常長(zhǎng)的字符串值, 一個(gè)由很多項(xiàng)組成的列表, 一個(gè)包含了很多元素的集合, 等等。

    客戶端的固定大小緩沖區(qū)由 buf 和 bufpos 兩個(gè)屬性組成:

    定義:char buf[REDIS_REPLY_CHUNK_BYTES]; int bufpos;

    buf 是一個(gè)大小為 REDIS_REPLY_CHUNK_BYTES 字節(jié)的字節(jié)數(shù)組, 而 bufpos 屬性則記錄了 buf 數(shù)組目前已使用的字節(jié)數(shù)量。

    REDIS_REPLY_CHUNK_BYTES 常量目前的默認(rèn)值為 16*1024 , 也即是說(shuō), buf 數(shù)組的默認(rèn)大小為 16 KB 。

    可變大小緩沖區(qū)由 reply 鏈表和一個(gè)或多個(gè)字符串對(duì)象組成:

    定義:list *reply;

    身份驗(yàn)證

    客戶端狀態(tài)的 authenticated 屬性用于記錄客戶端是否通過(guò)了身份驗(yàn)證:

    定義:int authenticated;

    • 0:客戶端未通過(guò)身份驗(yàn)證
    • 1:客戶端已通過(guò)身份驗(yàn)證

    當(dāng)客戶端 authenticated 屬性的值為 0 時(shí), 除了 AUTH 命令之外, 客戶端發(fā)送的所有其他命令都會(huì)被服務(wù)器拒絕執(zhí)行:

    時(shí)間

    定義:time_t ctime;time_t lastinteraction;time_t obuf_soft_limit_reached_time;

    ctime 屬性記錄了創(chuàng)建客戶端的時(shí)間, 這個(gè)時(shí)間可以用來(lái)計(jì)算客戶端與服務(wù)器已經(jīng)連接了多少秒 —— CLIENT_LIST 命令的 age 域記錄了這個(gè)秒數(shù):

    lastinteraction 屬性記錄了客戶端與服務(wù)器最后一次進(jìn)行互動(dòng)(interaction)的時(shí)間。

    lastinteraction 屬性可以用來(lái)計(jì)算客戶端的空轉(zhuǎn)(idle)時(shí)間, 也即是, 距離客戶端與服務(wù)器最后一次進(jìn)行互動(dòng)以來(lái), 已經(jīng)過(guò)去了多少秒 —— CLIENT_LIST 命令的 idle 域記錄了這個(gè)秒數(shù):

    obuf_soft_limit_reached_time 屬性記錄了輸出緩沖區(qū)第一次到達(dá)軟性限制(soft limit)的時(shí)間,

    客戶端的創(chuàng)建與關(guān)閉

    創(chuàng)建客戶端

    在客戶端使用connect連接服務(wù)器時(shí)創(chuàng)建redisClient

    關(guān)閉客戶端

    關(guān)閉原因:

    • 客戶端進(jìn)程退出或kill
    • 命令協(xié)議不正確
    • CLIENT KILL
    • timeout
    • 命令請(qǐng)求大小超過(guò)輸入緩沖區(qū)的限制大小(默認(rèn)1GB)
    • 命令回復(fù)大小超過(guò)輸出緩沖區(qū)的限制大小

    服務(wù)器會(huì)時(shí)刻檢查客戶端的輸出大小,并在緩沖區(qū)的大小超出范圍時(shí),執(zhí)行限制操作:

    硬性限制(hard limit):超過(guò)硬性限制,則服務(wù)器立即關(guān)閉客戶端。

    軟性限制(soft limit ):超過(guò)軟性限制,服務(wù)器將使用客戶端狀態(tài)的obuf_soft_limit_reached_time記錄下客戶端達(dá)到軟性限制的起始時(shí)間,之后繼續(xù)監(jiān)視,一直超出,超過(guò)服務(wù)器設(shè)置的時(shí)間(sort seconds),則關(guān)閉客戶端,否則不關(guān)閉,obuf_soft_limit_reached_time清零。

    使用client_output_buffer_limit選項(xiàng)可以為從服務(wù)器客戶端,發(fā)布訂閱客戶端,普通客戶端設(shè)置軟性,硬性限制,格式為:

    client_output_buffer_limit <class> <hard limit> <soft limit> <sort seconds>

    示例:

    client_output_buffer_limit? normal 0 0 0???? #普通客戶端 不限制。

    client_output_buffer_limit? slave 256mb 64mb 60 #從服務(wù)器

    client_output_buffer_limit? pubsub 32mb 8mb 60 #發(fā)布訂閱

    Lua腳本的偽客戶端

    定義:redisClient *lua_client;

    AOF文件偽客戶端

    服務(wù)器

    初始化服務(wù)器

    從啟動(dòng) Redis 服務(wù)器, 到服務(wù)器可以接受外來(lái)客戶端的網(wǎng)絡(luò)連接這段時(shí)間, Redis 需要執(zhí)行一系列初始化操作。

    整個(gè)初始化過(guò)程可以分為以下六個(gè)步驟:

  • 初始化服務(wù)器全局狀態(tài)。
  • 載入配置文件。
  • 創(chuàng)建 daemon 進(jìn)程。
  • 初始化服務(wù)器功能模塊。
  • 載入數(shù)據(jù)。
  • 開(kāi)始事件循環(huán)。
  • 初始化服務(wù)器全局狀態(tài)。

    redisServer 結(jié)構(gòu)記錄了和服務(wù)器相關(guān)的所有數(shù)據(jù), 這個(gè)結(jié)構(gòu)主要包含以下信息:

    • 服務(wù)器中的所有數(shù)據(jù)庫(kù)。
    • 命令表:在執(zhí)行命令時(shí),根據(jù)字符來(lái)查找相應(yīng)命令的實(shí)現(xiàn)函數(shù)。
    • 事件狀態(tài)。
    • 服務(wù)器的網(wǎng)絡(luò)連接信息:套接字地址、端口,以及套接字描述符。
    • 所有已連接客戶端的信息。
    • Lua 腳本的運(yùn)行環(huán)境及相關(guān)選項(xiàng)。
    • 實(shí)現(xiàn)訂閱與發(fā)布(pub/sub)功能所需的數(shù)據(jù)結(jié)構(gòu)。
    • 日志(log)和慢查詢?nèi)罩?#xff08;slowlog)的選項(xiàng)和相關(guān)信息。
    • 數(shù)據(jù)持久化(AOF 和 RDB)的配置和狀態(tài)。
    • 服務(wù)器配置選項(xiàng):比如要?jiǎng)?chuàng)建多少個(gè)數(shù)據(jù)庫(kù),是否將服務(wù)器進(jìn)程作為 daemon 進(jìn)程來(lái)運(yùn)行,最大連接多少個(gè)客戶端,壓縮結(jié)構(gòu)(zip structure)的實(shí)體數(shù)量,等等。
    • 統(tǒng)計(jì)信息:比如鍵有多少次命令、不命中,服務(wù)器的運(yùn)行時(shí)間,內(nèi)存占用,等等。

    在這一步, 程序創(chuàng)建一個(gè) redisServer 結(jié)構(gòu)的實(shí)例變量 server 用作服務(wù)器的全局狀態(tài), 并將 server 的各個(gè)屬性初始化為默認(rèn)值

    ?

    載入配置文件。

    程序?yàn)?server 變量(也即是服務(wù)器狀態(tài))的各個(gè)屬性設(shè)置了默認(rèn)值, 但這些默認(rèn)值有時(shí)候并不是最合適的。

    為了讓使用者按自己的要求配置服務(wù)器, Redis 允許用戶在運(yùn)行服務(wù)器時(shí), 提供相應(yīng)的配置文件(config file)或者顯式的選項(xiàng)(option), Redis 在初始化完 server 變量之后, 會(huì)讀入配置文件和選項(xiàng), 然后根據(jù)這些配置來(lái)對(duì) server 變量的屬性值做相應(yīng)的修改:

  • 如果單純執(zhí)行 redis-server 命令,那么服務(wù)器以默認(rèn)的配置來(lái)運(yùn)行 Redis 。

  • 另一方面, 如果給 Redis 服務(wù)器送入一個(gè)配置文件, 那么 Redis 將按配置文件的設(shè)置來(lái)更新服務(wù)器的狀態(tài)。

    比如說(shuō), 通過(guò)命令 redis-server /etc/my-redis.conf , Redis 會(huì)根據(jù) my-redis.conf 文件的內(nèi)容來(lái)對(duì)服務(wù)器狀態(tài)做相應(yīng)的修改。

  • 除此之外, 還可以顯式地給服務(wù)器傳入選項(xiàng), 直接修改服務(wù)器配置。

    舉個(gè)例子, 通過(guò)命令 redis-server --port 10086 , 可以讓 Redis 服務(wù)器端口變更為 10086 。

  • 當(dāng)然, 同時(shí)使用配置文件和顯式選項(xiàng)也是可以的, 如果文件和選項(xiàng)有沖突的地方, 那么優(yōu)先使用選項(xiàng)所指定的配置值

    舉個(gè)例子, 如果運(yùn)行命令 redis-server /etc/my-redis.conf --port 10086 , 并且 my-redis.conf 也指定了 port 選項(xiàng), 那么服務(wù)器將優(yōu)先使用 --port 10086 (實(shí)際上是選項(xiàng)指定的值覆蓋了配置文件中的值)。

  • ?

    創(chuàng)建 daemon 進(jìn)程。

    Redis 默認(rèn)以 daemon 進(jìn)程的方式運(yùn)行。

    當(dāng)服務(wù)器初始化進(jìn)行到這一步時(shí), 程序?qū)?chuàng)建 daemon 進(jìn)程來(lái)運(yùn)行 Redis , 并創(chuàng)建相應(yīng)的 pid 文件

    初始化服務(wù)器功能模塊。

    初始化程序完成兩件事:

    • 為 server 變量的數(shù)據(jù)結(jié)構(gòu)子屬性分配內(nèi)存
    • 初始化這些數(shù)據(jù)結(jié)構(gòu)

    為數(shù)據(jù)結(jié)構(gòu)分配內(nèi)存, 并初始化這些數(shù)據(jù)結(jié)構(gòu), 等同于對(duì)相應(yīng)的功能進(jìn)行初始化。

    比如說(shuō), 當(dāng)為訂閱與發(fā)布所需的鏈表分配內(nèi)存之后, 訂閱與發(fā)布功能就處于就緒狀態(tài), 隨時(shí)可以為 Redis 所用了。

    在這一步, 程序完成的主要?jiǎng)幼魅缦?#xff1a;

    • 初始化 Redis 進(jìn)程的信號(hào)功能。
    • 初始化日志功能。
    • 初始化客戶端功能。
    • 初始化共享對(duì)象。
    • 初始化事件功能。
    • 初始化數(shù)據(jù)庫(kù)。
    • 初始化網(wǎng)絡(luò)連接。
    • 初始化訂閱與發(fā)布功能。
    • 初始化各個(gè)統(tǒng)計(jì)變量。
    • 關(guān)聯(lián)服務(wù)器常規(guī)操作(cron job)到時(shí)間事件,關(guān)聯(lián)客戶端應(yīng)答處理器到文件事件。
    • 如果 AOF 功能已打開(kāi),那么打開(kāi)或創(chuàng)建 AOF 文件。
    • 設(shè)置內(nèi)存限制。
    • 初始化 Lua 腳本環(huán)境。
    • 初始化慢查詢功能。
    • 初始化后臺(tái)操作線程。

    完成這一步之后, 服務(wù)器打印出 Redis 的 ASCII LOGO 、服務(wù)器版本等信息, 表示所有功能模塊已經(jīng)就緒, 可以等待被使用了。

    ?

    載入數(shù)據(jù)。

    在這一步, 程序需要將持久化在 RDB 或者 AOF 文件里的數(shù)據(jù), 載入到服務(wù)器進(jìn)程里面。

    如果服務(wù)器有啟用 AOF 功能的話, 那么使用 AOF 文件來(lái)還原數(shù)據(jù); 否則, 程序使用 RDB 文件來(lái)還原數(shù)據(jù)。

    當(dāng)執(zhí)行完這一步時(shí), 服務(wù)器打印出一段載入完成信息:

    [6717] 22 Feb 11:59:14.830 * DB loaded from disk: 0.068 seconds

    開(kāi)始事件循環(huán)。

    服務(wù)器的初始化已經(jīng)完成, 程序打開(kāi)事件循環(huán), 開(kāi)始接受客戶端連接。

    以下是服務(wù)器在這一步打印的信息:

    [6717] 22 Feb 11:59:14.830 * The server is now ready to accept connections on port 6379

    以下是初始化完成之后, 服務(wù)器狀態(tài)和各個(gè)模塊之間的關(guān)系圖:

    ?

    ?

    客戶端連接到服務(wù)器

    當(dāng) Redis 服務(wù)器完成初始化之后, 它就準(zhǔn)備好可以接受外來(lái)客戶端的連接了。

    當(dāng)一個(gè)客戶端通過(guò)套接字函數(shù) connect 到服務(wù)器時(shí), 服務(wù)器執(zhí)行以下步驟:

  • 服務(wù)器通過(guò)文件事件無(wú)阻塞地 accept 客戶端連接,并返回一個(gè)套接字描述符 fd 。
  • 服務(wù)器為 fd 創(chuàng)建一個(gè)對(duì)應(yīng)的 redis.h/redisClient 結(jié)構(gòu)實(shí)例,并將該實(shí)例加入到服務(wù)器的已連接客戶端的鏈表中。
  • 服務(wù)器在事件處理器為該 fd 關(guān)聯(lián)讀文件事件
  • 完成這三步之后,服務(wù)器就可以等待客戶端發(fā)來(lái)命令請(qǐng)求了。

    Redis 以多路復(fù)用的方式來(lái)處理多個(gè)客戶端, 為了讓多個(gè)客戶端之間獨(dú)立分開(kāi)、不互相干擾, 服務(wù)器為每個(gè)已連接客戶端維持一個(gè) redisClient 結(jié)構(gòu), 從而單獨(dú)保存該客戶端的狀態(tài)信息。

    redisClient 結(jié)構(gòu)主要包含以下信息:

    • 套接字描述符。
    • 客戶端正在使用的數(shù)據(jù)庫(kù)指針和數(shù)據(jù)庫(kù)號(hào)碼。
    • 客戶端的查詢緩沖(query buffer)和回復(fù)緩存(reply buffer)。
    • 一個(gè)指向命令函數(shù)的指針,以及字符串形式的命令、命令參數(shù)和命令個(gè)數(shù),這些屬性會(huì)在命令執(zhí)行時(shí)使用。
    • 客戶端狀態(tài):記錄了客戶端是否處于 SLAVE 、 MONITOR 或者事務(wù)狀態(tài)。
    • 實(shí)現(xiàn)事務(wù)功能(比如 MULTI 和 WATCH)所需的數(shù)據(jù)結(jié)構(gòu)。
    • 實(shí)現(xiàn)阻塞功能(比如 BLPOP 和 BRPOPLPUSH)所需的數(shù)據(jù)結(jié)構(gòu)。
    • 實(shí)現(xiàn)訂閱與發(fā)布功能(比如 PUBLISH 和 SUBSCRIBE)所需的數(shù)據(jù)結(jié)構(gòu)。
    • 統(tǒng)計(jì)數(shù)據(jù)和選項(xiàng):客戶端創(chuàng)建的時(shí)間,客戶端和服務(wù)器最后交互的時(shí)間,緩存的大小,等等。

    命令的請(qǐng)求、處理和結(jié)果返回

    當(dāng)客戶端連上服務(wù)器之后, 客戶端就可以向服務(wù)器發(fā)送命令請(qǐng)求了。

    從客戶端發(fā)送命令請(qǐng)求, 到命令被服務(wù)器處理、并將結(jié)果返回客戶端, 整個(gè)過(guò)程有以下步驟:

  • 客戶端通過(guò)套接字向服務(wù)器傳送命令協(xié)議數(shù)據(jù)。
  • 服務(wù)器通過(guò)讀事件來(lái)處理傳入數(shù)據(jù),并將數(shù)據(jù)保存在客戶端對(duì)應(yīng) redisClient 結(jié)構(gòu)的查詢緩存中。
  • 根據(jù)客戶端查詢緩存中的內(nèi)容,程序從命令表中查找相應(yīng)命令的實(shí)現(xiàn)函數(shù)。
  • 程序執(zhí)行命令的實(shí)現(xiàn)函數(shù),修改服務(wù)器的全局狀態(tài) server 變量,并將命令的執(zhí)行結(jié)果保存到客戶端 redisClient 結(jié)構(gòu)的回復(fù)緩存中,然后為該客戶端的 fd 關(guān)聯(lián)寫(xiě)事件。
  • 當(dāng)客戶端 fd 的寫(xiě)事件就緒時(shí),將回復(fù)緩存中的命令結(jié)果傳回給客戶端。至此,命令執(zhí)行完畢。
  • ?

    總結(jié)

    以上是生活随笔為你收集整理的redis(12)--事件,客户端,服务器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。