# 2. 反向代理
#### 1. 什么叫反向代理服務器?
要說反向代理服務器,先來說一般的代理服務器。代理就是受委托去做一些事。假如用戶A委托B去做一些事,做完之后B告訴A結果。在代理服務器中也是一樣的道理,用戶A通過代理服務器B訪問網站C(`www.example.com`),請求先到代理服務器B,B再轉發請求到網站C,代理服務器B是真正訪問網站C的,訪問之后再把網站C的應答結果發給用戶A。這樣給用戶A的感覺是C直接提供服務的一樣,因為看不到B的整個處理過程。代理服務器是一個中間者,是充當轉發請求的角色。這種代理也叫`正向代理`。
使用正向代理是要在客戶端進行設置,比如瀏覽器設置代理服務器的域名或IP,還有端口等。
正向代理的作用有很多,例如,能訪問本無法訪問的,加速,cache,隱藏訪問者的行蹤等,具體的不再詳述了。
`反向代理`(reverse proxy)正好與正向代理相反,對于客戶端而言代理服務器就像是原始服務器,并且客戶端不需要進行任何特別的設置。假如用戶A訪問網站B,這個時候網站B充當了web服務器,也充當了反向代理服務器,它充當的代理服務器的角色是這樣,假如用戶A要得到網站C的內容,而用戶A又不能直接訪問到(例如網絡原因),而服務器B可以訪問到網站C,那服務器可以得到網站C的內容再存起來發給用戶A,這整個過程用戶A是直接和代理服務器B交互的,用戶A不知道網站C的存在,這個web服務器B就是一臺反向代理服務器,這個網站C就是上游服務器(upstream servers)。
反向代理的作用是,隱藏和保護原始服務器,就像剛才的例子,用戶A根本不知道服務器C的存在,但服務器C確實提供了服務。還有,就是負載均衡。當反向代理服務器不止一個的時候,就可以做成一個集群,當用戶A訪問網站B時,用戶A又需要網站C的內容,而網站C有好多服務器,這些服務器就形成了集群,而網站B在請求網站C,就可以有多種方式(輪循,hash等),把請求均勻地分配給集群中的服務器,這個就是負載均衡。
。
#### 2. 示例
我們先來看最一個最簡單的例子。
##### 2.1 最簡單的反向代理
nginx的反向代理是依賴于[ngx\_http\_proxy\_module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html)這個module來實現的。
反向代理服務器能代理的請求的協議包括http(s),FastCGI,SCGI,uwsgi,memcached等。我們這里主要集中在http(s)協議。
我有一個網站,用的是https協議來訪問的。用這個協議訪問的網站在chrome等瀏覽器的地址欄是可以看到一個綠色的代表安全的標志的。你請求的所有資源都要是https的,它才會出現。假如你請求了一張外部的圖片,而這張圖片是以http協議請求的,那這個時候那個安全的標志就不存在的。
所以我要把這個https協議的圖片請求反向代理到http協議的真實圖片上。https協議的這張圖片是不存在,而它有一個地址實際指向的內容是http協議中的圖片。
```
# https
server {
server_name www.example.com;
listen 443;
location /newchart/hollow/small/nsh000001.gif {
proxy_pass http://image.sinajs.cn/newchart/hollow/small/nsh000001.gif;
}
location /newchart/hollow/small/nsz399001.gif {
proxy_pass http://image.sinajs.cn/newchart/hollow/small/nsz399001.gif;
}
```
假如我的網站是`www.example.com`這樣就能使用`https://www.example.com/newchart/hollow/small/nsh000001.gif`,它指向是`http://image.sinajs.cn/newchart/hollow/small/nsh000001.gif`。
##### 2.2 動態轉發
我們的網站不止是展示用的,我們還要處理動態請求,例如表單等。所以nginx也要和php,java,ruby等語言配合。
下面的例子是nginx和unicorn(ruby的應用服務器)的一個例子。
```
upstream rails365 {
# Path to Unicorn SOCK file, as defined previously
server unix:///home/yinsigan/rails365/shared/tmp/sockets/unicorn.sock fail_timeout=0;
}
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
server_name www.rails365.net;
root /home/yinsigan/rails365/current/public;
try_files $uri/index.html $uri @rails365;
location @rails365 {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://rails365;
}
}
```
先從`try_files $uri/index.html $uri @rails365;`這句說起,它先找根目錄`/home/yinsigan/rails365/current/public`下的index.html,假如是`www.rails365.net/about.html`還是會找根目錄下的about.html,如果都找不到,才會執行`@rails365`的部分,也就是`location @rails365`。
前面兩行是設置請求的頭部,第三行是設置不轉地址,這些先不管。來看第三行`proxy_pass http://rails365;`。這行會反向代理到`upstream rails365`指定的內容。`upstream`里面指定了一個服務器,這個服務器和nginx是同一臺機器的,用的是unix socket來連接,連接的是一個unicorn進程。
總結起來是這樣的。假如用戶要訪問`https://www.rails365.net/articles/`,這個請求不能只靠nginx,因為又不是以.html結尾,所以轉發給了upstream所指向的服務器,轉發請求的方式是unix socket,到了unicorn進程,unicorn處理后交給nginx,nginx才最終發給客戶。在這里,nginx還起到一個cache和保護的作用,unicorn就是上游服務器。
##### 2.3 websocket
關于webcoket的概念,這里不再詳細,可以參照這篇文章。
```
upstream ws {
server unix:///home/eason/tt_deploy/shared/tmp/sockets/puma.sock fail_timeout=0;
}
server {
location /ws/ {
proxy_pass http://ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
```
先由http協議升級為ws協議,然后通過unix socket連接到puma進程,一個支持ws協議的進程。原理跟上面的unicorn差不多。
注意:nginx要支持websocket協議,必須是 1.3.13或以上版本。
要測試是否成功,有兩種比較簡單的方法。
第一種是在chrome瀏覽器上console那里直接訪問。
比如`new WebSocket('ws://www.example.com/wx');`
第二種就是在chrome的開發者工具,network那里看有沒有101協議的請求。
比如。

完結。