深入淺出 Redis client/server交互流程

綜述

最近筆者閱讀并研究redis源碼吆视,在redis客戶端與服務器端交互這個內(nèi)容點上啦吧,需要參考網(wǎng)上一些文章授滓,但是遺憾的是發(fā)現(xiàn)大部分文章都斷斷續(xù)續(xù)的非系統(tǒng)性的肆糕,不能給讀者此交互流程的整體把握诚啃。所以這里我嘗試始赎,站在源碼的角度仔燕,將redis client/server 交互流程盡可能簡單地展現(xiàn)給大家晰搀,同時也站在DBA的角度給出一些日常工作中注意事項厕隧。

Redis client/server 交互步驟分為以下6個步驟:

一俄周、Client 發(fā)起socket 連接

二峦朗、Server 接受socket連接

三波势、客戶端 開始寫入

四、server 端接收寫入

五拴曲、server 返回寫入結果

六澈灼、Client收到返回結果

注:為使文章盡可能簡潔店溢,這里只討論客戶端命令寫入的過程床牧,不討論客戶端命令讀取的流程戈咳。

在進一步閱讀和了解互動流程之前,請大家確保已經(jīng)熟練掌握了Linux Socket 建立流程和epoll I/O 多路復用技術兩個技術點删铃,這對文章內(nèi)容的理解至關重要泳姐。

交互的整體流程

在介紹6個步驟之前胖秒,首先看一下redis client/server 交互流程整體的程序執(zhí)行流程圖:

(點擊放大圖像)

上圖中6個步驟分別用不同的顏色箭頭表示阎肝,并且最終結果也用相對應的顏色標識。

首先看看綠色框里面的循環(huán)執(zhí)行的方法判导,最末是epoll_wait方法眼刃,即等待事件產(chǎn)生的方法擂红。然后再看第2围小、4肯适、5步驟的末尾都有epoll_ctl方法框舔,即epoll事件注冊函數(shù)雨饺。關于epoll的相關技術解析請參看文末一段。

在這里的循環(huán)還有個beforeSleep方法,其實它跟我們這次討論的話題沒有太大的關系移斩。但是還是想給大家介紹一下向瓷。

beforeSleep方法主要做以下幾件事:

執(zhí)行一次快速的主動過期檢查猖任,檢查是否有過期的key

當有客戶端阻塞時瓷耙,向所有從庫發(fā)送ACK請求

unblock 在同步復制時候被阻塞的客戶端

嘗試執(zhí)行之前被阻塞客戶端的命令

將AOF緩沖區(qū)的內(nèi)容寫入到AOF文件中

如果是集群,將會根據(jù)需要執(zhí)行故障遷移宇弛、更新節(jié)點狀態(tài)枪芒、保存node.conf 配置文件谁尸。

如此良蛮,redis整個事件管理器機制就比較清楚了背镇。接下來進一步探討并理解事件是如何觸發(fā)并創(chuàng)建瞒斩。

交互的六大步驟

下面正式開始介紹redis client/server 交互的6大步驟

一、Client 發(fā)起socket 連接

(點擊放大圖像)

這里以redis-cli 客戶端為例,當執(zhí)行以下語句時:

[root@zbdba redis-3.0]# ./src/redis-cli -p 6379 -h 127.0.0.1

127.0.0.1:6379>

客戶端會做如下操作:

1裳扯、獲取客戶端參數(shù)谤职,如端口允蜈、ip地址饶套、dbnum妓蛮、socket等

也就是我們執(zhí)行./src/redis-cli --help 中列出的參數(shù)

2、根據(jù)用戶指定參數(shù)確定客戶端處于哪種模式

目前共有:

Latency mode/Slave mode/Get RDB mode/Pipe mode/Find

big keys/Stat mode/Scan mode/Intrinsic latency mode

以上8種模式

例如:stat 模式

[root@zbdba redis-3.0]# ./src/redis-cli -p 6379 -h 127.0.0.1 --stat

------- data ------ --------------------- load -------------------- - child -

keys? ? ? mem? ? ? clients blocked requests? ? ? ? ? ? connections? ? ? ?

