【小哥哥, 跨域要不要了解下】NGINX 反向代理

圈圈的圈發表於2018-12-10

系列文章:

原本本系列文章是不打算詳寫 NGINX 反向代理的. 至於為什麼不想寫呢? 當然是因為我不太會咯~~

2018-12-10-17-47-38
但是, 不久前有大佬點了這道菜, 那當然就得上啦 ?

代理是個啥

既然要聊反向代理, 那首先得知道代理是個啥吧? 嗯.

2018-12-10-17-51-05

正向代理

比如, 你買束花, 想要給隔壁工位的測試妹子小麗表白. 但是又怕被人家直面拒絕太沒面子. 於是你把鮮花委託給平時和小麗一起的測試小夥伴小紅. 讓她幫忙把花送給小麗. 這就是一個簡單的代理過程, 小紅作為代理幫你把花送給了小麗, 當然這種情況在現實中並不推薦使用, 因為難以避免中間商賺差價 ?.

在上面的例子中, 你作為客戶端(請求方), 想要向服務方(小麗)發起請求. 但是礙於面子你主動找到了第三方(小紅)作為代理向服務方傳送請求, 這種情況就是常說的正向代理. 正向代理在網際網路中的使用主要是科學上網, 你想訪問谷歌但是礙於防火牆你只能通過vpn伺服器作為代理才能訪問. 這個時候一般也要找值得信賴的vpn廠商, 避免中間商賺差價 ?.

2018-12-10-19-09-12

反向代理

關於反向代理的例子, 那就比較多啦. 比如, 孤獨的你躺在床上夜不能寐. 於是乎, 拿出手機, 點亮了螢幕, 撥通 10086, 中國移動就會隨機分配一個當前處於空閒的客服MM, 你可以和客服MM聊聊天, 問問她家住哪裡, 有沒有男朋友, 她的微訊號, 她的手機號, 星座, 八字.......

在這個例子中, 中國移動就充當了反向代理的角色. 你只需要撥打 10086. 至於會不會分配到 MM 會分配到哪個 MM 在接通之前你都是不知道的. 反向代理在網際網路中的使用主要是實現負載均衡. 當你訪問某個網站的時候, 反向代理伺服器會從當前網站的所有伺服器中選擇一個空閒的伺服器為你響應. 用於均衡每臺伺服器的負載率.

2018-12-10-19-09-56

修改 hosts 完成域名繫結

mac 使用者直接執行 vim /private/etc/hosts 在 hosts 檔案最後新增一行:

127.0.0.1 a.com
複製程式碼

這一句是什麼意思呢? 就是告訴我們的電腦訪問 a.com 的時候, 無需請求 DNS, 直接指向我們本機.

ps: win 環境下, hosts 檔案在 C:\Windows\System32\drivers\etc 資料夾下. 如果沒有許可權修改, 把 hosts 檔案先拷貝到別的位置, 通過編輯器開啟並新增最後一行內容以後再剪下到原來的位置替換即可.

驗證: 開啟命令列視窗執行 ping a.com, 如果訪問的 ip 為 127.0.0.1 說明我們的域名繫結就完成啦 ^_^

6af89bc8gw1f8r5kn9ezgg202s02smzp

安裝 nginx

要做 NGINX 反向代理, 肯定要安裝 nginx, 本文安裝步驟示例環境為 mac, win 的小夥伴, 可以百度一下嗷, 這個東西大同小異.

  • 安裝 brew 命令, 執行 ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
  • 安裝 nginx, 執行 brew install nginx
  • 啟動 nginx nginx, 如果報沒有許可權, 執行 sudo nginx

nginx 啟動後, 瀏覽器開啟 localhost:8080, 即可驗證. 出現以下介面說明安裝成功.

2018-12-10-19-31-35

nginx 配置初探

配置完 hosts 域名已經能夠成功繫結. 現在如果我們訪問 a.com 實際上是會訪問到我們的自己的電腦辣. 那還不抓緊試一下?

瀏覽器訪問 a.com

2018-12-10-19-58-29

這是什麼鬼????

2018-12-10-19-57-20

為什麼會 無法訪問此網站 呢? 我們下載安裝完 nginx 還沒有做任何配置. 接下來, 我們稍微配置一下就 OK:

  • 命令列切換到 nginx 配置目錄下 cd /usr/local/etc/nginx/servers
  • 建立並編輯配置檔案 vim test.conf, 在配置檔案中貼上以下內容
server {
    # 監聽80埠號
    listen 80;

    # 監聽訪問的域名
    server_name a.com;

    # 根據訪問路徑配置
    location / {
        # 把請求轉發到 https://www.baidu.com
        proxy_pass https://www.baidu.com;
    }
}
複製程式碼
  • 儲存檔案, 並執行 nginx -s reload 重啟 nginx. (此處原本有一個拼寫錯誤, 感謝StanYe小夥伴提出 ?)
  • 回到瀏覽器, 開啟 a.com 的頁籤, 強制重新整理.
    2018-12-10-20-09-04

恭喜你已經完成了第一個 nginx 配置.

9150e4e5gy1ftr3ghw3rdg208c08cwg3

建立跨域環境

通過一系列的折騰, 我們已經可以通過 nginx 將a.com 轉發到百度. 完成了第一步, 接下來我們建立跨域的 Case 並一步一步通過 nginx 配置實現跨域.

