Nginx由於它的穩定性、豐富的模塊庫、靈活的配置和低系統資源的消耗而聞名.業界一致認爲它是Apache2.2+mod_proxy_balancer的輕量級代替者,不只是由於響應靜態頁面的速度很是快,並且它的模塊數量達到Apache的近2/3。對proxy和rewrite模塊的支持很完全,還支持mod_fcgi、ssl、vhosts ,適合用來作mongrel clusters的前端HTTP響應。
nginx和Apache同樣使用模塊化設計,nginx模塊包括內置模塊和第三方模塊,其中內置模塊中包含主模塊和事件模塊。php
nginx處理請求邏輯圖
css
處理靜態小文件(小於1M),nginx和lighttpd比Apache更有優點,lighttpd最強。html
通常PHP引擎支持的併發參考值300-1000,JAVA引擎併發300-1000,數據庫的併發300-1000.前端
靜態業務:高併發、採用nginx,lighttpd,根據本身的掌握程度或公司的要求。
動態業務:採用nginx和Apache都可。
既有靜態業務又有動態業務:nginx或Apache,不要多選要單選。
動態業務能夠由前端代理(haproxy),根據頁面元素的類型,向後轉發相應的服務器進行處理。
思想:咱們工做都不要追求一步到位,知足需求的前提下,先用,而後逐步完善。
提示:nginx作web(Apache,lighttpd)、反向代理(haproxy,lvs,nat)及緩存服務器(squid)也是不錯的。
最終建議:對外的業務nginx,對內的業務Apache(yum httpd mysql-server php)。python
yum install pcre pcre-devel -y yum install openssl openssl-devel -y
使用./configure --help
查看各個模塊的使用狀況,使用--without-http_ssi_module
的方式關閉不須要的模塊。可使用--with-http_perl_modules
方式安裝須要的模塊。mysql
編譯命令linux
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配置文件是否正常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服務器web
/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
信號名稱
例子
kill -QUIT `cat /data/app/nginx/nginx.pid` kill -HUP `cat /data/app/nginx/nginx.pid`
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狀態的地址 location /NginxStatus { stub_status on; access_log off; # auth_basic "NginxStatus"; # auth_basic_user_file conf/htpasswd; }
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
文件。
# curl http://127.0.0.1/NginxStatus Active connections: 11921 server accepts handled requests 11989 11989 11991 Reading: 0 Writing: 7 Waiting: 42
Waiting — 開啓 keep-alive 的狀況下,這個值等於 active – (reading+writing), 意思就是 Nginx 已經處理完正在等候下一次請求指令的駐留鏈接.
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 }
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
./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
libpcre.so.1
解決:
ln -s /ser/local/lib/libpcre.so.l /lib64
。--with-pcre=/data/tools/pcre-8.33
。yum install pcre-devel -y
不會出現上述報錯。[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。
$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_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 更多的監控指標,能夠參看 http://demo.nginx.com/status.html
nginx 的 access log 中能夠記錄不少有價值的信息,經過分析 access log,能夠收集到不少指標。
python 編寫的 linux 工具 ngxtop 就實現了對 access log 的分析功能。
select最先於1983年出如今4.2BSD中,它經過一個select()系統調用來監視多個文件描述符的數組,當select()返回後,該數組中就緒的文件描述符便會被內核修改標誌位,使得進程能夠得到這些文件描述符從而進行後續的讀寫操做。
select目前幾乎在全部的平臺上支持,其良好跨平臺支持也是它的一個優勢,事實上從如今看來,這也是它所剩很少的優勢之一。
select的一個缺點在於單個進程可以監視的文件描述符的數量存在最大限制,在Linux上通常爲1024,不過能夠經過修改宏定義甚至從新編譯內核的方式提高這一限制。
另外,select()所維護的存儲大量文件描述符的數據結構,隨着文件描述符數量的增大,其複製的開銷也線性增加。同時,因爲網絡響應時間的延遲使得大量TCP鏈接處於非活躍狀態,但調用select()會對全部socket進行一次線性掃描,因此這也浪費了必定的開銷。
poll在1986年誕生於System V Release 3,它和select在本質上沒有多大差異,可是poll沒有最大文件描述符數量的限制。
poll和select一樣存在一個缺點就是,包含大量文件描述符的數組被總體複製於用戶態和內核的地址空間之間,而不論這些文件描述符是否就緒,它的開銷隨着文件描述符數量的增長而線性增大。
另外,select()和poll()將就緒的文件描述符告訴進程後,若是進程沒有對其進行IO操做,那麼下次調用select()和poll()的時候將再次報告這些文件描述符,因此它們通常不會丟失就緒的消息,這種方式稱爲水平觸發(Level Triggered)。
直到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主進程讀取配置文件,若是發現配置文件變動,會建立一個新的主進程,而後同時舊的進程,及舊的子進程關閉,舊進程會拒絕新的鏈接,服務到本身的鏈接結束,而後關閉。
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密集型服務,它們各有各的優點,並不存在誰取代誰的傾向。