1? ? ? ? ? 817.18K? 2? ? ? 0? ? ? 1 (+0)? ? ? ? ? ? ? 2? ? ? ? ?

1? ? ? ? ? 817.18K? 2? ? ? 0? ? ? 2 (+1)? ? ? ? ? ? ? 2? ? ? ? ?

1? ? ? ? ? 817.18K? 2? ? ? 0? ? ? 3 (+1)? ? ? ? ? ? ? 2? ? ? ? ?

1? ? ? ? ? 817.18K? 2? ? ? 0? ? ? 4 (+1)? ? ? ? ? ? ? 2? ? ? ? ?

1? ? ? ? ? 817.18K? 2? ? ? 0? ? ? 5 (+1)? ? ? ? ? ? ? 2? ? ? ? ?

1? ? ? ? ? 817.18K? 2? ? ? 0? ? ? 6 (+1)? ? ? ? ? ? ? 2

我們這里沒有指定夷蚊,就是默認的模式撬码。

3版保、進入上圖中step1的cliConnect 方法彻犁,cliConnect主要包含redisConnect汞幢、redisConnectUnix方法森篷。這兩個方法分別用于TCP Socket連接以及Unix Socket連接,Unix Socket用于同一主機進程間的通信买乃。我們上面是采用的TCP Socket連接方式也就是我們平常生產(chǎn)環(huán)境常用的方式剪验,這里不討論Unix Socket連接方式功戚,如果要使用Unix Socket連接方式啸臀,需要配置unixsocket 參數(shù)烁落,并且按照下面方式進行連接:

[root@zbdba redis-3.0]# ./src/redis-cli -s /tmp/redis.sock

redis /tmp/redis.sock>

4顽馋、進入redisContextInit方法寸谜,redisContextInit方法用于創(chuàng)建一個Context結構體保存在內(nèi)存中熊痴,如下:

/* Context for a connection to Redis */

typedef struct redisContext {

? ? int err; /* Error flags, 0 when there is no error */

? ? char errstr[128]; /* String representation of error when applicable */

? ? int fd;

? ? int flags;

? ? char *obuf; /* Write buffer */

? ? redisReader *reader; /* Protocol reader */

} redisContext;

主要用于保存客戶端的一些東西果善,最重要的就是 write buffer和redisReader巾陕,write buffer 用于保存客戶端的寫入鄙煤,redisReader用于保存協(xié)議解析器的一些狀態(tài)梯刚。

5、進入redisContextConnectTcp 方法澜共,開始獲取IP地址和端口用于建立連接嗦董,主要方法如下:

s = socket(p->ai_family,p->ai_socktype,p->ai_protocol

connect(s,p->ai_addr,p->ai_addrlen)

到此客戶端向服務端發(fā)起建立socket連接展懈,并且等待服務器端響應存崖。

當然cliConnect方法中還會調(diào)用cliAuth方法用于權限驗證来惧、cliSelect用于db選擇,這里不著重討論妓忍。

二、Server 接受socket連接

(點擊放大圖像)

服務器接收客戶端的請求首先是從epoll_wait取出相關的事件屿脐,然后進入上圖中step2中的方法的诵,執(zhí)行acceptTcpHandler或者acceptUnixHandler方法西疤,那么這兩個方法對應的事件是在什么時候注冊的呢代赁?他們是在服務器端初始化的時候創(chuàng)建管跺。下面看看服務器端在初始化的時候與socket相關的地方

1豁跑、打開TCP監(jiān)聽端口

? ? if (server.port != 0 &&

? ? ? ? listenToPort(server.port,server.ipfd,&server.ipfd_count) == REDIS_ERR)

? ? ? ? exit(1);

2艇拍、打開unix 本地端口

? if (server.unixsocket != NULL) {

? ? ? ? unlink(server.unixsocket); /* don't care if this fails */

? ? ? ? server.sofd = anetUnixServer(server.neterr,server.unixsocket,

? ? ? ? ? ? server.unixsocketperm, server.tcp_backlog);

? ? ? ? if (server.sofd == ANET_ERR) {

? ? ? ? ? ? redisLog(REDIS_WARNING, "Opening socket: %s", server.neterr);

? ? ? ? ? ? exit(1);

? ? ? ? }

? ? ? ? anetNonBlock(NULL,server.sofd);

? ? }

3层释、為TCP連接關聯(lián)連接應答處理器(accept)

? for (j = 0; j < server.ipfd_count; j++) {

? ? ? ? if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,

? ? ? ? ? ? acceptTcpHandler,NULL) == AE_ERR)

? ? ? ? ? ? {

? ? ? ? ? ? ? ? redisPanic(

? ? ? ? ? ? ? ? ? ? "Unrecoverable error creating server.ipfd file event.");

? ? ? ? ? ? }

? ? }

4贡羔、為Unix Socket關聯(lián)應答處理器

if (server.sofd > 0 && aeCreateFileEvent

? ? (server.el,server.sofd,AE_READABLE,

? ? ? ? acceptUnixHandler,NULL) == AE_ERR)

redisPanic("Unrecoverable error creating server.sofd file event.");

在1/2步驟涉及到的方法中是Linux Socket的常規(guī)操作乖寒,獲取IP地址楣嘁,端口。最終通過socket叭爱、bind涤伐、listen方法建立起Socket監(jiān)聽凝果。也就是上圖中acceptTcpHandler和acceptUnixHandler下面對應的方法器净。

在3/4步驟涉及到的方法中采用aeCreateFileEvent 方法創(chuàng)建相關的連接應答處理器山害,在客戶端請求連接的時候觸發(fā)浪慌。

所以現(xiàn)在整個socket連接建立流程就比較清楚了权纤,如下:

服務器初始化建立socket監(jiān)聽

服務器初始化創(chuàng)建相關連接應答處理器,通過epoll_ctl注冊事件

客戶端初始化創(chuàng)建socket connect 請求

服務器接受到請求汹想,用epoll_wait方法取出事件

服務器執(zhí)行事件中的方法(acceptTcpHandler/acceptUnixHandler)并接受socket連接

至此客戶端和服務器端的socket連接已經(jīng)建立古掏,但是此時服務器端還繼續(xù)做了2件事:

采用createClient方法在服務器端為客戶端創(chuàng)建一個client槽唾,因為I/O復用所以需要為每個客戶端維持一個狀態(tài)庞萍。這里的client也在內(nèi)存中分配了一塊區(qū)域挂绰,用于保存它的一些信息葵蒂,如套接字描述符践付、默認數(shù)據(jù)庫永高、查詢緩沖區(qū)命爬、命令參數(shù)饲宛、認證狀態(tài)艇抠、回復緩沖區(qū)等家淤。這里提醒一下DBA同學關于client-output-buffer-limit設置絮重,設置不恰當將會引起客戶端中斷绿鸣。

采用aeCreateFileEvent方法在服務器端創(chuàng)建一個文件讀事件并且綁定readQueryFromClient方法潮模。

可以從圖中得知擎厢,aeCreateFileEvent 調(diào)用aeApiAddEvent方法最終通過epoll_ctl 方法進行注冊事件动遭。

三厘惦、客戶端開始寫入

(點擊放大圖像)

客戶端在與服務器端建立好socket連接之后宵蕉,開始執(zhí)行上圖中step3的repl方法羡玛。從圖中可知repl方法接受輸入輸出主要是采用linenoise插件薄榛。當然這是針對redis-cli客戶端哦敞恋。linenoise 是一款優(yōu)秀的命令行編輯庫,被廣泛的運用在各種DB上耳舅,如Redis碌上、MongoDB,這里不詳細討論浦徊。客戶端寫入流程分為以下幾步:

1天梧、linenoise等待接受用戶輸入

2盔性、linenoise 將用戶輸入內(nèi)容傳入cliSendCommand方法,cliSendCommand方法會判斷命令是否為特殊命令呢岗,如:

help

info

cluster nodes

cluster info

client list

shutdown

monitor

subscribe

psubscribe

sync

psync

客戶端會根據(jù)以上命令設置對應的輸出格式以及客戶端的模式,因為這里我們是普通寫入后豫,所以不會涉及到以上的情況悉尾。

