實戰高併發nginx+redis+tomcat8.5實現負載均衡和session共享
目錄
前言
新型冠狀病毒氾濫,至今已有1w+同胞感染,在此深刻祝福他們早日康復。
學校依託易班APP展開了線上簽到的web應用,思路很簡單,沒有什麼複雜的,但是要命的不是程式碼,而是併發,學校共有4w人,可能是大家在家比較閒,流量高峰時期學校分配的土豆伺服器會崩掉,在此我參考前輩們的做法,決定用nginx+tomcat叢集的方式來實現流量的負載均衡,用redis來解決session共享問題
首先,我們知道nginx是:
Nginx (engine x) 是一個高效能的HTTP和反向代理web伺服器,同時也提供了IMAP/POP3/SMTP服務。Nginx是由伊戈爾·賽索耶夫為俄羅斯訪問量第二的Rambler.ru站點(俄文:Рамблер)開發的,第一個公開版本0.1.0釋出於2004年10月4日。
其將原始碼以類BSD許可證的形式釋出,因它的穩定性、豐富的功能集、示例配置檔案和低系統資源的消耗而聞名。2011年6月1日,nginx 1.0.4釋出。
Nginx是一款輕量級的Web 伺服器/反向代理伺服器及電子郵件(IMAP/POP3)代理伺服器,在BSD-like 協議下發行。其特點是佔有記憶體少,併發能力強,事實上nginx的併發能力在同類型的網頁伺服器中表現較好,中國大陸使用nginx網站使用者有:百度、京東、新浪、網易、騰訊、淘寶等。
官方測得能支援五萬併發,這個就算在實際併發中有縮水的情況,應對學校的併發數是遠遠夠了,你總不可能讓四萬人在同一時間同時訪問吧,實現的大致思路如下:
1. nginx配置
#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; # include hosts/tomcat_test.conf; upstream localhost{ server 127.0.0.1:8080 weight=1; server 127.0.0.1:8081 weight=1; } server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { # root html; # index index.html index.htm; proxy_pass http://localhost; } #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; #} } # another virtual host using mix of IP-,name-,and port-based configuration # #server { # listen 8000; # listen somename:8080; # server_name somename alias another.alias; # location / { # root html; # index index.html index.htm; # } #} # HTTPS server # #server { # listen 443 ssl; # server_name localhost; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 5m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # location / { # root html; # index index.html index.htm; # } #} }
最核心的兩個地方,負責轉發
不得不說的是nginx實現負載均衡的幾個方式:
-
輪詢
預設配置
upstream xsdemo{
server 192.168.110.128:8081;
server 192.168.110.128:8082;
} -
weight(權重)
正向代理 設定權重
upstream xsdemo{
server 192.168.110.128:8081 weight=10;
server 192.168.110.128:8082 weight=10;
} -
ip_hash
每個請求按訪問ip的hash結果分配,這樣每個訪客固定訪問一個後端伺服器,可以解決session(並不是共享session解決)的問題。 網傳這種方式其實不算是負載均衡,但是我覺得算吧,假如A和B訪問,A去tomcat1,B去tomcat2,雖然session可以不通過共享的方式來減輕某個tomcat的壓力,但是我覺得也算是負載均衡的一種實現思路吧!
upstream xsdemo{
ip_hash;
server 192.168.110.128:8081 weight=10;
server 192.168.110.128:8082 weight=10;
} -
fair(未經測試) 按後端伺服器的響應時間來分配請求,響應時間短的優先分配
-
url_hash(未經測試)按訪問url的hash結果來分配請求,使每個url定向到同一個後端伺服器,後端伺服器為快取時比較有效
2. tomcat叢集搭建方式
tomcat8下載(官網)http://tomcat.apache.org/download-80
需要我們做的
-
下載jar包:分別是 commons-pool2-2.2.jar、jedis-2.8.2.jar、tomcat-redis-session-manager.jar,注意各個版本之間的依賴,這個依賴經實際測試沒有問題,我放到了我的百度網盤裡,請自行下載:連結:https://pan.baidu.com/s/14UU4rnMGxaggqKuBs7vn6Q
提取碼:xlt5
這個實現的還得得益於一個外國大神,我參考了他的GitHub才搞成了的!
原始碼地址:https://github.com/jcoleman/tomcat-redis-session-manager -
下載我的jar包後,將這三個jar包放到tomcat目錄下的lib資料夾裡
-
更改xml配置檔案:
(1)更改conf目錄下的context.xml,在上方加入如下所示
<Valve className="com.seejoke.tomcat.redissessions.RedisSessionHandlerValve"/>
<Manager className="com.seejoke.tomcat.redissessions.RedisSessionManager"
host="localhost"
port="6379"
database="0"
password="123456"
maxInactiveInterval="1800" />
host:主機。
port:埠。
database:這裡指的是redis的資料庫名。
password:redis的密碼,這個一定要設定,我就吃了很多虧,因為沒有設定這個!
maxInactiveInterval:session存活時間,以秒為單位。
(2)更改tomcat埠,這裡要改三個埠,不會出現閃退的情況:
<!--web服務埠-->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!--推出埠-->
<Server port="8005" shutdown="SHUTDOWN">
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
更改三個埠直至互不佔用
3. redis
redis是不需要做什麼具體配置的,只需要將密碼設定一下就好了
在redis命令列,或者在RDM裡面設定一下:
config set requirepass 123456
所有的跑起來,先執行tomcat,在執行nginx,比較雞肋的就是nginx的Windows版本得在工作管理員裡面殺死然後在啟動才算重啟。
效果圖:
大功告成!
4. 後續,SQL語句的優化
後來專案的推進,又搞了一個SQL語句的優化我想把它一併寫到這裡
原語句擠不太清楚,大概就是這樣的:
SELECT DISTINCT message.id,message.content,message.title,message.date,IF(EXISTS(SELECT * FROM tongzhi WHERE yb_userid = '16751040' AND tongzhi.tongzhi = message.id),1,0) AS is_read FROM message
冗長而且這個併發度一點都不高
第一沒有走索引
第二用exist會進行全表搜尋
優化思路:
- 建立索引,在where條件語句後面
- 用left join代替eixt
- 去掉if判斷
- 資料庫垂直分表,因為有一部分例如學號和個人ID是永不會變的,而其他的例如這個狀態值是會改變的而且是頻繁改變的
- 用MyISAM儲存引擎代替InnoDB,有必要提幾句這兩個儲存引擎有哪些異同:
MyISAM 它不支援事務,也不支援外來鍵,尤其是訪問速度快,對事務完整性沒有要求或者以SELECT、INSERT為主的應用基本都可以使用這個引擎來建立表。
InnoDB InnoDB儲存引擎提供了具有提交、回滾和崩潰恢復能力的事務安全。但是對比MyISAM的儲存引擎,InnoDB寫的處理效率差一些並且會佔用更多的磁碟空間以保留資料和索引。
【更多區別:】
MyISAM是非事務安全型的,而InnoDB是事務安全型的。
MyISAM鎖的粒度是表級,而InnoDB支援行級鎖定。
MyISAM支援全文型別索引,而InnoDB不支援全文索引。
MyISAM相對簡單,所以在效率上要優於InnoDB,小型應用可以考慮使用MyISAM。
MyISAM表是儲存成檔案的形式,在跨平臺的資料轉移中使用MyISAM儲存會省去不少的麻煩。
InnoDB表比MyISAM表更安全,可以在保證資料不會丟失的情況下,切換非事務表到事務表(alter table tablename type=innodb)。
回到我們的場景,我們這個應用對於資料的準確性要求並不是很高,退而求其次的我們會用MyISAM的更快一些,在保證資料要求的前提下。這個就像是HashMap的擴容因子為0.75以空間換取時間的做法一樣,魚和熊掌不可得兼嘛 - 其他的像什麼資料庫表資料多了就自動分表啊,MySQL分庫,主從資料庫的搭建都是可以應對高併發的很好的思路。