nginx 功能介紹
Nginx因為它的穩定性、豐富的模塊庫、靈活的配置和低系統資源的消耗而聞名.業界一致認為它是Apache2.2+mod_proxy_balancer的輕量級代替者,不僅是因為響應靜態頁面的速度非常快,而且它的模塊數量達到Apache的近2/3。對proxy和rewrite模塊的支持很徹底,還支持mod_fcgi、ssl、vhosts ,適合用來做mongrel clusters的前端HTTP響應。
nginx和Apache一樣使用模塊化設計,nginx模塊包括內置模塊和第三方模塊,其中內置模塊中包含主模塊和事件模塊。
nginx處理請求邏輯圖
nginx可以提供的服務
- web 服務.
- 負載均衡 (反向代理)
- web cache(web 緩存)
nginx 的優點
- 高並發。靜態小文件
- 占用資源少。2萬並發、10個線程,內存消耗幾百M。
- 功能種類比較多。web,cache,proxy。每一個功能都不是特別強。
- 支持epoll模型,使得nginx可以支持高並發。
- nginx 配合動態服務和Apache有區別。(FASTCGI 接口)
- 利用nginx可以對IP限速,可以限制連接數。
- 配置簡單,更靈活。
nginx應用場合
- 靜態服務器。(圖片,視頻服務)另一個lighttpd。並發幾萬,html,js,css,flv,jpg,gif等。
- 動態服務,nginx——fastcgi 的方式運行PHP,jsp。(PHP並發在500-1500,MySQL 並發在300-1500)。
- 反向代理,負載均衡。日pv2000W以下,都可以直接用nginx做代理。
- 緩存服務。類似 SQUID,VARNISH。
主流web服務產品對比說明
Apache-特性
- 2.2版本本身穩定強大,據官方說:其2.4版本性能更強。
- prefork模式取消了進程創建開銷,性能很高。
- 處理動態業務數據時,因關聯到后端的引擎和數據庫,瓶頸不在與Apache本身。
- 高並發時消耗系統資源相對多一些。
- 基於傳統的select模型。
- 擴展庫,DSO方法。
nginx-特性
- 基於異步IO模型,(epoll,kqueue),性能強,能夠支持上萬並發。
- 對小文件支持很好,性能很高(限靜態小文件1M)。
- 代碼優美,擴展庫必須編譯進主程序。
- 消耗代碼資源比較低。
- lighttpd(百度貼吧,豆瓣)
- 基於異步IO模式,性能和nginx相近。
- 擴展庫是SO模式,比nginx要靈活。
8.通過差距(mod_secdownload)可實現文件URL地址加密。
web服務產品性能對比測試
靜態數據性能對比
- 處理靜態文件Apache性能比nginx和lighttpd要差。
- nginx在處理小文件優勢明顯。
- 處理靜態小文件(小於1M),nginx和lighttpd比Apache更有優勢,lighttpd最強。
動態數據性能對比
- 處理動態內容三者相差不大,主要取決於PHP和數據庫的壓力。
- 當處理動態數據時,三者差距不大,從測試結果看,Apache更有優勢一點。這是因為處理動態數據能力取決於PHP和后端數據的提供服務能力。也就是說瓶頸不在web服務器上。
- 一般PHP引擎支持的並發參考值300-1000,JAVA引擎並發300-1000,數據庫的並發300-1000.
為什么nginx的總體性能比Apache高。
- nginx使用最新的epoll和kqueue網絡IO模型,而Apache使用床頭的select模式。
- 目前Linux下能夠承受高並發訪問的squid、Memcached 都采用的是epoll網絡IO模型。
如何選擇WEB服務器:
靜態業務:高並發、采用nginx,lighttpd,根據自己的掌握程度或公司的要求。
動態業務:采用nginx和Apache均可。
既有靜態業務又有動態業務:nginx或Apache,不要多選要單選。
動態業務可以由前端代理(haproxy),根據頁面元素的類型,向后轉發相應的服務器進行處理。
思想:我們工作都不要追求一步到位,滿足需求的前提下,先用,然后逐步完善。
提示:nginx做web(Apache,lighttpd)、反向代理(haproxy,lvs,nat)及緩存服務器(squid)也是不錯的。
最終建議:對外的業務nginx,對內的業務Apache(yum httpd mysql-server php)。
nginx實戰過程
安裝依賴包
- nginx安裝依賴GCC、openssl-devel、pcre-devel和zlib-devel軟件庫。
- Pcre全稱(Perl Compatible Regular Expressions),中文perl兼容正則表達式,pcre官方站點。
yum install pcre pcre-devel -y
yum install openssl openssl-devel -y
開始編譯
使用./configure --help
查看各個模塊的使用情況,使用--without-http_ssi_module
的方式關閉不需要的模塊。可以使用--with-http_perl_modules
方式安裝需要的模塊。
編譯命令
tar -zxf nginx-1.10.1.tar.gz
cd nginx-1.10.1/
./configure --prefix=/data/app/nginx-1.10.1 --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module
useradd nginx -M -s /sbin/nologin
make && make install
ln -s /data/app/nginx-1.10.1 /data/app/nginx
測試nginx配置文件是否正常
/data/app/nginx/sbin/nginx -t
nginx: the configuration file /data/app/nginx-1.10.1/conf/nginx.conf syntax is ok
nginx: configuration file /data/app/nginx-1.10.1/conf/nginx.conf test is successful
啟動nginx服務器
/data/app/nginx/sbin/nginx -t ##檢查配置文件
/data/app/nginx/sbin/nginx ##確定nginx服務
netstat -lntup |grep nginx ## 檢查進程是否正常
curl 192.168.56.12 ## 確認結果
nginx其他命令
nginx -s signal
signal:
stop — fast shutdown
quit — graceful shutdown
reload — reloading the configuration file
reopen — reopening the log files
用來打開日志文件,這樣nginx會把新日志信息寫入這個新的文件中
/data/app/nginx/sbin/nginx -V
查看已經編譯的參數。
使用kill命令操作nginx。格式:kill -信號 PID
信號名稱
- TERM,INT 快速關閉
- QUIT 優雅的關閉,保持吸納有的客戶端連接
- HUP 重啟應用新的配置文件
- USR1 重新打開日志文件
- USR2 升級程序
- WINCH 優雅的關閉工作進程
例子
kill -QUIT `cat /data/app/nginx/nginx.pid`
kill -HUP `cat /data/app/nginx/nginx.pid`
nginx配置文件
配置基礎配置文件
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
### 測試配置文件是否正常
shell> /data/app/nginx/sbin/nginx -t
nginx: the configuration file /data/app/nginx-1.10.3/conf/nginx.conf syntax is ok
nginx: configuration file /data/app/nginx-1.10.3/conf/nginx.conf test is successful
shell> curl -I http://192.168.56.12
HTTP/1.1 200 OK
nginx監控
開啟nginx的監控服務
開啟狀態頁
#設定查看Nginx狀態的地址
location /NginxStatus {
stub_status on;
access_log off;
# auth_basic "NginxStatus";
# auth_basic_user_file conf/htpasswd;
}
- stub_status on; 表示開啟stubStatus的工作狀態統計功能。
- access_log off; 關閉access_log 日志記錄功能。
- auth_basic "NginxStatus"; auth_basic 是nginx的一種認證機制。
- auth_basic_user_file conf/htpasswd; 用來指定密碼文件的位置。
配置登錄密碼
yum install -y httpd-tools
/usr/local/apache/bin/htpasswd -c /data/app/nginx/conf/htpasswd biglittleant
New password:
完成后會在/data/app/nginx/conf/
目錄下生成htpasswd
文件。
訪問URL
# curl http://127.0.0.1/NginxStatus
Active connections: 11921
server accepts handled requests
11989 11989 11991
Reading: 0 Writing: 7 Waiting: 42
- active connections – 活躍的連接數量
- server accepts handled requests — 總共處理了11989個連接 , 成功創建11989次握手, 總共處理了11991個請求
- Reading — 讀取客戶端的連接數.
- Writing — 響應數據到客戶端的數量
- Waiting — 開啟 keep-alive 的情況下,這個值等於 active – (reading+writing), 意思就是 Nginx 已經處理完正在等候下一次請求指令的駐留連接.
編寫zabbix監控腳本
nginx_status_fun(){
NGINX_PORT=$1
NGINX_COMMAND=$2
nginx_active(){
/usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| grep 'Active' | awk '{print $NF}'
}
nginx_reading(){
/usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| grep 'Reading' | awk '{print $2}'
}
nginx_writing(){
/usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| grep 'Writing' | awk '{print $4}'
}
nginx_waiting(){
/usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| grep 'Waiting' | awk '{print $6}'
}
nginx_accepts(){
/usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| awk NR==3 | awk '{print $1}'
}
nginx_handled(){
/usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| awk NR==3 | awk '{print $2}'
}
nginx_requests(){
/usr/bin/curl "http://127.0.0.1:"$NGINX_PORT"/NginxStatus/" 2>/dev/null| awk NR==3 | awk '{print $3}'
}
case $NGINX_COMMAND in
active)
nginx_active;
;;
reading)
nginx_reading;
;;
writing)
nginx_writing;
;;
waiting)
nginx_waiting;
;;
accepts)
nginx_accepts;
;;
handled)
nginx_handled;
;;
requests)
nginx_requests;
esac
}
nginx優化
nginx內核優化
net.ipv4.tcp_fin_timeout = 2
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_keepalive_time = 600
net.ipv4.ip_local_port_range = 4000 65000
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_max_tw_buckets = 36000
net.ipv4.route.gc_timeout = 100
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_synack_retries = 1
net.core.somaxconn = 16384
net.core.netdev_max_backlog = 16384
net.ipv4.tcp_max_orphans = 16384
#以下參數是對iptables防火牆的優化,防火牆不開會提示,可以忽略不理。
net.ipv4.ip_conntrack_max = 25000000
net.ipv4.netfilter.ip_conntrack_max=25000000
net.ipv4.netfilter.ip_conntrack_tcp_timeout_established=180
net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait=120
net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait=60
net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait=120
報錯匯總
問題1:編譯報錯
./configure: error: the HTTP rewrite module requires the PCRE library.
You can either disable the module by using —without-http_rewrite_module(偽靜態)
option, or install the PCRE library into the system, or build the PCRE library
statically from the source with nginx by using --with-pcre=<path> option
yum install pcre pcre-devel -y
問題2: 提示找不到libpcre.so.1
解決:
- find / -name libpcre.so*
- 將找到的路徑 追加到 /etc/ld.so.conf
- ldconfig 生效。
- 或
ln -s /ser/local/lib/libpcre.so.l /lib64
。 - 或編譯時指定源碼的安裝路徑:
--with-pcre=/data/tools/pcre-8.33
。 - 最終解決方案
yum install pcre-devel -y
不會出現上述報錯。
問題3:啟動nginx報錯
[root@centos6 tools]# /application/nginx/sbin/nginx
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()
解決辦法:(因為開啟了Apache服務)
[root@nfs-client application]# lsof -i :80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
httpd 35833 www 4u IPv6 129886 0t0 TCP *:http (LISTEN)
httpd 35834 www 4u IPv6 129886 0t0 TCP *:http (LISTEN)
httpd 98511 root 4u IPv6 129886 0t0 TCP *:http (LISTEN)
[root@nfs-client application]# /application/apache/bin/apachectl stop
[root@nfs-client application]# /application/nginx/sbin/nginx ##重新啟動nginx。
擴展閱讀:
nginx全局變量
$args
:這個變量等於請求行中的參數,同$query_string。$is_args
: 如果已經設置$args
,則該變量的值為"?",否則為""。$content_length
: 請求頭中的Content-length字段。$content_type
: 請求頭中的Content-Type字段。$document_uri
: 與$uri相同。$document_root
: 當前請求在root指令中指定的值。$host
: 請求主機頭字段,否則為服務器名稱。$http_user_agent
: 客戶端agent信息。$http_cookie
: 客戶端cookie信息。$limit_rate
: 這個變量可以限制連接速率。$request_method
: 客戶端請求的動作,通常為GET或POST。$remote_addr
: 客戶端的IP地址。$remote_port
: 客戶端的端口。$remote_user
: 已經經過Auth Basic Module驗證的用戶名。- $request_body_file`: 客戶端請求主體的臨時文件名。
$request_uri
: 請求的URI,帶參數$request_filename
: 當前請求的文件路徑,由root或alias指令與URI請求生成。$scheme
: 所用的協議,比如http或者是https,比如rewrite ^(.+)$ $scheme://example.com$1 redirect;
。$server_protocol
: 請求使用的協議,通常是HTTP/1.0或HTTP/1.1。$server_addr
: 服務器地址,在完成一次系統調用后可以確定這個值。$server_name
: 服務器名稱。$server_port
: 請求到達服務器的端口號。$request_uri
: 包含請求參數的原始URI,不包含主機名,如:/foo/bar.php?arg=baz
。$uri
: 不帶請求參數的當前URI,$uri不包含主機名,如/foo/bar.html
可能和最初的值有不同,比如經過重定向之類的。
例子:
訪問鏈接是:http://localhost:88/test1/test2/test.php
網站路徑是:/var/www/html
$host:localhost
$server_port:88
$request_uri:http://localhost:88/test1/test2/test.php
$document_uri:/test1/test2/test.php
$document_root:/var/www/html
$request_filename:/var/www/html/test1/test2/test.php
nginx plus -- ngx_http_status_module
商業版的 nginx plus 通過他的 ngx_http_status_module 提供了比 nginx 更多的監控指標,可以參看 http://demo.nginx.com/status.html
nginx access log 分析
nginx 的 access log 中可以記錄很多有價值的信息,通過分析 access log,可以收集到很多指標。
python 編寫的 linux 工具 ngxtop 就實現了對 access log 的分析功能。
web服務器事件處理模型
select
select最早於1983年出現在4.2BSD中,它通過一個select()系統調用來監視多個文件描述符的數組,當select()返回后,該數組中就緒的文件描述符便會被內核修改標志位,使得進程可以獲得這些文件描述符從而進行后續的讀寫操作。
select目前幾乎在所有的平台上支持,其良好跨平台支持也是它的一個優點,事實上從現在看來,這也是它所剩不多的優點之一。
select的一個缺點在於單個進程能夠監視的文件描述符的數量存在最大限制,在Linux上一般為1024,不過可以通過修改宏定義甚至重新編譯內核的方式提升這一限制。
另外,select()所維護的存儲大量文件描述符的數據結構,隨着文件描述符數量的增大,其復制的開銷也線性增長。同時,由於網絡響應時間的延遲使得大量TCP連接處於非活躍狀態,但調用select()會對所有socket進行一次線性掃描,所以這也浪費了一定的開銷。
poll
poll在1986年誕生於System V Release 3,它和select在本質上沒有多大差別,但是poll沒有最大文件描述符數量的限制。
poll和select同樣存在一個缺點就是,包含大量文件描述符的數組被整體復制於用戶態和內核的地址空間之間,而不論這些文件描述符是否就緒,它的開銷隨着文件描述符數量的增加而線性增大。
另外,select()和poll()將就緒的文件描述符告訴進程后,如果進程沒有對其進行IO操作,那么下次調用select()和poll()的時候將再次報告這些文件描述符,所以它們一般不會丟失就緒的消息,這種方式稱為水平觸發(Level Triggered)。
epoll
直到Linux2.6才出現了由內核直接支持的實現方法,那就是epoll,它幾乎具備了之前所說的一切優點,被公認為Linux2.6下性能最好的多路I/O就緒通知方法。
epoll可以同時支持水平觸發和邊緣觸發(Edge Triggered,只告訴進程哪些文件描述符剛剛變為就緒狀態,它只說一遍,如果我們沒有采取行動,那么它將不會再次告知,這種方式稱為邊緣觸發),理論上邊緣觸發的性能要更高一些,但是代碼實現相當復雜。
epoll同樣只告知那些就緒的文件描述符,而且當我們調用epoll_wait()獲得就緒文件描述符時,返回的不是實際的描述符,而是一個代表就緒描述符數量的值,你只需要去epoll指定的一個數組中依次取得相應數量的文件描述符即可,這里也使用了內存映射(mmap)技術,這樣便徹底省掉了這些文件描述符在系統調用時復制的開銷。
另一個本質的改進在於epoll采用基於事件的就緒通知方式。在select/poll中,進程只有在調用一定的方法后,內核才對所有監視的文件描述符進行掃描,而epoll事先通過epoll_ctl()來注冊一個文件描述符,一旦基於某個文件描述符就緒時,內核會采用類似callback的回調機制,迅速激活這個文件描述符,當進程調用epoll_wait()時便得到通知。
nginx -s reload 過程
nginx主進程讀取配置文件,如果發現配置文件變更,會創建一個新的主進程,然后同時舊的進程,及舊的子進程關閉,舊進程會拒絕新的連接,服務到自己的連接結束,然后關閉。
Apache select模型和 nginx epoll 模型對比講解
Nginx的高並發得益於其采用了epoll模型,與傳統的服務器程序架構不同,epoll是linux內核2.6以后才出現的。下面通過比較Apache和Nginx工作原理來比較。
傳統Apache都是多進程或者多線程來工作,假設是多進程工作(prefork),apache會先生成幾個進程,類似進程池的工作原理,只不過這里的進程池會隨着請求數目的增加而增加。對於每一個連接,apache都是在一個進程內處理完畢。具體是 recv(),以及根據 URI 去進行磁盤I/O來尋找文件,還有 send()都是阻塞的。其實說白了都是 apche 對於套接字的I/O,讀或者寫,但是讀或者寫都是阻塞的,阻塞意味着進程就得掛起進入sleep狀態,那么一旦連接數很多,Apache必然要生成更多的進程來響應請求,一旦進程多了,CPU對於進程的切換就頻繁了,很耗資源和時間,所以就導致apache性能下降了,說白了就是處理不過來這么多進程了。其實仔細想想,如果對於進程每個請求都沒有阻塞,那么效率肯定會提高很多。
Nginx采用epoll模型,異步非阻塞。對於Nginx來說,把一個完整的連接請求處理都划分成了事件,一個一個的事件。比如accept(), recv(),磁盤I/O,send()等,每部分都有相應的模塊去處理,一個完整的請求可能是由幾百個模塊去處理。真正核心的就是事件收集和分發模塊,這就是管理所有模塊的核心。只有核心模塊的調度才能讓對應的模塊占用CPU資源,從而處理請求。拿一個HTTP請求來說,首先在事件收集分發模塊注冊感興趣的監聽事件,注冊好之后不阻塞直接返回,接下來就不需要再管了,等待有連接來了內核會通知你(epoll的輪詢會告訴進程),cpu就可以處理其他事情去了。一旦有請求來,那么對整個請求分配相應的上下文(其實已經預先分配好),這時候再注冊新的感興趣的事件(read函數),同樣客戶端數據來了內核會自動通知進程可以去讀數據了,讀了數據之后就是解析,解析完后去磁盤找資源(I/O),一旦I/O完成會通知進程,進程開始給客戶端發回數據send(),這時候也不是阻塞的,調用后就等內核發回通知發送的結果就行。整個下來把一個請求分成了很多個階段,每個階段都到很多模塊去注冊,然后處理,都是異步非阻塞。異步這里指的就是做一個事情,不需要等返回結果,做好了會自動通知你。
select/epoll的特點
select的特點:select 選擇句柄的時候,是遍歷所有句柄,也就是說句柄有事件響應時,select需要遍歷所有句柄才能獲取到哪些句柄有事件通知,因此效率是非常低。但是如果連接很少的情況下, select和epoll的LT觸發模式相比, 性能上差別不大。
這里要多說一句,select支持的句柄數是有限制的, 同時只支持1024個,這個是句柄集合限制的,如果超過這個限制,很可能導致溢出,而且非常不容易發現問題, 當然可以通過修改linux的socket內核調整這個參數。
epoll的特點:epoll對於句柄事件的選擇不是遍歷的,是事件響應的,就是句柄上事件來就馬上選擇出來,不需要遍歷整個句柄鏈表,因此效率非常高,內核將句柄用紅黑樹保存的。
對於epoll而言還有ET和LT的區別,LT表示水平觸發,ET表示邊緣觸發,兩者在性能以及代碼實現上差別也是非常大的。
可以舉一個簡單的例子來說明Apache的工作流程,我們平時去餐廳吃飯。餐廳的工作模式是一個服務員全程服務客戶,流程是這樣,服務員在門口等候客人(listen),客人到了就接待安排的餐桌上(accept),等着客戶點菜(request uri),去廚房叫師傅下單做菜(磁盤I/O),等待廚房做好(read),然后給客人上菜(send),整個下來服務員(進程)很多地方是阻塞的。這樣客人一多(HTTP請求一多),餐廳只能通過叫更多的服務員來服務(fork進程),但是由於餐廳資源是有限的(CPU),一旦服務員太多管理成本很高(CPU上下文切換),這樣就進入一個瓶頸。
再來看看Nginx得怎么處理?餐廳門口掛個門鈴(注冊epoll模型的listen),一旦有客人(HTTP請求)到達,派一個服務員去接待(accept),之后服務員就去忙其他事情了(比如再去接待客人),等這位客人點好餐就叫服務員(數據到了read()),服務員過來拿走菜單到廚房(磁盤I/O),服務員又做其他事情去了,等廚房做好了菜也喊服務員(磁盤I/O結束),服務員再給客人上菜(send()),廚房做好一個菜就給客人上一個,中間服務員可以去干其他事情。整個過程被切分成很多個階段,每個階段都有相應的服務模塊。我們想想,這樣一旦客人多了,餐廳也能招待更多的人。
不管是Nginx還是Squid這種反向代理,其網絡模式都是事件驅動。事件驅動其實是很老的技術,早期的select、poll都是如此。后來基於內核通知的更高級事件機制出現,如libevent里的epoll,使事件驅動性能得以提高。事件驅動的本質還是IO事件,應用程序在多個IO句柄間快速切換,實現所謂的異步IO。事件驅動服務器,最適合做的就是這種IO密集型工作,如反向代理,它在客戶端與WEB服務器之間起一個數據中轉作用,純粹是IO操作,自身並不涉及到復雜計算。反向代理用事件驅動來做,顯然更好,一個工作進程就可以run了,沒有進程、線程管理的開銷,CPU、內存消耗都小。
所以Nginx、Squid都是這樣做的。當然,Nginx也可以是多進程 + 事件驅動的模式,幾個進程跑libevent,不需要Apache那樣動輒數百的進程數。Nginx處理靜態文件效果也很好,那是因為靜態文件本身也是磁盤IO操作,處理過程一樣。至於說多少萬的並發連接,這個毫無意義。隨手寫個網絡程序都能處理幾萬的並發,但如果大部分客戶端阻塞在那里,就沒什么價值。
再看看Apache或者Resin這類應用服務器,之所以稱他們為應用服務器,是因為他們真的要跑具體的業務應用,如科學計算、圖形圖像、數據庫讀寫等。它們很可能是CPU密集型的服務,事件驅動並不合適。例如一個計算耗時2秒,那么這2秒就是完全阻塞的,什么event都沒用。想想MySQL如果改成事件驅動會怎么樣,一個大型的join或sort就會阻塞住所有客戶端。這個時候多進程或線程就體現出優勢,每個進程各干各的事,互不阻塞和干擾。當然,現代CPU越來越快,單個計算阻塞的時間可能很小,但只要有阻塞,事件編程就毫無優勢。所以進程、線程這類技術,並不會消失,而是與事件機制相輔相成,長期存在。
總言之,事件驅動適合於IO密集型服務,多進程或線程適合於CPU密集型服務,它們各有各的優勢,並不存在誰取代誰的傾向。