epoll LT/ET 深刻剖析nginx
EPOLL事件有兩種模型:git
Level Triggered (LT) 水平觸發
.socket接收緩衝區不爲空 有數據可讀 讀事件一直觸發
.socket發送緩衝區不滿 能夠繼續寫入數據 寫事件一直觸發
符合思惟習慣,epoll_wait返回的事件就是socket的狀態github
Edge Triggered (ET) 邊沿觸發
.socket的接收緩衝區狀態變化時觸發讀事件,即空的接收緩衝區剛接收到數據時觸發讀事件
.socket的發送緩衝區狀態變化時觸發寫事件,即滿的緩衝區剛空出空間時觸發讀事件
僅在狀態變化時觸發事件編程
ET仍是LT?服務器
LT的處理過程:
. accept一個鏈接,添加到epoll中監聽EPOLLIN事件
. 當EPOLLIN事件到達時,read fd中的數據並處理
. 當須要寫出數據時,把數據write到fd中;若是數據較大,沒法一次性寫出,那麼在epoll中監聽EPOLLOUT事件
. 當EPOLLOUT事件到達時,繼續把數據write到fd中;若是數據寫出完畢,那麼在epoll中關閉EPOLLOUT事件網絡
ET的處理過程:
. accept一個一個鏈接,添加到epoll中監聽EPOLLIN|EPOLLOUT事件
. 當EPOLLIN事件到達時,read fd中的數據並處理,read須要一直讀,直到返回EAGAIN爲止
. 當須要寫出數據時,把數據write到fd中,直到數據所有寫完,或者write返回EAGAIN
. 當EPOLLOUT事件到達時,繼續把數據write到fd中,直到數據所有寫完,或者write返回EAGAINsocket
從ET的處理過程當中能夠看到,ET的要求是須要一直讀寫,直到返回EAGAIN,不然就會遺漏事件。而LT的處理過程當中,直到返回EAGAIN不是硬性要求,但一般的處理過程都會讀寫直到返回EAGAIN,但LT比ET多了一個開關EPOLLOUT事件的步驟性能
LT的編程與poll/select接近,符合一直以來的習慣,不易出錯
ET的編程能夠作到更加簡潔,某些場景下更加高效,但另外一方面容易遺漏事件,容易產生bug測試
這裏有兩個簡單的例子演示了LT與ET的用法(其中epoll-et的代碼比epoll要少10行):
https://github.com/yedf/handy/blob/master/raw-examples/epoll.cc
https://github.com/yedf/handy/blob/master/raw-examples/epoll-et.cc事件
針對容易觸發LT開關EPOLLOUT事件的情景(讓服務器返回1M大小的數據),我用ab作了性能測試
測試的結果顯示ET的性能稍好,詳情以下:
LT 啓動命令 ./epoll a
ET 啓動命令 ./epoll-et a
ab 命令:ab -n 1000 -k 127.0.0.1/
LT 結果:Requests per second: 42.56 [#/sec] (mean)
ET 結果:Requests per second: 48.55 [#/sec] (mean)
當我把服務器返回的數據大小改成48576時,開關EPOLLOUT更加頻繁,性能的差別更大
ab 命令:ab -n 5000 -k 127.0.0.1/
LT 結果:Requests per second: 745.30 [#/sec] (mean)
ET 結果:Requests per second: 927.56 [#/sec] (mean)
對於nginx這種高性能服務器,ET模式是很好的,而其餘的通用網絡庫,更可能是使用LT,避免使用的過程當中出現bug