3、cliSendCommand方法會調(diào)用redisAppendCommandArgv方法挫酿,redisAppendCommandArgv方法會調(diào)用redisFormatCommandArgv和__redisAppendCommand方法

redisFormatCommandArgv方法用于將客戶端輸入的內(nèi)容格式化成redis協(xié)議:

例如:

set zbdba jingbo

*3\r\n$3\r\n set\r\n $5\r\n zbdba\r\n $6\r\n jingbo

__redisAppendCommand方法用于將命令寫入到outbuf中

接著客戶端進入下一個流程构眯,將outbuf內(nèi)容寫入到套接字描述符上并傳輸?shù)椒掌鞫恕?/p>

4、進入redisGetReply方法早龟,該方法下主要有redisGetReplyFromReader和redisBufferWrite 方法惫霸,redisGetReplyFromReader主要用于讀取掛起的回復,redisBufferWrite 方法用于將當前outbuf中的內(nèi)容寫入到套接字描述符中葱弟,并傳輸內(nèi)容壹店。

主要方法如下:

nwritten = write(c->fd,c->obuf,sdslen(c->obuf));

此時客戶端等待服務器端接收寫入。

四芝加、server 端接收寫入

(點擊放大圖像)

服務器端依然在進行事件循環(huán)硅卢,在客戶端發(fā)來內(nèi)容的時候觸發(fā),對應的文件讀取事件藏杖。這就是之前創(chuàng)建socket連接的時候建立的事件将塑,該事件綁定的方法是readQueryFromClient 。此時進入step4的readQueryFromClient 方法制市。

readQueryFromClient 方法用于讀取客戶端的發(fā)送的內(nèi)容抬旺。它的執(zhí)行步驟如下:

1、在readQueryFromClient方法中從服務器端套接字描述符中讀取客戶端的內(nèi)容到服務器端初始化client的查詢緩沖中祥楣,主要方法如下:

nread = read(fd, c->querybuf+qblen, readlen);

2开财、交給processInputBuffer處理汉柒,processInputBuffer 主要包含兩個方法,processInlineBuffer和processCommand责鳍。processInlineBuffer方法用于采用redis協(xié)議解析客戶端內(nèi)容并生成對應的命令并傳給processCommand 方法碾褂,processCommand方法則用于執(zhí)行該命令

3、processCommand方法會以下操作:

處理是否為quit命令历葛。

對命令語法及參數(shù)會進行檢查正塌。

這里如果采取認證也會檢查認證信息。

如果Redis為集群模式恤溶,這里將進行hash計算key所屬slot并進行轉(zhuǎn)向操作乓诽。

如果設置最大內(nèi)存,那么檢查內(nèi)存是否超過限制咒程,如果超過限制會根據(jù)相應的內(nèi)存策略刪除符合條件的鍵來釋放內(nèi)存

如果這是一個主服務器鸠天,并且這個服務器之前執(zhí)行bgsave發(fā)生了錯誤,那么不執(zhí)行命令

如果min-slaves-to-write開啟帐姻,如果沒有足夠多的從服務器將不會執(zhí)行命令

注:所以DBA在此的設置非常重要稠集,建議不是特殊場景不要設置。

如果這個服務器是一個只讀從庫的話饥瓷,拒絕寫入命令剥纷。

在訂閱于發(fā)布模式的上下文中,只能執(zhí)行訂閱和退訂相關的命令

當這個服務器是從庫呢铆,master_link down 并且slave-serve-stale-data 為 no 只允許info 和slaveof命令

如果服務器正在載入數(shù)據(jù)到數(shù)據(jù)庫晦鞋,那么只執(zhí)行帶有REDIS_CMD_LOADING標識的命令

lua腳本超時,只允許執(zhí)行限定的操作刺洒,比如shutdown鳖宾、script kill 等

4、最后進入call方法逆航。

call方法會調(diào)用setCommand鼎文,因為這里我們執(zhí)行的set zbdba jingbo,set 命令對應setCommand 方法因俐,redis服務器端在開始初始化的時候就會初始化命令表拇惋,命令表如下:

struct redisCommand redisCommandTable[] = {

? ? {"get",getCommand,2,"r",0,NULL,1,1,1,0,0},

? ? {"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},

? ? {"setnx",setnxCommand,3,"wm",0,NULL,1,1,1,0,0},

? ? {"setex",setexCommand,4,"wm",0,NULL,1,1,1,0,0},

? ? {"psetex",psetexCommand,4,"wm",0,NULL,1,1,1,0,0},

? ? {"append",appendCommand,3,"wm",0,NULL,1,1,1,0,0},

? ? {"strlen",strlenCommand,2,"r",0,NULL,1,1,1,0,0},

? ? {"del",delCommand,-2,"w",0,NULL,1,-1,1,0,0},

? ? {"exists",existsCommand,2,"r",0,NULL,1,1,1,0,0},

? ? {"setbit",setbitCommand,4,"wm",0,NULL,1,1,1,0,0},

? ? ....

}

所以如果是其他的命令會調(diào)用其他相對應的方法。call方法還會做一些事件抹剩,比如發(fā)送命令到從庫撑帖、發(fā)送命令到aof、計算命令執(zhí)行的時間澳眷。

5胡嘿、setCommand方法,setCommand方法會調(diào)用setGenericCommand方法钳踊,該方法首先會判斷該key是否已經(jīng)過期衷敌,最后調(diào)用setKey方法勿侯。

這里需要說明一點的是,通過以上的分析缴罗。redis的key過期包括主動檢測以及被動監(jiān)測

主動監(jiān)測

在beforeSleep方法中執(zhí)行key快速過期檢查助琐,檢查模式為ACTIVE_EXPIRE_CYCLE_FAST。周期為每個事件執(zhí)行完成時間到下一次事件循環(huán)開始

在serverCron方法中執(zhí)行key過期檢查面氓,這是key過期檢查主要的地方兵钮,檢查模式為ACTIVE_EXPIRE_CYCLE_SLOW,serverCron方法執(zhí)行周期為1秒鐘執(zhí)行server.hz 次舌界,hz默認為10掘譬,所以約100ms執(zhí)行一次。hz設置越大過期鍵刪除就越精準呻拌,但是cpu使用率會越高屁药,這里我們線上redis采用的默認值。redis主要是在這個方法里刪除大部分的過期鍵柏锄。

被動監(jiān)測

使用內(nèi)存超過最大內(nèi)存被迫根據(jù)相應的內(nèi)存策略刪除符合條件的key复亏。

在key寫入之前進行被動檢查趾娃,檢查key是否過期,過期就進行刪除缔御。

還有一種不友好的方式抬闷,就是randomkey命令,該命令隨機從redis獲取鍵耕突,每次獲取到鍵的時候會檢查該鍵是否過期笤成。

以上主要是讓運維的同學更加清楚redis的key過期刪除機制。

6眷茁、進入setKey方法炕泳,setKey方法最終會調(diào)用dbAdd方法,其實最終就是將該鍵值對存入服務器端維護的一個字典中上祈,該字典是在服務器初始化的時候創(chuàng)建培遵,用于存儲服務器的相關信息,其中包括各種數(shù)據(jù)類型的鍵值存儲登刺。完成了寫入方法時候籽腕,此時服務器端會給客戶端返回結果。

7纸俭、進入prepareClientToWrite方法然后通過調(diào)用_addReplyToBuffer方法將返回結果寫入到outbuf中(客戶端連接時創(chuàng)建的client)

8皇耗、通過aeCreateFileEvent方法注冊文件寫事件并綁定sendReplyToClient方法

五、server 返回寫入結果

(點擊放大圖像)

此時按照慣例揍很,aeMain主函數(shù)循環(huán)郎楼,監(jiān)測到新注冊的事件万伤,調(diào)用sendReplyToClient方法。sendReplyToClient方法主要包含兩個操作:

1箭启、將outbuf內(nèi)容寫入到套接字描述符并傳輸?shù)娇蛻舳撕爵妫饕椒ㄈ缦拢?/p>

nwritten = write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen);

2、aeDeleteFileEvent 用于刪除 文件寫事件

