前言
新型冠狀病毒氾濫,至今已有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分庫,主從數據庫的搭建都是可以應對高併發的很好的思路。
5. 後續2.0
面試裏問到了這個點,說我的redis假如不可用了怎麼辦,nginx宕機了服務就整個起不來了等等一些列的問題
其實早就思考過這個問題,但是呢,因爲博主先去幹活了,能用就行的原則就先放了,今天補上
5.1 Redis集羣搭建
選來選去採用集羣(cluster-enable)方式,其他的這裏暫不強調
用docker方式快速搭建Redis cluster集羣
redis-cluster規定,至少需要3個master和3個slave
具體詳細的我就不過多提了,參考這位老哥的吧:
5.1 NGINX集羣搭建
這個就相對簡單一些,就是主節點配置conf文件
upstream 172.17.0.2和server兩個字典
具體配置參考這個老哥
因爲不涉及原理,沒什麼可研究的,就跟Liunx命令一樣,記住就OK