redis server多路復(fù)用機制是什么?相信大部分人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,話不多說,一起往下看吧。
創(chuàng)新互聯(lián)公司主要從事做網(wǎng)站、成都網(wǎng)站設(shè)計、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)肇州,十載網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):028-86922220
redis server 啟動時調(diào)用bind() 傳入文件描述符fd6 綁定端口6379,調(diào)用listen()監(jiān)聽端口,并通過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 啟動后通過文件描述符fd6 監(jiān)聽系統(tǒng)內(nèi)核
Client1 / Client2 分別通過fd7,fd8 請求訪問redis
在BIO的場景下,redis server 會調(diào)用read()方法并進入阻塞狀態(tài),也就是直到fd7 有請求過來,處理完才能處理其他請求
這個模式缺點很明顯,就是阻塞IO導(dǎo)致效率低
第二階段 NIO (非阻塞IO)
跟BIO的區(qū)別在于,調(diào)用read(fd7) 時,如果沒有請求數(shù)據(jù),立即給redis server 返回一個錯誤
redis server 收到該類型的錯誤即可知道當前連接沒有數(shù)據(jù)過來,可以繼續(xù)處理下一個請求,提高處理效率
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
該模式的問題在于,定時輪詢調(diào)用read(fdx)系統(tǒng)調(diào)用,當多個client 請求過來時,需要頻繁的進行內(nèi)核態(tài)/用戶態(tài)切換,上下文切換開銷大
第三階段 select 同步非阻塞
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)).
目標是同時監(jiān)聽多個fd,直到一個或者多個fd 進入ready 狀態(tài),才會調(diào)用read()等系統(tǒng)調(diào)用處理業(yè)務(wù)邏輯,而不像上述的NIO場景下,需要輪詢調(diào)用x個read()
select 只能解決事件通知問題(即哪些進程能讀,哪些不能讀的問題),但到了內(nèi)核態(tài),仍需在內(nèi)核中遍歷x個fd,看哪個client 發(fā)生了IO,再通知select 把結(jié)果集返回給server端,接著由sever端向指定的fd發(fā)起read() 系統(tǒng)調(diào)用
第四階段 epoll 多路復(fù)用
epoll 機制包括 epoll_create / epoll_ctl / epoll_wait 3個系統(tǒng)調(diào)用
// epoll_create // 說明 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 //說明 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 //說明 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、進程啟動時通過epoll_create() 創(chuàng)建epoll instance,成功時返回一個非負數(shù)的fdn,失敗返回-1還有錯誤碼
2、調(diào)用epoll_ctl(上一步epoll_create 返回的fd,op,fd6,事件類型
3、調(diào)用epoll_wait() 監(jiān)聽內(nèi)核事件,調(diào)用成功時返回該fd。例如當c1請求redisserver 時,首先需要通過fd6建立連接,此時通過epoll_ctl() 中對fd6 的accept()調(diào)用可以監(jiān)聽到該請求,并將fd6傳給epoll_wait()
4、redis server端 從epoll_wait() 獲取需要IO操作的fd,發(fā)現(xiàn)c1 通過fd6請求建立連接,為其分配fd7,并在epoll_ctl()注冊一個監(jiān)聽,例如epoll_ctl(fdn,op, fd7,
看完上述內(nèi)容,你們對redis server的多路復(fù)用機制大概了解了嗎?如果想了解更多相關(guān)文章內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!