六傅寡、Client收到返回結果

(點擊放大圖像)

客戶端接收到服務器端的返回調(diào)用redisBufferRead方法放妈,該方法主要用于從socket中讀取數(shù)據(jù)。主要方法如下:

nread = read(c->fd,buf,sizeof(buf));

并且將讀取的數(shù)據(jù)交由redisReaderFeed方法荐操,該方法主要用于將數(shù)據(jù)交給回復解析器處理芜抒,也就是cliFormatReplyRaw,該方法將回復內(nèi)容格式化托启。最終通過

fwrite(out,sdslen(out),1,stdout);

方法返回給客戶端并打印展示給用戶宅倒。

至此整個寫入流程完成。以上還有很多細節(jié)沒有說到屯耸,感興趣的朋友可以自行閱讀源碼拐迁。

結語

在深入了解一個DB的時候,我的第一步就是去理解它執(zhí)行一條命令執(zhí)行的整個流程疗绣,這樣就能對它整個運行流程較為熟悉线召,接著我們可以去深入各個細節(jié)的部分,比如Redis的相關數(shù)據(jù)結構多矮、持久化以及高可用相關的東西缓淹。寫這篇文章的初衷就是希望我們更加輕松的走好這第一步。這里還需要提醒的是塔逃,在我們進行Redis源碼閱讀的時候最關鍵的是需要靈活的使用GDB調(diào)試工具讯壶,它能幫我們更好的去理順相關執(zhí)行步驟,從而讓我們更加容易理解其實現(xiàn)原理湾盗。

附錄:兩個相關重要知識點

1伏蚊、Linux Socket 建立流程

(點擊放大圖像)

linux socket建立過程如上圖所示。在Linux編程時淹仑,無論是操作文件還是網(wǎng)絡操作時都是通過文件描述符來進行讀寫的丙挽,但是他們有一點區(qū)別,這里我們不具體討論匀借,我們將網(wǎng)絡操作時就稱為套接字描述符颜阐。大家可以自行用c寫一個簡單的demo,這里就不詳細說明了吓肋。

這里列出幾個重要的方法:

int socket(int family,int type,int protocol);

int connect(int sockfd,const struct sockaddr * servaddr,socklen_taddrlen);

int bind(int sockfd,const struct sockaddr * myaddr,socklen_taddrlen);

int listen(int sockfd,int backlog);

int accept(int sockfd,struct sockaddr *cliaddr,socklen_t * addrlen);

Redis client/server 也是基于linux socket連接進行交互凳怨,并且最終調(diào)用以上方法綁定IP,監(jiān)聽端口最終與客戶端建立連接。

2肤舞、epoll I/O 多路復用技術

這里重點介紹一下epoll紫新,因為Redis事件管理器核心實現(xiàn)基本依賴于它。首先來看epoll是什么李剖,它能做什么芒率?

epoll是在Linux 2.6內(nèi)核中引進的,是一種強大的I/O多路復用技術篙顺,上面我們已經(jīng)說到在進行網(wǎng)絡操作的時候是通過文件描述符來進行讀寫的偶芍,那么平常我們就是一個進程操作一個文件描述符。然而epoll可以通過一個文件描述符管理多個文件描述符德玫,并且不阻塞I/O匪蟀。這使得我們單進程可以操作多個文件描述符,這就是redis在高并發(fā)性能還如此強大的原因之一宰僧。

下面簡單介紹epoll 主要的三個方法:

int epoll_create(int size) //創(chuàng)建一個epoll句柄用于監(jiān)聽文件描述符FD材彪,size用于告訴內(nèi)核這個監(jiān)聽的數(shù)目一共有多大。該epoll句柄創(chuàng)建后在操作系統(tǒng)層面只會占用一個fd值琴儿,但是它可以監(jiān)聽size+1 個文件描述符段化。

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)//epoll事件注冊函數(shù)

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)//等待事件的產(chǎn)生

Redis 的事件管理器主要是基于epoll機制,先采用 epoll_ctl方法 注冊事件造成,然后再使用epoll_wait方法取出已經(jīng)注冊的事件穗泵。

