1、高併發基礎架構php
簡要流程:css
1.客戶端發請求。html
2.又LVS等四層負載均衡系統將請求轉發給不一樣的Nginx服務器。java
3.Nginx與客戶端創建TCP鏈接,拿到請求後分析URI,而後將其轉發給對應的功能模塊服務(Tomcat容器)。linux
4.等待後端功能模塊服務的響應。nginx
5.功能模塊進行計算,並從後端存儲中獲取數據,並返回。web
6.Nginx收到響應後返回給客戶端。正則表達式
2、Nginx和Tengineshell
Nginx(engin x)是一個高性能的HTTP和反向代理服務器,也是一個IMAP/POP3/SMTP代理服務器。數據庫
主要以穩定性、豐富的功能集、低系統資源消耗而聞名。
官方測試nginx可以支撐5萬併發鏈接,而且CPU、內存等資源消耗很是低,運行穩定。
什麼是反向代理(通俗理解)?
正向代理:是代用戶訪問遠程資源。 例如咱們要訪問國外的網站,咱們能夠經過位於香港等地的代理服務器來幫咱們從國外獲取資源,但咱們請求的目的仍是真正的國外服務器地址。國外服務器看到的請求方是代理服務器。
反向代理:就是幫後端資源進行代理,也就是咱們看到的目標服務器就是該反向代理服務器,而看不到真正提供資源的服務器。咱們看到的資源地址是反向代理服務器。
Nginx相對apache的優勢:
1.nginx是輕量級,一樣web服務,比apache佔用更少的內存及資源。
2.抗併發,nginx是異步非阻塞的,而apache是阻塞型的,在高併發下nginx保持低資源消耗,高性能
3.高度模塊化的設計,編寫模塊相對簡單
4.社區活躍,各類高性能模塊出品迅速
5.配置簡潔
apache的優勢:
1.rewrite強大
2.模塊超多
3.bug少
最核心的不一樣:
apache是同步多進程模型(select),一個連接對應一個進程;nginx是異步(epoll),多個連接(萬級別)對應一個進程。
nginx不會浪費時間在進程的切換上,因此效率很高。
3、安裝Nginx(Tengine)
1.安裝依賴
yum install gcc pcre-devel openssl-devel -y
2.下載tengine包,並解壓
cd ~ wget http://tengine.taobao.org/download/tengine-2.3.0.tar.gz tar zxf tengine-2.3.0.tar.gz
3.安裝tengine
cd tengine-2.3.0 ./configure --prefix=/opt/nginx
**企業標準安裝:
./configure --prefix=/usr --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --user=nginx --group=nginx --with-http_ssl_module --with-http_flv_module --with-http_stub_status_module --with-http_gzip_static_module --http-client-body-temp-path=/var/tmp/nginx/client/ --http-proxy-temp-path=/var/tmp/nginx/proxy/ --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi --http-scgi-temp-path=/var/tmp/nginx/scgi --with-pcre
make && make install
4.設置nginx爲系統服務
添加nginx.service文件:
vi /usr/lib/systemd/system/nginx.service [Unit] Description=The nginx HTTP and reverse proxy server After=syslog.target network.target remote-fs.target nss-lookup.target [Service] Type=forking PIDFile=/opt/nginx/logs/nginx.pid ExecStartPre=/opt/nginx/sbin/nginx -t ExecStart=/opt/nginx/sbin/nginx -c /opt/nginx/conf/nginx.conf ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s QUIT $MAINPID PrivateTmp=true [Install] WantedBy=multi-user.target
注意,全部path的部分都要修改成實際安裝nginx的目錄。
使用systemctl啓動nginx:
systemctl start nginx.service
使用瀏覽器訪問(默認監聽80端口,能夠在nginx.conf中修改):
其餘服務操做:
# 設置開機啓動
systemctl enable nginx.service
# 中止服務
systemctl stop nginx.service
# 重啓服務
systemctl restart nginx.service
# 取消開機啓動
systemctl disable nginx.service
# 查看服務運行狀態
systemctl status nginx.service
查看全部已啓動服務:
systemctl list-units --type=service
查看開機啓動服務列表:
[root@real-server-1 conf]# systemctl list-unit-files
UNIT FILE STATE proc-sys-fs-binfmt_misc.automount static dev-hugepages.mount static dev-mqueue.mount static proc-sys-fs-binfmt_misc.mount static sys-fs-fuse-connections.mount static sys-kernel-config.mount static sys-kernel-debug.mount static tmp.mount disabled brandbot.path enabled systemd-ask-password-console.path static systemd-ask-password-plymouth.path static systemd-ask-password-wall.path static session-1.scope static arp-ethers.service disabled auditd.service enabled autovt@.service enabled blk-availability.service disabled brandbot.service static chrony-dnssrv@.service static chrony-wait.service disabled chronyd.service enabled console-getty.service disabled console-shell.service disabled container-getty@.service static cpupower.service disabled crond.service enabled dbus-org.freedesktop.hostname1.service static dbus-org.freedesktop.import1.service static dbus-org.freedesktop.locale1.service static dbus-org.freedesktop.login1.service static dbus-org.freedesktop.machine1.service static dbus-org.freedesktop.timedate1.service static dbus.service static debug-shell.service disabled dm-event.service static dnsmasq.service disabled
4、配置Nginx(Tengine)
1.修改nginx配置文件:
[root@real-server-1 conf]# vi /opt/nginx/conf/nginx.conf 1 # 雖然user nobody是註釋掉的,但仍然在使用,固然也能夠修改成任意用戶。子進程worker是nobody用戶全部。父進程是root用戶的。 2 #user nobody; # worker_processes 是真正作事的進程的數量,通常爲物理核心的1-2倍 3 worker_processes 1; 4 # 配置日誌 5 #error_log logs/error.log; 6 #error_log logs/error.log notice; 7 #error_log logs/error.log info; 8 #error_log "pipe:rollback logs/error_log interval=1d baknum=7 maxsize=2G"; 9 10 #pid logs/nginx.pid; 11 12 # worker_connection很重要,除了修改此處的數字,還須要修改操做系統內核容許進程所能操做文件描述符的數量。 # 使用ulimit -a能夠查看當前內核容許的進程操做文件描述符的數量(open files)。 # 操做系統所能操做文件描述符的總數通常和內存成正比(例如1GB對應10W個文件描述符)。 # 咱們在考慮worker_connnection時,除了考慮客戶端鏈接數,還要考慮nginx從後端請求數據時的socket,因此須要配置得更大一些。 # 使用ulimit -SHn 65535能夠修改內核限制,而且這裏也修改成對應的數量。 13 events { 14 #worker_connections 1024; worker_connections 65535; 15 } 16 17 # load modules compiled as Dynamic Shared Object (DSO) 18 # 19 #dso { 20 # load ngx_http_fastcgi_module.so; 21 # load ngx_http_rewrite_module.so; 22 #} 23 24 http { 25 include mime.types; 26 default_type application/octet-stream; 27 # 日誌記錄的格式化,控制日誌怎麼記錄,作日誌分析的時候能夠參照這裏的規範(重要) # 有些公司使用nginx作小數據的採集,就能夠直接使用日誌來記錄,例如傳感器數據,直接訪問nginx,將小數據傳遞過來,寫進日誌。 28 #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 29 # '$status $body_bytes_sent "$http_referer" ' 30 # '"$http_user_agent" "$http_x_forwarded_for"'; 31 32 #access_log logs/access.log main; 33 #access_log "pipe:rollback logs/access_log interval=1d baknum=7 maxsize=2G" main; 34 # (重要)零拷貝,例如nginx讀取文件內容,而後返回給客戶端的過程。首先調內核,讓內核讀取文件,讀到的內容放在內核的緩衝區。 # 而後將內核緩衝區的內容拷貝到用戶態buffer中。當要從socket發送給客戶端時,又要從用戶態buffer將內容拷貝到socket在內核的緩衝區。 # 這樣就要進行兩次內核態和用戶態之間的數據拷貝。 # 零拷貝的意思就是,nginx調內核讀取文件的時候,直接告訴內核文件和socker(也就是輸入和輸出),而後內核讀取數據後,直接將數據用socket發送給客戶端。 # 這就減小了2次拷貝的時間,效率大大提升。 35 sendfile on; # 就是socket buffer是否寫滿才發送,相似於執行不執行flush。 36 #tcp_nopush on; 37 # http1.1擴充了一個字段交keepalive,就是tcp連接保持長鏈接多久才斷開。作實驗咱們爲了看效果,設置爲0。正式環境應該配一個合適的值。 38 keepalive_timeout 0; 39 #keepalive_timeout 65; 40 # 返回時是否壓縮數據,減小IO和帶寬消耗。能夠提供更多的請求響應。 41 #gzip on; 42 # 其中一個虛擬服務器(nginx能夠支持多個虛擬服務器,他能夠用請求頭中的Host域名來區分,經過瀏覽器F12查看) # 也就是說若是DNS上有兩個域名指向同一個IP地址,nginx能夠經過兩個域名來提供2個虛擬服務,都使用80端口 43 server { # 監聽的端口是80 44 listen 80; # 虛擬服務器名,就是域名。例如DNS中有兩個域名,www.123.com,www.234.com,這裏填寫其中一個。 45 server_name localhost; # www.123.com 46 47 #charset koi8-r; 48 49 #access_log logs/host.access.log main; 50 #access_log "pipe:rollback logs/host.access_log interval=1d baknum=7 maxsize=2G" main; 51 # 訪問的根,也就是http://www.123.com/ 52 location / { # root是相對路徑,html就是咱們安裝nginx的地方/opt/nginx/html目錄 53 root html; # index用來定義默認根頁面 54 index index.html index.htm; 55 } 56 57 #error_page 404 /404.html; 58 59 # redirect server error pages to the static page /50x.html 60 # 61 error_page 500 502 503 504 /50x.html; 62 location = /50x.html { 63 root html; 64 } 65 66 # proxy the PHP scripts to Apache listening on 127.0.0.1:80 67 # 68 #location ~ \.php$ { 69 # proxy_pass http://127.0.0.1; 70 #} 71 72 # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 73 # 74 #location ~ \.php$ { 75 # root html; 76 # fastcgi_pass 127.0.0.1:9000; 77 # fastcgi_index index.php; 78 # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; 79 # include fastcgi_params; 80 #} 81 82 # deny access to .htaccess files, if Apache's document root 83 # concurs with nginx's one 84 # 85 #location ~ /\.ht { 86 # deny all; 87 #} 88 }
其中Location是很是重要的,咱們能夠參看官方文檔中Location的語法:
http://tengine.taobao.org/nginx_docs/cn/docs/http/ngx_http_core_module.html#location
能夠看到如下內容:
讓咱們用一個例子解釋上面的說法: location = / { [ configuration A ] } location / { [ configuration B ] } location /documents/ { [ configuration C ] } location ^~ /images/ { [ configuration D ] } location ~* \.(gif|jpg|jpeg)$ { [ configuration E ] } 請求「/」匹配配置A, 請求「/index.html」匹配配置B, 請求「/documents/document.html」匹配配置C, 請求「/images/1.gif」匹配配置D, 請求「/documents/1.jpg」匹配配置E。
符號解釋:
"=":精確匹配
"/dir/":當前路徑及子路徑都匹配,最大前綴匹配,但注意,是用這個字段去和用戶請求的URI匹配,而不是反過來。
"~":後面用正則表達式。區分大小寫
"~*":後面用正則表達式。不區分大小寫
"^~":阻斷,匹配到這裏就再也不進行正則匹配,例如上面的/images/1.gif,匹配到/images/,就再也不匹配後面的".gif結尾"。
匹配規則:
他們有優先級關係: "=" > "^~" > "~ | ~*" > "/ | /dir/"
普通location之間是無匹配順序的。而正則location之間是有順序的,只要匹配到第一個就不匹配後面的。
若是匹配到^~項,後面的正則也不進行匹配。
普通location和正則location之間,先匹配普通的,再考慮是否匹配正則:
這裏的考慮就是指「可能」,當普通location前使用了^~,則不匹配後面的正則。或者當普通location恰好匹配好(非最大前綴匹配),則也不匹配後面的正則。
以下圖所示:
2.添加一個虛擬服務器(yum本地源)
# 這裏添加一個虛擬服務器www.repo.com,將/mnt做爲repo源,經過nginx發佈 server { listen 80; server_name www.repo.com; location / { root /mnt; # autoindex就是將/mnt的文件列表展現出去 autoindex on; } }
這裏的域名爲www.repo.com,而訪問index.html的域名爲www.123.com,因此能夠區分咱們要訪問哪一個服務器。
配置完nginx.conf後,咱們要重置nginx:
systemctl reload nginx.service
將光盤掛載在/mnt:
mount /dev/cdrom /mnt
在瀏覽器操做系統的hosts中添加響應的DNS映射:
#在windows的hosts中添加如下內容 192.168.1.201 real-server-1 www.123.com www.repo.com
此時咱們訪問http://www.repo.com:80:
在訪問http://www.123.com:80:
咱們在瀏覽器的F12中查看一下兩個域名訪問時的host字段:
咱們能夠看到,兩次請求的請求頭中的Request URL分別是兩個域名,Nginx就能夠經過域名與配置文件的server_name匹配來區分咱們要訪問的服務,匹配到某個服務名後,再將域名後的URI(也就是"/"、"/search/"等)用location字段去匹配。
3.反向代理
在location中配置反向代理,將上面第一個server配置加上一個location作反向代理:
server { listen 8000; server_name www.123.com; location / { root html; index index.html index.htm; } # 反向代理,訪問192.168.1.202:80 location /ooxx { proxy_pass http://192.168.1.202:80/; } }
此時,咱們訪問www.123.com:80,就不是返回tengine的默認頁面,而是會幫咱們請求192.168.1.202:80的頁面;
從結果能夠看出,咱們經過www.123.com:80/ooxx(也就是real-server-1 192.168.1.201)訪問了real-server-2(192.168.1.202的服務),並返回給客戶端。
示例:反向代理www.baidu.com:
server { listen 8000; server_name www.123.com; location / { root html; index index.html index.htm; } # 反向代理,訪問192.168.1.202:80 location /ooxx { proxy_pass http://192.168.1.202:80/; } # 代理baidu首頁 location /baidu { proxy_pass https://www.baidu.com/; } }
使用瀏覽器訪問http://www.123.com/baidu:
注意,這裏的proxy_pass配置的是https://www.baidu.com。若是配置成http://www.baidu.com,則百度可能先返回頁面跳轉給瀏覽器,瀏覽器會直接用https://www.baidu.com去訪問百度。因此這裏必定要寫成https協議。
咱們使用代理後的百度進行搜索:
報錯信息:找不到URL,主要關注後面的URI(s?wd=*****):
咱們再添加一跳location:
server { listen 8000; server_name www.123.com; location / { root html; index index.html index.htm; } # 反向代理,訪問192.168.1.202:80 location /ooxx { proxy_pass http://192.168.1.202:80/; } # 代理baidu首頁 location /baidu { proxy_pass https://www.baidu.com/; } # 匹配以/s開頭的URI location ~* ^/s { proxy_pass https://www.baidu.com; } }
注意:location /baidu和location ~* ^/s的proxy_pass最後一個帶"/",一個不帶"/",區別很大。
若是帶"/"或"/xxx",則會直接使用其進行訪問,例如www.baidu.com/xxx。
若是什麼都不帶,則會使用location的匹配到的URI來串接在後面進行訪問,例如www.baidu.com/s?xxxxxxx(location ~* ^/s)。
此時使用代理baidu搜索"香港":
4.負載均衡
在nginx配置文件中配置負載均衡:
# 在server前面定義一個upstream池 upstream leeoo { server 192.168.1.121:80; server 192.168.1.202; } # 而後在對應的location中,將目標服務器的域名或IP替換爲leeoo location /ooxx { proxy_pass http://leeoo/; }
此時,當咱們訪問http://www.123.com:80/ooxx時,nginx會自動進行負載均衡,將輪詢訪問leeoo中的兩臺real server。
5.Session一致性問題
若是咱們的後端服務器使用的是tomcat等容器,須要爲用戶保存Session。當Nginx將一個用戶連接負載到不一樣的後端服務器時,咱們須要保證他們可以使用同一個Session對用戶進行驗證,不然會出現讓用戶重複登陸的問題;
1)首先,要保證集羣中的服務器時間一致性。
2)使用專門管理Session的軟件,或者使用memcache、Redis等內存數據庫等來幫助Tomcat共享Session。
在192.168.1.199上安裝memcached:
yum install memcached -y
使用如下命令啓動:
memcached -d -m 128m -p 11211 -l 192.168.1.199 -u root -P /tmp/
-m是使用128M內存空間
-p是使用11211端口進行通訊
-l是服務器地址
-u用戶名
使用netstat查看memcached監聽狀況:
[root@lvs-server-1 etc]# netstat -natp | grep 11211 tcp 0 0 192.168.1.199:11211 0.0.0.0:* LISTEN 1540/memcached
在192.168.1.202和192.168.1.121上安裝JDK和Tomcat:
下載JDK和Tomcat:
apache-tomcat-7.0.96.tar.gz jdk-7u80-linux-x64.rpm
安裝JDK:
rpm -i jdk-7u80-linux-x64.rpm
配置環境變量:
vi /etc/profile # 在最後添加 export JAVA_HOME=/usr/java/jdk1.7.0_80 export PATH=$PATH:$JAVA_HOME/bin # 使其生效 source /etc/profile
運行jps命令,檢查是否安裝成功:
[root@real-server-2 etc]# jps 1793 Jps
解壓Tomcat:
tar xf apache-tomcat-7.0.96.tar.gz
建立一個頁面,讓其打印Session:
cd ~/apache-tomcat-7.0.96/webapps/ROOT cp index.jsp index.jsp.bak
vi index.jsp from 192.168.1.202<br>Session: <%= session.getId()%>
啓動Tomcat:
cd ~/apache-tomcat-7.0.96/bin ./startup.sh
[root@real-server-2 bin]# ./startup.sh Using CATALINA_BASE: /root/apache-tomcat-7.0.96 Using CATALINA_HOME: /root/apache-tomcat-7.0.96 Using CATALINA_TMPDIR: /root/apache-tomcat-7.0.96/temp Using JRE_HOME: /usr/java/jdk1.7.0_80 Using CLASSPATH: /root/apache-tomcat-7.0.96/bin/bootstrap.jar:/root/apache-tomcat-7.0.96/bin/tomcat-juli.jar Tomcat started.
訪問192.168.1.202:8080:
重複刷新,Session是不會變化的,由於咱們訪問的是同一個Tomcat服務器。
在192.168.1.202和192.168.1.121上都安裝好Tomcat以後,咱們使用nginx來作負載均衡(192.168.1.201上):
在/opt/nginx/conf/nginx.conf中作以下配置:
upstream tom { server 192.168.1.121:8080; server 192.168.1.202:8080; } server { listen 80; server_name www.123.com; location / { root html; index index.html index.htm; } location /cat/ { proxy_pass http://tom/; } }
從新載入:
systemctl reload nginx
而後此時訪問www.123.com:80:
咱們能夠看到,重複訪問的時候,負載均衡是起效的,可是他們的Session不一致的。
停掉兩臺機器的Tomcat:
cd ~/apache-tomcat-7.0.96/bin ./shutdown.sh
配置Tomcat使用memcached:
cd /root/apache-tomcat-7.0.96/conf vi context.xml
在最後添加以下內容:
<Manager className="de.javakaffee.web.msm.MencachedBackupSessionManager" memcachedNodes="n1:192.168.1.199:11211" sticky="false" lockingMode="auto" sessionBackupAsync="false" requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$" sessionBackupTimeout="1000" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory" />
再爲Tomcat的lib中補充須要的一些jar包:
[root@real-server-2 tomcat_jar]# ll total 804 -rw-r--r-- 1 root root 43398 Feb 26 2019 asm-3.2.jar -rw-r--r-- 1 root root 94830 Feb 26 2019 kryo-1.04.jar -rw-r--r-- 1 root root 62112 Feb 26 2019 kryo-serializers-0.11.jar -rw-r--r-- 1 root root 142281 Feb 26 2019 memcached-session-manager-1.7.0.jar -rw-r--r-- 1 root root 11283 Feb 26 2019 memcached-session-manager-tc7-1.8.1.jar -rw-r--r-- 1 root root 4879 Feb 26 2019 minlog-1.2.jar -rw-r--r-- 1 root root 26511 Feb 26 2019 msm-kryo-serializer-1.7.0.jar -rw-r--r-- 1 root root 11615 Feb 26 2019 reflectasm-1.01.jar -rw-r--r-- 1 root root 407912 Feb 26 2019 spymemcached-2.7.3.jar
將這些jar包拷貝到Tomcat的lib目錄中:
cp ~/tomcat_jar/* ~/apache-tomcat-7.0.96/lib
從新啓動tomcat,後再次嘗試訪問http://www.123.com/cat,發現負載均衡正常,Session也不會發生變化。