Node之可擴展性 --- Nginx反向代理建立
nodejs
Lastmod: 2019-12-15

在上一篇文章中,我們使用cluster來建立多process的應用,這個方法是我們上一篇所提到X軸擴展的複制的方法之一。

而這一篇文章,我們一樣是要來討論X軸擴展的複制的另一種方法 :

反向代理器

這種擴展的方法為,在不同的 port 或不同的機器上,我們會啟動多個應用程式,然後使用反向代理器來存取這些機器,用來分散流量。

他不會像 cluster 上有一個master process然後將工作分配給多個worker,而是有更多個獨立的程式執行在同一個機器不同 port上或是分散在相同的網路中的不同機器上,然後會以反向代理器為入口,由他處理請求並與後端的伺服器做處理,然後在由他回傳給客戶端。

下圖為該結構的圖示 :

那他這樣做有什麼優點呢 ? 事實上他就是 proxy 的用法,也就是說 :

他可以保護伺服器

反向代理器可以和我們上一章所說的cluster一起使用,例如單一機器使用 cluster 進行垂直擴展,再使用反向代理器來做水平性擴展。

本篇文章中我們將使用最常用來做反向代理器的Nginx

Nginx 做反向代理器,並配置負載平衡

nginx是一個網頁伺服器,它的設計架構和 nodejs 非常的相似,都是單一執行緒架構,並且還有豐富的模組庫和第三方工具可以使用,非常的方便啊。

這邊我們將要使用nginx來作為反向代理器,並且進行負載平衡的功能,它要做的工作就是 :

我們有多台伺服器,然後請求進來要將請求分給其它台伺服器。

首先我們先安裝 nginx

// ubuntu
apt-get install nginx

// mac
brew install nginx

然後我們簡單的建立一個 server,它每一次收到請求時,都會回傳這個工作是那個port來進行處理。

// app.js

const http = require('http');

http.createServer(function (req, res) {
    console.log("master:" + process.pid);

    res.writeHead(200);
    res.write("port:" + this._connectionKey.toString());
    res.end();

}).listen(process.env.PORT || process.argv[2] || 8080, function () {
    console.log('started:' + process.pid);
});

接下來我們直接使用上一篇有說到的forever來開啟四個應用程式,並且每一個都給予指定的port

forever start app.js 8081 
forever start app.js 8082
forever start app.js 8083 
forever start app.js 8084 

然後我們可以執行forever list來看看已啟動的應用程式,這邊每一個實例都是一個 process

info:    Forever processes running
data:        uid  command                                        script                forever pid   id logfile     uptime
data:    [0] P8D5 /Users/mark/.nvm/versions/node/v6.2.1/bin/node server_nochid.js 8081 39585   46636    /Users/mark/.forever/P8D5.log 0:2:23:56.342
data:    [1] MKn0 /Users/mark/.nvm/versions/node/v6.2.1/bin/node server_nochid.js 8082 39618   46637    /Users/mark/.forever/MKn0.log 0:2:23:56.342
data:    [2] wAAK /Users/mark/.nvm/versions/node/v6.2.1/bin/node server_nochid.js 8083 39637   46638    /Users/mark/.forever/wAAK.log 0:2:23:56.339
data:    [3] pcFf /Users/mark/.nvm/versions/node/v6.2.1/bin/node server_nochid.js 8084 39648   46639    /Users/mark/.forever/pcFf.log 0:2:23:56.335

接下來我們就將 nginx 反向代理器設置負載平衡

首先我們先進到某個位置找到nginx.conf,然後套用如下設定,由於不同的系統平台位置有異,所以要自已去找找囉。

worker_processes  1;


events {
    worker_connections  1024;
}

http {
	upstream myproject {
		server 127.0.0.1:8081;
		server 127.0.0.1:8082;
		server 127.0.0.1:8083;
		server 127.0.0.1:8084;
	}

	server {
		listen 80;

		location / {
			proxy_pass http://myproject;
		}
	}
}

我們根據上面的設定,來理解一下他裡面的參數意義。

  • worker_process : 用來指定 nginx 要開啟的子進程的數量,建議根據 cpu 有幾個就開幾個。
  • events : 該模組用來指定 nginx 的工作模式。
  • events.worker_connections : 用來指定每個進程的最大請求數,默認為1024
  • http : 該模組為核心,它負責 http 伺服器的所有配置。
  • http.upstream : 該模組用來處理負載均衡的配置,像我們上面的設定就是將該http的連線經過這個 nginx,然後負載均衡到我們設定的 8081、8082、8083、8084 這四個 server 上。
  • http.server : 它用來定義一個主機。
  • http.server.listen : 定義該主機的 port。
  • http.server.location : 該模組主要用來處理定位的,基本上反向代理負載均衡位置等都要在這處理。