首先, 專案前後端新增 nginx 目錄, 使用者存放前後端程式碼. 程式碼結構如下圖所示.

2018-12-10-19-38-25

其次編寫前後端程式碼:

前端程式碼(./fe/nginx/index.html):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>CORS 實現跨域</title>
</head>
<body>
    <h3>CORS 實現跨域</h3>

    <script>
        var xhr = new XMLHttpRequest()
        xhr.open('GET', 'http://localhost:8888/api/getFriend')
        xhr.setRequestHeader('token', 'quanquanbunengshuo')
        xhr.withCredentials = true;
        xhr.onreadystatechange = function() {
            if(xhr.readyState === 4 && xhr.status === 200) {
                console.log(xhr.responseText)
                console.log(xhr.getAllResponseHeaders())
            }
        }
        xhr.send()
    </script>
</body>
</html>
複製程式碼

編寫完前端程式碼以後, 啟動前端 web 容器. live-server ./fe/nginx

2018-12-10-20-17-56

命令列中出現了黃色警告, 通知我們 8080 埠已經被佔用, 這又是為什麼呢? 大家請思考一哈.

2018-12-10-20-21-29

我們重新指定一個埠live-server ./fe/nginx --port=9999 哈哈, 換一個指令, 依舊是那麼順暢. ^_^

後端程式碼:

const http = require('http');

const PORT = 8888;

// 建立一個 http 服務
const server = http.createServer((request, response) => {
  console.log(request.headers)
  response.end("{name: 'quanquan', friend: 'guiling'}");
});

// 啟動服務, 監聽埠
server.listen(PORT, () => {
  console.log('服務啟動成功, 正在監聽: ', PORT);
});
複製程式碼

啟動後端服務 node ./be/nginx/index.js

完善 nginx 配置

前後端程式碼已經準備完成, 這一步我們就來點乾貨. 完成最後的配置.

  • 首先, 修改 nginx 配置, 把百度地址替換成本地的前端地址
server {
    # 監聽80埠號
    listen 80;

    # 監聽訪問的域名
    server_name a.com;

    # 根據訪問路徑配置
    location / {
        # 把請求轉發到 http://127.0.0.1:9999
        proxy_pass http://127.0.0.1:9999;
    }
}
複製程式碼
  • 修改完成 nginx 配置檔案以後, 切記執行 nginx -s -reload 重啟 nginx.
  • 訪問a.com
    2018-12-10-20-34-13

熟悉的報錯又出現了...

  • 修改前端專案中的介面地址
// 介面地址修改為當前域名下 /api 路勁下的 getFriend
xhr.open('GET', '/api/getFriend')
複製程式碼
  • 修改 nginx 配置檔案
server {
    # 監聽80埠號
    listen 80;

    # 監聽訪問的域名
    server_name a.com;

    # 根據訪問路徑配置
    location / {
        # 把請求轉發到 http://127.0.0.1:9999
        proxy_pass http://127.0.0.1:9999;
    }

    # 監聽根目錄下的 /api 路徑
    location /api/ {
        # 把請求轉發到 http://127.0.0.1:8888
        proxy_pass http://127.0.0.1:8888;
    }
}
複製程式碼

新加的對於 api 路徑的監聽的意思就是把關於後端 api 的請求轉發到後端專案上(哈哈, 當然這就是為啥好多後端介面都是要有 /api 開頭的啦). 重啟 nginx 以後, 再次重新整理瀏覽器, 後端返回的結果已經成功的列印到了控制檯, 本次跨域訪問任務完成.

2018-12-10-20-46-57

細心的小夥伴肯定發現了, 控制檯還有一個報錯. 這個是因為我們的專案中用到了 live-server 這個工具需要 websocket 導致的. 我們可以通過新增以下配置解決.

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
複製程式碼

2018-12-10-20-50-39

報錯消失 ?, 此時完整的 nginx 配置檔案為

server {
    # 監聽80埠號
    listen 80;

    # 監聽訪問的域名
    server_name a.com;

    # 根據訪問路徑配置
    location / {
        # 把請求轉發到 http://127.0.0.1:9999
        proxy_pass http://127.0.0.1:9999;

        # 相容websocket
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    # 監聽根目錄下的 /api 路徑
    location /api/ {
        # 把請求轉發到 http://127.0.0.1:8888
        proxy_pass http://localhost:8888;
    }
}
複製程式碼

前後端程式碼地址為: github

總結

至此, 我們已經通過 nginx 反向代理的方式實現了跨域訪問 api, 在系列文章第一篇對於跨域的解釋為: 跨域源於同源策略, 是瀏覽器保證使用者安全的行為. 我們使用的 nginx 反向代理實際上是對瀏覽器的一種 "哄騙", 讓它認為自己訪問到的是同域的 api. 實際上是在服務端做了個調包, 這個道理就如同你撥打 10086 你就認定了給你分配到的一定是中國移動的客服MM(客服GG也是有可能出現的 ?)而中國移動的客服MM就是一個很安全的聊天物件, 沒有必要再進行限制...

下集預告: 終於蹩腳的碼完了最後一行, 作為生產環境中最常用的 nginx 反向代理, 比我想象的要簡單很多很多. 由於涉及到諸多配置的步驟. 有寫的不明白的地方還望小夥伴們評論區一起討論. 下一節預計聊聊服務端代理 ServerProxy 這個也是我要做的介面測試工具需要用到的技術方案, See you.

相關文章