實戰高併發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實現負載均衡的幾個方式:

  1. 輪詢
    默認配置
    upstream xsdemo{
    server 192.168.110.128:8081;
    server 192.168.110.128:8082;
    }

  2. weight(權重)
    正向代理 設置權重
    upstream xsdemo{
    server 192.168.110.128:8081 weight=10;
    server 192.168.110.128:8082 weight=10;
    }

  3. 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;
    }

  4. fair(未經測試) 按後端服務器的響應時間來分配請求,響應時間短的優先分配

  5. url_hash(未經測試)按訪問url的hash結果來分配請求,使每個url定向到同一個後端服務器,後端服務器爲緩存時比較有效

2. tomcat集羣搭建方式

tomcat8下載(官網)http://tomcat.apache.org/download-80

在這裏插入圖片描述
需要我們做的

  1. 下載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

  2. 下載我的jar包後,將這三個jar包放到tomcat目錄下的lib文件夾裏
    在這裏插入圖片描述

  3. 更改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會進行全表搜索
優化思路:

  1. 創建索引,在where條件語句後面
  2. 用left join代替eixt
  3. 去掉if判斷
  4. 數據庫垂直分表,因爲有一部分例如學號和個人ID是永不會變的,而其他的例如這個狀態值是會改變的而且是頻繁改變的
  5. 用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以空間換取時間的做法一樣,魚和熊掌不可得兼嘛
  6. 其他的像什麼數據庫表數據多了就自動分表啊,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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章