設定好後,我們來確任一下 conf 有沒有設定錯誤,我們執行sudo nginx -t,然後如果正確的話會輸出下列結果 :

nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful

確定成功後,我們還要在執行以下指令,來重新讀取 conf 檔。

sudo nginx -s reload

但有點要注意,如果你之前有將 nginx 停止或關閉記得要在執行sudo nginx來開啟它,不然會出現下面的錯誤訊息。

nginx: [error] invalid PID number "" in "/usr/local/var/run/nginx.pid"

接下來我們做以下的實驗 :

發送請求到http://127.0.0.1後,如果有設置好,目前預設,應該是會每次的請求都會在不同的 port 。

我們直接發個五次請求。

curl -G http://127.0.0.1

然後你會看到回傳的結果如下,下面結果就證實了我們的負載平衡反向代理有設置成功,你每次發送127.0.0.1反向代理器會自動的從四個伺服器(8081、8082、8083、8084)中,選出一個來處理。

port:6::::8081%
port:6::::8082%
port:6::::8083%
port:6::::8084%
port:6::::8081%

但這裡我們就還要在思考一個問題,負載平衡他是如何決定要用那個呢 ?

那要如何決定那個工作分配給誰呢 ?

在 nginx 中,負載平衡模組總共有提供四個方法給使用者。

輪詢 ( round robin )

nginx 預設的分配方法,假設你有個用戶叫 mark ,而以有三個 port 分別為8081、8082、8083,然後輪詢就會你配置的順序來分配請求,如下 :

request 1 => port 8081
request 2 => port 8082
request 3 => port 8083
request 4 => port 8081

基於權重 weight

接下來是權重分配,事實上,上面的輪詢也算是這種類型,只是他weight默認為1,所以這也代表,這四個分別處理1(weight)/4(weight加總) = 25%的資料量。

我們將 nginx conf 修改成下列這樣來測試看看。

	upstream myproject {
		server 127.0.0.1:8081 weight=1;  // 10%
 		server 127.0.0.1:8082 weight=2;  // 20%
		server 127.0.0.1:8083 weight=3;  // 30%
		server 127.0.0.1:8084 weight=4;  // 40%
	}

結果如下統計顯示。

84
83
82
84
81
83
84
82
83
84
--------
84 => 4次
83 => 3次
82 => 2次
81 => 1次

這種方式的優點就在於,你可以將效能較好的伺服器權重設定高一些,讓他可以處理更多的請求。

基於 ip_hash

這種方式會根據每次請求的 ip的 hash 結果作分配,也就是說,同樣的 ip 會固定到同樣的伺服器來進行處理。

	upstream myproject {
	 ip_hash;
		server 127.0.0.1:8081 ;
 		server 127.0.0.1:8082 ;
		server 127.0.0.1:8083 ;
		server 127.0.0.1:8084 ;
	}

執行結果,因為我都是本機打,所以當然都會是一樣的。

81
81
81
81
81

這種方法是用來處理session共享的問題,在分布式架構中,有一個問題一定會被問到,那就是 :

要如何處理 session ?

因為在分布式架構中,有多台的伺服器,而我們也知道 session 是存在某台伺服器上,如果我們第一次請求在 port 81 上,而第二次是在 port 82 上,如果兩次都有修改到 session,那就會不同步了。

目前大至上有以下幾種的解法。

  • 不使用session,換用cookie : 簡單,但缺點在於如果用戶禁 cookie 了,你就不用玩了。
  • session 存在數據庫 : 資料庫的I/O就會加重,而且如果資料庫也是分散式的,還要進行 session 資料表的同步。
  • session 存在 redis 中 : 這種方法好像目前都比較推。
  • 將相同 ip 的請求導致同一台伺服器 : 這就是我們現在所說明的功能,這種方法也算不錯用,但有一個前提假設,那就是nginx 是最前端的伺服器,保證得到正確的 ip

結論

本篇文章中說明了以下幾個重點。

  • 為什麼要使用反向代理器與負載平衡。
  • 使用 nginx 建立反向代理器與負載平衡。
  • nginx 所提供的負載平衡分配的方法。

這些都是可以幫助我們處理前一篇所說的X軸擴展中的複制,而他又和上一篇cluster不同點在於cluster是屬於同一台機器的垂直擴展,而本篇所說明的則屬於多台機器的水平擴展,而至於接下來的文章,我們還會繼續討論X軸這個主軸

參考資料

comments powered by Disqus