溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

redis server多路復(fù)用機(jī)制是什么?

發(fā)布時(shí)間:2020-05-21 11:16:19 來(lái)源:億速云 閱讀:1331 作者:Leah 欄目:系統(tǒng)運(yùn)維

redis server多路復(fù)用機(jī)制是什么?相信大部分人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,話不多說(shuō),一起往下看吧。

redis server 啟動(dòng)時(shí)調(diào)用bind() 傳入文件描述符fd6 綁定端口6379,調(diào)用listen()監(jiān)聽(tīng)端口,并通過(guò)accept() 等待連接

root@pmghong-VirtualBox:/usr/local/redis/bin# strace -ff -o /data/redis_strace/redis ./redis-server 
root@pmghong-VirtualBox:/proc/22330/fd# ls /data/redis_strace/ -l
total 48
-rw-r--r-- 1 root root 34741 3月  14 10:37 redis.25102
-rw-r--r-- 1 root root   134 3月  14 10:37 redis.25105
-rw-r--r-- 1 root root   134 3月  14 10:37 redis.25106
-rw-r--r-- 1 root root   134 3月  14 10:37 redis.25107

root@pmghong-VirtualBox:/proc/22330/fd# vi /data/redis_strace/redis.25102
... ...
epoll_create(1024)                      = 5
socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP) = 6
setsockopt(6, SOL_IPV6, IPV6_V6ONLY, [1], 4) = 0
setsockopt(6, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(6, {sa_family=AF_INET6, sin6_port=htons(6379), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
listen(6, 511)                          = 0
fcntl(6, F_GETFL)                       = 0x2 (flags O_RDWR)
fcntl(6, F_SETFL, O_RDWR|O_NONBLOCK)    = 0
... ...
root@pmghong-VirtualBox:/proc/25102/fd# ll
total 0
dr-x------ 2 root root  0 3月  14 12:05 ./
dr-xr-xr-x 9 root root  0 3月  14 10:37 ../
lrwx------ 1 root root 64 3月  14 12:28 0 -> /dev/pts/0
lrwx------ 1 root root 64 3月  14 12:28 1 -> /dev/pts/0
lrwx------ 1 root root 64 3月  14 12:05 2 -> /dev/pts/0
lr-x------ 1 root root 64 3月  14 12:28 3 -> pipe:[104062]
l-wx------ 1 root root 64 3月  14 12:28 4 -> pipe:[104062]
lrwx------ 1 root root 64 3月  14 12:28 5 -> anon_inode:[eventpoll]
lrwx------ 1 root root 64 3月  14 12:28 6 -> socket:[104063]
lrwx------ 1 root root 64 3月  14 12:28 7 -> socket:[104064]
lrwx------ 1 root root 64 3月  14 12:28 8 -> socket:[256344]

第一階段:BIO(阻塞IO)

redis server多路復(fù)用機(jī)制是什么?

Redis Server 啟動(dòng)后通過(guò)文件描述符fd6 監(jiān)聽(tīng)系統(tǒng)內(nèi)核

Client1 / Client2 分別通過(guò)fd7,fd8 請(qǐng)求訪問(wèn)redis

在BIO的場(chǎng)景下,redis server 會(huì)調(diào)用read()方法并進(jìn)入阻塞狀態(tài),也就是直到fd7 有請(qǐng)求過(guò)來(lái),處理完才能處理其他請(qǐng)求

這個(gè)模式缺點(diǎn)很明顯,就是阻塞IO導(dǎo)致效率低

第二階段 NIO (非阻塞IO)

redis server多路復(fù)用機(jī)制是什么?

跟BIO的區(qū)別在于,調(diào)用read(fd7) 時(shí),如果沒(méi)有請(qǐng)求數(shù)據(jù),立即給redis server 返回一個(gè)錯(cuò)誤

redis server 收到該類型的錯(cuò)誤即可知道當(dāng)前連接沒(méi)有數(shù)據(jù)過(guò)來(lái),可以繼續(xù)處理下一個(gè)請(qǐng)求,提高處理效率

bind(6, {sa_family=AF_INET6, sin6_port=htons(6379), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
listen(6, 511)                          = 0
fcntl(6, F_GETFL)                       = 0x2 (flags O_RDWR)
fcntl(6, F_SETFL, O_RDWR|O_NONBLOCK)    = 0

該模式的問(wèn)題在于,定時(shí)輪詢調(diào)用read(fdx)系統(tǒng)調(diào)用,當(dāng)多個(gè)client 請(qǐng)求過(guò)來(lái)時(shí),需要頻繁的進(jìn)行內(nèi)核態(tài)/用戶態(tài)切換,上下文切換開(kāi)銷大

第三階段 select 同步非阻塞

redis server多路復(fù)用機(jī)制是什么?

int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);

select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class
of I/O operation (e.g., input possible).  A file descriptor is considered ready if it is possible to perform a corresponding I/O  operation  (e.g.,  read(2)
without blocking, or a sufficiently small write(2)).

目標(biāo)是同時(shí)監(jiān)聽(tīng)多個(gè)fd,直到一個(gè)或者多個(gè)fd 進(jìn)入ready 狀態(tài),才會(huì)調(diào)用read()等系統(tǒng)調(diào)用處理業(yè)務(wù)邏輯,而不像上述的NIO場(chǎng)景下,需要輪詢調(diào)用x個(gè)read()

select 只能解決事件通知問(wèn)題(即哪些進(jìn)程能讀,哪些不能讀的問(wèn)題),但到了內(nèi)核態(tài),仍需在內(nèi)核中遍歷x個(gè)fd,看哪個(gè)client 發(fā)生了IO,再通知select 把結(jié)果集返回給server端,接著由sever端向指定的fd發(fā)起read() 系統(tǒng)調(diào)用

第四階段 epoll 多路復(fù)用

redis server多路復(fù)用機(jī)制是什么?

epoll 機(jī)制包括 epoll_create / epoll_ctl / epoll_wait 3個(gè)系統(tǒng)調(diào)用

// epoll_create
// 說(shuō)明
epoll_create() creates an epoll(7) instance.

//函數(shù)簽名
int epoll_create(int size);

//返回值 
On success, these system calls return a nonnegative file descriptor.  
On error, -1 is returned, and errno is set to indicate the error.

//epoll_ctl
//說(shuō)明
This  system  call  performs control operations on the epoll(7) instance referred to by the file descriptor epfd.  It requests that the operation op be per‐
formed for the target file descriptor, fd.

//函數(shù)簽名
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

// op 類型 EPOLL_CTL_ADD / EPOLL_CTL_MOD  /EPOLL_CTL_DEL

// 返回值
When successful, epoll_ctl() returns zero.  
When an error occurs, epoll_ctl() returns -1 and errno is set appropriately.

//epoll_ctl
//說(shuō)明
The  epoll_wait()  system call waits for events on the epoll(7) instance referred to by the file descriptor epfd.  The memory area pointed to by events will
contain the events that will be available for the caller.  Up to maxevents are returned by epoll_wait().  The maxevents argument must be greater than zero.

//函數(shù)簽名
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

//返回值
When successful, epoll_wait() returns the number of file descriptors ready for the requested I/O, or zero if no file  descriptor  became  ready  during  the
requested timeout milliseconds.  When an error occurs, epoll_wait() returns -1 and errno is set appropriately.
epoll_create(1024)                      = 5
... ...
bind(6, {sa_family=AF_INET6, sin6_port=htons(6379), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
listen(6, 511)                          = 0
... ...
bind(7, {sa_family=AF_INET, sin_port=htons(6379), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(7, 511)                          = 0
... ...
epoll_ctl(5, EPOLL_CTL_ADD, 6, {EPOLLIN, {u32=6, u64=6}}) = 0
epoll_ctl(5, EPOLL_CTL_ADD, 7, {EPOLLIN, {u32=7, u64=7}}) = 0
epoll_ctl(5, EPOLL_CTL_ADD, 3, {EPOLLIN, {u32=3, u64=3}}) = 0

write(...)
read(...)
epoll_wait(5, [], 10128, 0)             = 0

1、進(jìn)程啟動(dòng)時(shí)通過(guò)epoll_create() 創(chuàng)建epoll instance,成功時(shí)返回一個(gè)非負(fù)數(shù)的fdn,失敗返回-1還有錯(cuò)誤碼

2、調(diào)用epoll_ctl(上一步epoll_create 返回的fd,op,fd6,事件類型<accpet>)

3、調(diào)用epoll_wait() 監(jiān)聽(tīng)內(nèi)核事件,調(diào)用成功時(shí)返回該fd。例如當(dāng)c1請(qǐng)求redisserver 時(shí),首先需要通過(guò)fd6建立連接,此時(shí)通過(guò)epoll_ctl() 中對(duì)fd6 的accept()調(diào)用可以監(jiān)聽(tīng)到該請(qǐng)求,并將fd6傳給epoll_wait()

4、redis server端 從epoll_wait() 獲取需要IO操作的fd,發(fā)現(xiàn)c1 通過(guò)fd6請(qǐng)求建立連接,為其分配fd7,并在epoll_ctl()注冊(cè)一個(gè)監(jiān)聽(tīng),例如epoll_ctl(fdn,op, fd7, <read>)

更多相關(guān)知識(shí)點(diǎn)文章:

redis多路復(fù)用原理是什么以及常見(jiàn)問(wèn)題有哪些

redis多路復(fù)用技術(shù)的示例分析

看完上述內(nèi)容,你們對(duì)redis server的多路復(fù)用機(jī)制大概了解了嗎?如果想了解更多相關(guān)文章內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!


向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI