接上一篇 http://www.cnblogs.com/charlesblc/p/6241926.htmlphp
來源:html
https://zhuanlan.zhihu.com/p/20204159程序員
網上關於Apache和Nginx性能比較的文章很是多,基本上有以下的定論:編程
這裏咱們要着重討論的是爲何 Nginx在併發性能上比Apache要好不少。
非阻塞&事件驅動這麼好,爲何你們沒有一開始就採用這種方式呢? 緣由有二:瀏覽器
epoll是Linux內核的可擴展I/O事件通知機制。它設計目的只在取代既有POSIX select(2)與poll(2)系統函數,讓須要大量操做文件描述符的程序得以發揮更優異的性能 (舉例來講:舊有的系統函數所花費的時間複雜度爲O(n),epoll則耗時O(1))。 epoll與FreeBSD的kqueue相似,底層都是由可配置的操做系統內核對象建構而成, 並以文件描述符(file descriptor)的形式呈現於用戶空間。服務器
epoll由下面幾個系統調用組成:網絡
int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event); int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
爲了解決高併發問題,大約在2000年,Jonathan Lemon在FreeBSD內核中實現了第一個版本 的kqueue,並在FreeBSD 4.1版本發佈。以後FreeBSD在處理高併發的問題上一直領先於Linux。併發
各類操做系統在解決這個問題的辦法上也是百花齊放:框架
技術操做系統kqueueUNIX (FreeBSD、MacOS)epollLinux 2.5.44/2.6.9IOCP (IO Completion Port)Windows NT 3.5, AIX, Solaris 10第三篇
下面是WikiPedia對於libevent的介紹:異步
libevent是一個異步事件處理軟件函式庫,以BSD許可證發佈。 libevent提供了一組應用程序編程接口(API),讓程序員能夠設定某些事件發生時所執行的函式,也就是說,libevent能夠用來取代網絡服務器所使用的事件循環檢查框架。
因爲能夠省去對網絡的處理,且擁有不錯的效能, 有些軟件使用libevent做爲網絡底層的函式庫,如:Chromium(Chrome的開源版)、 memcached、Tor。
按照libevent的官方網站,libevent庫提供瞭如下功能:當一個文件描述符的特定事件 (如可讀,可寫或出錯)發生了,或一個定時事件發生了, libevent就會自動執行用戶指定的回調函數,來處理事件。
兩者的差別在於Level Triggered模式下只要某個socket處於readable/writable狀態, 不管何時進行epoll_wait都會返回該socket;
而Edge Triggered模式下只有某個socket從unreadable變爲readable或 從unwritable變爲writable時,epoll_wait纔會返回該socket。
TCP的「鏈接」僅僅是鏈接的兩端對於四元組和sequence號的一種約定而已。
在有些文章裏總會提到這名詞、或者五元組,甚至七元組。 雖然我很反對擺弄名詞秀專業,但咱們也要防止被「秀」。 其實很容易理解:
- 四元組: 源IP地址、目的IP地址、源端口、目的端口
- 五元組: 源IP地址、目的IP地址、協議、源端口、目的端口
- 七元組: 源IP地址、目的IP地址、協議、源端口、目的端口,服務類型,接口索引
在 HTTP 1.0 中, 沒有官方的 keepalive 的操做。一般是在現有協議上添加一個指數。 若是瀏覽器支持 keep-alive,它會在請求的包頭中添加:
Connection: Keep-Alive
而後當服務器收到請求,做出迴應的時候,它也添加一個頭在響應中:
Connection: Keep-Alive
這樣作,鏈接就不會中斷,而是保持鏈接。
在 HTTP 1.1 中 全部的鏈接默認都是持續鏈接,除非特殊聲明不支持。爲了規避上面說的對圖片等靜態資源的影響,大多數商業網站會啓用獨立的靜態資源域名。 從而保證主站的動態資源請求和靜態資源的請求不會互相擠佔鏈接。
動靜分離同時還會有一個額外的好處:
對於靜態資源的請求,HTTP請求頭裏的Cookie等信息是沒有用處的, 反而佔用了寶貴的上行網絡資源。用獨立的域名存放靜態資源後, 請求靜態資源域名就不會默認帶上主站域的Cookie,從而解決了這個問題。
以下表:
端口號裏有一個極爲特殊的端口,各類文檔書籍中都鮮有記載,就是0號端口。
在IANA官方的標準裏0號端口是保留端口。
然而,標準歸標準,在UNIX/Linux網絡編程中0號端口被賦予了特殊的涵義:
若是在bind綁定的時候指定端口0,意味着由系統隨機選擇一個可用端口來綁定。
NAT是"Network Address Translation"的縮寫,直譯就是網絡地址轉換。 1990年代中期,爲了應對IPv4地址短缺,NAT技術流行起來。
NAT技術的普遍應用也給不少應用帶來了極大的麻煩: 處於NAT網絡環境內的服務器很難被外部的網絡程序主動鏈接,受這一點傷害最大的莫過於: 點對點視頻、語音、文件傳輸類的程序。
固然咱們聰明的工程師通過長時間的努力,發明了「NAT打洞」技術,必定程度上解決了此類問題。
若是一個端口正在被使用,不管是TIME_WAIT、CLOSE_WAIT、仍是ESTABLISHED狀態。 這個端口都不能被複用,這裏面天然也是包括不能被用來LISTEN(監聽)。
但這件事也不是絕對的,以前跟你們講進程的建立過程提到過一件事: 當進程調用fork(2)系統調用的時候,會發生一系列資源的複製,其中就包括句柄。 因此,在調用fork(2)以前,打開任何文件,監聽端口產生的句柄也將會被複制。
經過這種方式,咱們就能夠達成"多進程端口監聽"。
有一個問題就是
爲何有時候重啓Apache會失敗,報「Address already in use」?
當時答得不太好,不太明白這個問題的關鍵點在哪裏,後來逐漸明白了。
TCP的原理會致使這樣的一個結果:
主動close socket的一方會進入TIME_WAIT,這個情況持續的時間取決於三件事:
總之默認狀況下,處於TIME_WAIT狀態的端口是不能用來LISTEN的。 這就致使,Apache重啓時產生80端口TIME_WAIT,進而致使Apache再次嘗試LISTEN失敗。
在不少開源代碼裏咱們會看到以下代碼:
int reuseaddr = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int));
有了上面這段神奇的代碼,就不會出現上面的慘劇。但SO_REUSEADDR的做用不只限於上述。
Linux 的 SO_REUSEADDR 設置爲 1 有四種效果:
當端口處在TIME_WAIT時候,能夠複用監聽。
能夠容許多個進程監聽同一端口,可是必須不一樣IP。
這裏說的比較隱晦,若是進程A監聽0.0.0.0:80,B進程能夠成功監聽127.0.0.1:80, 順序反過來也是能夠的。
容許單個進程綁定相同的端口到多個socket上,但每一個socket綁定的IP地址不一樣。
使用UDP時候,能夠容許多個實例或者單進程同時監聽同個端口同個IP。