我們知道redis支持多種平臺,那么redis在這方面是如何兼容其他平臺的呢谜疤?Redis會根據(jù)操作系統(tǒng)的類型選擇對應的IO多路復用實現(xiàn)。

#ifdef HAVE_EVPORT

#include "ae_evport.c"

#else

? ? #ifdef HAVE_EPOLL

? ? #include "ae_epoll.c"

? ? #else

? ? ? ? #ifdef HAVE_KQUEUE

? ? ? ? #include "ae_kqueue.c"

? ? ? ? #else

? ? ? ? #include "ae_select.c"

? ? ? ? #endif

? ? #endif

#endif

ae_evport.c sun solaris

ae_poll.c linux

ae_select.c unix/linux epoll是select的加強版

ae_kqueue BSD/Apple

以上只是簡單的介紹现诀,大家需要詳細了解了epoll機制才能更好的理解后面的東西夷磕。

參考

http://redis.io/

https://github.com/antirez/redis

http://www.tenouk.com/Module39a.html

感謝木環(huán)對本文的審校。

給InfoQ中文站投稿或者參與內(nèi)容翻譯工作仔沿,請郵件至editors@cn.infoq.com坐桩。也歡迎大家通過新浪微博(@InfoQ@丁曉昀)封锉,微信(微信號:InfoQChina)關注我們绵跷。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市成福,隨后出現(xiàn)的幾起案子碾局,更是在濱河造成了極大的恐慌,老刑警劉巖奴艾,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件净当,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機像啼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門俘闯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人忽冻,你說我怎么就攤上這事真朗。” “怎么了僧诚?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵遮婶,是天一觀的道長。 經(jīng)常有香客問我振诬,道長蹭睡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任赶么,我火速辦了婚禮肩豁,結果婚禮上,老公的妹妹穿的比我還像新娘辫呻。我一直安慰自己清钥,他們只是感情好,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布放闺。 她就那樣靜靜地躺著祟昭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪怖侦。 梳的紋絲不亂的頭發(fā)上篡悟,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天,我揣著相機與錄音匾寝,去河邊找鬼搬葬。 笑死,一個胖子當著我的面吹牛艳悔,可吹牛的內(nèi)容都是我干的急凰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼猜年,長吁一口氣:“原來是場噩夢啊……” “哼抡锈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起乔外,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤床三,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后杨幼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勿璃,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了补疑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歧沪。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖莲组,靈堂內(nèi)的尸體忽然破棺而出诊胞,到底是詐尸還是另有隱情,我是刑警寧澤锹杈,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布撵孤,位于F島的核電站,受9級特大地震影響竭望,放射性物質(zhì)發(fā)生泄漏邪码。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一咬清、第九天 我趴在偏房一處隱蔽的房頂上張望闭专。 院中可真熱鬧,春花似錦旧烧、人聲如沸影钉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽平委。三九已至,卻和暖如春夺谁,著一層夾襖步出監(jiān)牢的瞬間廉赔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工匾鸥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留昂勉,地道東北人。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓扫腺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親村象。 傳聞我的和親對象是個殘疾皇子笆环,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)厚者,斷路器躁劣,智...
    卡卡羅2017閱讀 134,633評論 18 139
  • 分布式緩存技術PK:選擇Redis還是Memcached账忘? 經(jīng)平臺同意授權轉(zhuǎn)載 作者:田京昆(騰訊后臺研發(fā)工程師)...
    meng_philip123閱讀 68,920評論 7 60
  • 大綱 一.Socket簡介 二.BSD Socket編程準備 1.地址 2.端口 3.網(wǎng)絡字節(jié)序 4.半相關與全相...
    VD2012閱讀 2,295評論 0 5
  • 一些概念: fromViewController是當前試圖控制器,toViewController是跳轉(zhuǎn)的視圖控制...
    火火哥閱讀 272評論 0 0
  • 選自《呂氏春秋》,由于作者不是一位孤單的編撰者鳖擒,而是呂不韋及其門客共同編著溉浙,所以書中的思想觀點有兼容并包的特點,雜...
    王家人寧閱讀 535評論 0 0