1. 部署Nginx OpenResty
安裝部署Nginx
OpenResty將許多配置編譯成了一個(gè)二進(jìn)制包,內(nèi)部集成了許多nginx特性;
1、安裝openresty
1)通過在CentOS 系統(tǒng)中添加 openresty 倉(cāng)庫(kù),便于未來安裝或更新我們的軟件包(通過 yum update 命令)
sudo yum install yum-utils
sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
2)安裝openresty
sudo yum install openresty
3)安裝命令行工具 resty
sudo yum install openresty-resty
命令行工具 opm 在 openresty-opm 包里,而 restydoc 工具在 openresty-doc 包里頭。
4)查看openresty 倉(cāng)庫(kù)里頭的軟件包
sudo yum --disablerepo="*" --enablerepo="openresty" list available
在Nginx web服務(wù)器中:
location節(jié)點(diǎn)path:指定url映射key;
location節(jié)點(diǎn)內(nèi)容:root指定location path后對(duì)應(yīng)的根路徑,index指定默認(rèn)的訪問頁(yè);
sbin/nginx -c conf/nginx.conf啟動(dòng);
修改配置后直接sbin/nginx -s reload無縫重啟;
2.3 前端資源部署
打包上傳前端資源文件;
配置前端資源路由;
2.4 配置nginx反向代理
nginx動(dòng)靜分離服務(wù)器
location節(jié)點(diǎn)path特定resources:靜態(tài)資源路徑;
location節(jié)點(diǎn)其它路徑:動(dòng)態(tài)資源用;
如何使用動(dòng)態(tài)資源?
nginx做反向代理服務(wù)器
設(shè)置upstream server
設(shè)置動(dòng)態(tài)請(qǐng)求location為proxy pass路徑;
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
upstream backend_server{
server 192.168.239.102 weight=1;
server 192.168.239.103 weight=1;
keepalive 30;
}
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location /resources/ {
alias /usr/local/openresty/nginx/html/resources/;
index index.html index.htm;
}
location / {
proxy_pass http://backend_server;
proxy_set_header Host $http_host:$proxy_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
}
$remote_addr
獲取到上一級(jí)代理的IP
如果想要通過X-Forwarded-For獲得用戶ip,就必須先使用proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 這樣就可以獲得用戶真實(shí)ip。
開啟tomcat access log(訪問日志)驗(yàn)證
application.properties
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.directory=/usr/local/www/miaosha/tomcat
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D

