1. 程式人生 > 資料庫 >實戰高併發nginx+redis+tomcat8.5實現負載均衡和session共享

實戰高併發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分庫,主從資料庫的搭建都是可以應對高併發的很好的思路。