Nginx 反向代理實現線上測試環境(微信開發類專案)

ChoChik發表於2018-11-29

成文時間: 2018-11-29 11:18:10

環境說明
Ubuntu 16.04 LTS
Nginx version: nginx/1.10.3 (Ubuntu)
PHP 7.1.18
Laravel 5.5

需求說明

微信開發類專案,需要要除錯微信介面,本地開發上可以採用微信開發者工具和微信測試公眾號模擬執行環境和介面。但有部分微信商戶號的介面例如微信支付,目前需要用沙盒模擬的方式開發,不夠方便。
因此,我們想要構建一個能用於微信開發專案的線上測試環境,能調取到真實微信公眾號的介面方便線上測試。

思路

要構建線上測試環境最直接的想法就是額外購買一臺硬體引數、環境配置與生產環境伺服器完全一致的伺服器。對接微信平臺方面,還需額外註冊一個開通微信認證的微信公眾號。
這麼做雖然能解決問題,但增加了額外成本,而且增加了伺服器、微信賬號等等的額外維護工作,對於我們這種初創技術團隊來講不是上策。

有沒有更低成本和更便於系統維護迭代的方案呢?
經過一番折騰,我們摸索出如下方案,分享出來歡迎交流。

解決方案分享

問題的關鍵在於如何解決解決微信接入問題

做過微信開發的朋友都知道,接入微信,需要在微信公眾號後臺填寫一個繫結伺服器的備案域名


問題是 URL 這一項,可填值是唯一的且區分子域名。
例如:

http://www.project.com/login 指向生產環境
http://dev.project.com/login 指向線上測試環境
那麼微信公眾號只能選擇接入一個域名。

我們採取Nginx反向代理的功能去解決,即當請求訪問 http://www.project.com 這一域名,Nginx伺服器通過路徑規則匹配,實現請求轉拋,以指向不同的專案目錄。
例如:

http://www.project.com/login 指向生產環境
http://www.project.com/dev/login 指向線上測試環境

我們通過配置 Nginx 伺服器,使得請求匹配到 /dev/ 則將請求轉拋給測試環境下的專案去處理

下面是 Nginx 配置的程式碼實現

代理管理配置檔案 /etc/nginx/sites-available/project.proxy.conf

server {
    listen 80;

    server_name 127.0.0.1 www.project.com;

    index index.html index.php;

    charset utf-8;
    access_log /var/log/nginx/project.proxy.access.log;
    error_log /var/log/nginx/project.proxy.error.log;

    # 生產環境
    location / {
        proxy_pass http://127.0.0.1:9001;
        proxy_set_header Host $host:$server_port;
    }

    # 線上測試環境
    location ^~ /dev/ {
        proxy_pass http://127.0.0.1:9002;
        proxy_set_header Host $host:$server_port;
    }
}

除了 Nginx 伺服器的配置,還需要對專案的配置環境做設定。
主要是進入專案的請求加上統一字首,這個不同框架有不同的實現,下面僅以 php 框架 Laravel 為例:
Laravel 專案下,
先在 .env 配置環境中增加變數 PREFIX

PREFIX=/dev  // 前面加 / 是為了解決後續靜態資源處理的問題,算是一個小坑

然後修改 app/Providers/RouterServiceProvider.php 檔案中的 mapWebRouters() :

protected function mapWebRoutes()
    {
        // 從配置檔案中獲取字首
        $prefix = env("PREFIX") === "" ? "" : explode('/',env("PREFIX"))[1];
        Route::prefix($prefix) // 給路由新增統一字首 
             ->middleware('web')
             ->namespace($this->namespace)
             ->group(base_path('routes/web.php'));
    }

實際部署中生產環境和線上測試環境的 .env 檔案中的 PREFIX 分別配置為 "" 和 "/dev" 即可。

到這裡,本以為萬事俱備。

一訪問卻發現靜態檔案404。

排查後發現,問題在於靜態檔案引用路徑,例如:

<script src="/dev/test/test.js"></script>

實際頁面訪問時,這個資源請求路徑會被拼接上域名,即最終變為:

http://www.project.com/dev/test/test.js

而 Laravel 的靜態資源全部放置在 public 目錄下,由於新增了統一路由字首,所以上面的 URL 並不會指向 public 目錄所在的資源目錄,而是被當作路由請求處理了...

最終我的處理方案是,修改 Nginx 配置,對請求的 URI 路徑做規則校驗,匹配到 .js 或 .css 結尾的請求,去除 URL 中的 /dev/ 字串。

修改後的 /etc/nginx/sites-available/project.proxy.conf 配置程式碼如下:

server {
    listen 80;

    server_name 127.0.0.1 www.project.com;

    index index.html index.php;

    charset utf-8;
    access_log /var/log/nginx/project.proxy.access.log;
    error_log /var/log/nginx/project.proxy.error.log;

    # 生產環境
    location / {
        proxy_pass http://127.0.0.1:9001;
        proxy_set_header Host $host:$server_port;
    }

    # 線上測試環境
    location ^~ /dev/ {
        proxy_pass http://127.0.0.1:9002;
        # 靜態資源過濾 /dev/
        if ($request_uri ~* .(?:js|css)$) {
                rewrite /dev/(.+)$ /$1 break;
        }
        proxy_set_header Host $host:$server_port;
    }
}

現在當訪問

http://www.project.com/dev/test/test.js

請求轉拋後,會被處理成

http://www.project.com/test/test.js

因此能正常訪問到 Laravel 專案中的 public 資源目錄

坑記錄

過程中還是踩了不少的坑:

  1. Nginx 伺服器代理配置時

    proxy_set_header Host $host:$server_port;

    這行程式碼必不可少,否則初次訪問 http://www.project.com/dev/login 能去到正確目錄,但後續專案內所有請求都會被設定為 http://127.0.0.1:9002 開頭...

  2. 專案中的靜態資源引用路徑也要改變,如:

    <script src="/dev/test/test.js"></script>

    上面的是編譯後的執行程式碼,實際開發時的程式碼應該是( Laravel 框架的 blade 模板語法):

    <script src="{{ env('PREFIX') }}/test/test.js"></script>
  3. 靜態資源方面還存在的隱患,如果資源是非同步引用的,那就涼涼。這種情況多出現在引用工具庫,另外在引用圖片等靜態資源方面也會比較麻煩...

待優化問題

靜態資源引用方面存在的隱患促使需要更優解決方案,目前想到的優化方案是:

  1. 靜態資源全部採用 CDN 方式引用,避免路徑問題。
  2. 樣式 icon 全部採用 font-icon 方式,避免圖示元素的路徑問題。

總結

本文主要分享了單一伺服器構建微信專案線上測試環境的方式(同一個公眾號),主要有以下要點:

  1. 通過 Nginx 反向代理機制,實現請求分發
  2. 通過 專案環境配置,實現請求在專案內新增統一路由字首
  3. 專案中靜態資源路徑問題導致的坑,對此的處理方法及優化思路

參考文獻:

nginx 配置之 proxy_pass 神器!
proxy_pass反向代理配置中url後面加不加/的說明
laravel獲取當前的url以及當前的基礎域名方法彙總
Nginx中if語句中的判斷條件

相關文章