netstat -an | grep 192.168.239.1 | wc -l
netstat -an | grep 192.168.239.1 | grep ESTABLISHED
2.Nginx高性能原因
Nginx高性能主要分為以下三點(diǎn):
epoll多路復(fù)用完成非阻塞式的IO操作;
master worker進(jìn)程模型,允許其進(jìn)行平滑重啟和配置,不會(huì)斷開和客戶端連接;
協(xié)程機(jī)制,完成單進(jìn)程單線程模型,并支持并發(fā)編程調(diào)用接口;
2.1 epoll多路復(fù)用(解決IO阻塞回調(diào)通知問題)
I/O多路復(fù)用就是通過一種機(jī)制,可以監(jiān)視多個(gè)描述符,一旦某個(gè)IO能夠讀寫,通知程序進(jìn)行相應(yīng)的讀寫操作。
I/O多路復(fù)用的場(chǎng)合
當(dāng)客戶處理多個(gè)描述字時(shí)(通常是交互式輸入和網(wǎng)絡(luò)套接字),必須使用I/O復(fù)用
如果一個(gè)TCP服務(wù)器既要處理監(jiān)聽套接字,又要處理已連接套接字,一般也要用到I/O復(fù)用
如果一個(gè)服務(wù)器即要處理TCP,又要處理UDP,一般要使用I/O復(fù)用
Java BIO模型
client和server之間通過tpc/ip建立聯(lián)系,javaclient只有等到所有字節(jié)流socket.write到字節(jié)流的緩沖之后,對(duì)應(yīng)的java client才會(huì)返回;若網(wǎng)絡(luò)很慢,緩沖區(qū)填滿之后,client就必須等待信息傳輸過去之后,使得緩沖區(qū)可以給上游去寫時(shí),才可達(dá)到直接返回的效果;
Linux select模型
Linux select模型,變更觸發(fā)輪訓(xùn)查詢,文件描述符有1024數(shù)量上限;一旦java server被喚醒,并且對(duì)應(yīng)的socket連接打上有變化的標(biāo)識(shí)之后,就代表已經(jīng)有數(shù)據(jù)可以讓你讀寫;
弊端:
輪詢效率低,有1024數(shù)量限制;
epoll模型
epoll是為了解決select和poll的輪詢方式效率低問題;
假設(shè)一個(gè)場(chǎng)景:
有100萬個(gè)客戶端同時(shí)與一個(gè)服務(wù)器進(jìn)程保持著TCP連接。而每一時(shí)刻,通常只有幾百上千個(gè)TCP連接是活躍的(事實(shí)上大部分場(chǎng)景都是這種情況)。如何實(shí)現(xiàn)這樣的高并發(fā)?
在select/poll時(shí)代,服務(wù)器進(jìn)程每次都把這100萬個(gè)連接告訴操作系統(tǒng)(從用戶態(tài)復(fù)制句柄數(shù)據(jù)結(jié)構(gòu)到內(nèi)核態(tài)),讓操作系統(tǒng)內(nèi)核去查詢這些套接字上是否有事件發(fā)生,輪詢完后,再將句柄數(shù)據(jù)復(fù)制到用戶態(tài),服務(wù)器應(yīng)用程序輪詢處理已發(fā)生的網(wǎng)絡(luò)事件,這一過程資源消耗較大。因此,select/poll一般只能處理幾千的并發(fā)連接。
epoll的設(shè)計(jì)和實(shí)現(xiàn)與select完全不同:
epoll通過在Linux內(nèi)核中申請(qǐng)一個(gè)簡(jiǎn)單的文件系統(tǒng)(文件系統(tǒng)一般由B+樹實(shí)現(xiàn))
把原先的select/poll調(diào)用分成了3個(gè)部分:
- 調(diào)用epoll_create()建立一個(gè)epoll對(duì)象(在epoll文件系統(tǒng)中為這個(gè)句柄對(duì)象分配資源);
- 調(diào)用epoll_ctl向epoll對(duì)象中添加這100萬個(gè)連接的套接字;
- 調(diào)用epoll_wait收集發(fā)生的事件的連接;
實(shí)現(xiàn)上面說是的場(chǎng)景,只需要在進(jìn)程啟動(dòng)時(shí)建立一個(gè)epoll對(duì)象,然后在需要的時(shí)候向這個(gè)epoll對(duì)象中添加或者刪除連接。同時(shí),epoll_wait的效率也非常高,因?yàn)檎{(diào)用epoll_wait時(shí),并沒有一股腦的向操作系統(tǒng)復(fù)制這100萬個(gè)連接的句柄數(shù)據(jù),內(nèi)核也不需要去遍歷全部的連接。
2.2 master-worker進(jìn)程模型
2.2.1進(jìn)程模型
Nginx之所以為廣大碼農(nóng)喜愛,除了其高性能外,還有其優(yōu)雅的系統(tǒng)架構(gòu)。與經(jīng)典多線程模型相比,Nginx是經(jīng)典的多進(jìn)程模型。Nginx啟動(dòng)后以daemon的方式在后臺(tái)運(yùn)行,后臺(tái)進(jìn)程包含一個(gè)master進(jìn)程和多個(gè)worker進(jìn)程
Nginx多進(jìn)程模型如下所示:

master進(jìn)程主要用來管理worker進(jìn)程,具體包括如下4個(gè)主要功能:
(1)接收來自外界的信號(hào)。
(2)向各worker進(jìn)程發(fā)送信號(hào)。
(3)監(jiān)控woker進(jìn)程的運(yùn)行狀態(tài)。
(4)當(dāng)woker進(jìn)程退出后(異常情況下),會(huì)自動(dòng)重新啟動(dòng)新的woker進(jìn)程。
woker進(jìn)程主要用來處理網(wǎng)絡(luò)事件,各個(gè)woker進(jìn)程之間是對(duì)等且相互獨(dú)立的,它們同等競(jìng)爭(zhēng)來自客戶端的請(qǐng)求,一個(gè)請(qǐng)求只可能在一個(gè)woker進(jìn)程中處理,woker進(jìn)程個(gè)數(shù)一般設(shè)置為機(jī)器CPU核數(shù)。
2.2.2進(jìn)程控制
對(duì)Nginx進(jìn)程的控制主要是通過master進(jìn)程來做到的,主要有兩種方式:
(1)手動(dòng)發(fā)送信號(hào)
從圖1可以看出,master接收信號(hào)以管理眾woker進(jìn)程,那么,可以通過kill向master進(jìn)程發(fā)送信號(hào),比如kill -HUP pid用以通知Nginx從容重啟。所謂從容重啟就是不中斷服務(wù):master進(jìn)程在接收到信號(hào)后,會(huì)先重新加載配置,然后再啟動(dòng)新進(jìn)程開始接收新請(qǐng)求,并向所有老進(jìn)程發(fā)送信號(hào)告知不再接收新請(qǐng)求并在處理完所有未處理完的請(qǐng)求后自動(dòng)退出。
(2)自動(dòng)發(fā)送信號(hào)
可以通過帶命令行參數(shù)啟動(dòng)新進(jìn)程來發(fā)送信號(hào)給master進(jìn)程,比如./nginx -s reload用以啟動(dòng)一個(gè)新的Nginx進(jìn)程,而新進(jìn)程在解析到reload參數(shù)后會(huì)向master進(jìn)程發(fā)送信號(hào)(新進(jìn)程會(huì)幫我們把手動(dòng)發(fā)送信號(hào)中的動(dòng)作自動(dòng)完成)。當(dāng)然也可以這樣./nginx -s stop來停止Nginx。
nginx reload master進(jìn)程不會(huì)重啟,而是讓worker進(jìn)程重啟

2.2.3網(wǎng)絡(luò)事件
Nginx采用異步非阻塞的方式來處理網(wǎng)絡(luò)事件,類似于[Libevent](http://blog.chinaunix.net/uid-22312037-id-3546904.html),具體過程如下圖:

master進(jìn)程先建好需要listen的socket后,然后再fork出多個(gè)woker進(jìn)程,這樣每個(gè)work進(jìn)程都可以去accept這個(gè)socket。當(dāng)一個(gè)client連接到來時(shí),所有accept的work進(jìn)程都會(huì)受到通知,但只有一個(gè)進(jìn)程可以accept成功,其它的則會(huì)accept失敗。Nginx提供了一把共享鎖accept_mutex來保證同一時(shí)刻只有一個(gè)work進(jìn)程在accept連接,從而解決驚群?jiǎn)栴}。當(dāng)一個(gè)worker進(jìn)程accept這個(gè)連接后,就開始讀取請(qǐng)求,解析請(qǐng)求,處理請(qǐng)求,產(chǎn)生數(shù)據(jù)后,再返回給客戶端,最后才斷開連接,這樣一個(gè)完成的請(qǐng)求就結(jié)束了。
2.3協(xié)程機(jī)制
一個(gè)線程可以有多個(gè)協(xié)程,協(xié)程是線程的內(nèi)存模型
依附于線程的內(nèi)存模型,切換開銷小;
遇阻塞及歸還執(zhí)行權(quán),代碼同步,調(diào)用新的不阻塞的協(xié)程;
無需加鎖;