企業項目開發的重要知識之多環境
本文轉載自微信公眾號「程序員魚皮」,作者魚皮 。轉載本文請聯系程序員魚皮公眾號。
大家好,我是魚皮,今天給大家分享企業項目開發的重要知識 —— 多環境。
本文大綱:
魚皮 - 多環境技術大綱
什么是多環境?
先思考一個問題。
假如我們有一個數百萬用戶正在用的網站,網頁文件部署在幾臺服務器上。那現在我們要開發上線一個新功能,應該怎么做呢?
老弟小阿巴問:寫好代碼后,直接更新服務器上的網頁文件么?
我一巴掌抽過去:那萬一你的代碼有 Bug,不就影響到線上用戶的使用了么?
老弟思考了下:那寫好代碼后,在本地測試運行沒有問題后,再發布上線?
我:思路不錯,但問題在于,如果本地和線上運行項目時,連接的是同一個數據庫,那么當你在本地測試向數據庫中插入亂七八糟的假數據、或者修改數據庫表結構時,不就會影響到線上的數據了么?
老弟一拍手:對哦,那如何讓本地的測試不影響到線上項目呢?
這就需要 多環境 。根據實際需要,將同一個項目(或同一套代碼)按照一定方法進行區分,并將所需資源和項目本身部署到不同的機器上。不同環境的項目可以有 不同的行為 ,且能夠 同時存在、互不影響 。
舉個例子,可以給線上項目搭建一套開發環境,開發環境的數據存儲在獨立的開發數據庫,并且為了調試方便,不需要登錄也能夠訪問所有的用戶數據:
這樣一來,本地和線上的項目就完全隔離開了,開發者在本地想怎么折騰就怎么折騰!這便是多環境的好處。
常用環境
多環境聽起來雖然挺爽的,但事實上,環境不是區分的越多越好!
一方面是搭建多環境需要額外的工作量;另一方面是項目依賴的資源越多,成本就越高,而且維護起來也更麻煩。
因此,企業中常用的環境也就那么幾種,都快成為一種約定俗成的規范了,下面給大家介紹一下。
不同團隊區分環境的方式可能不同,僅供參考。
本地環境
一般用 local 標識,是指前端或后端獨立開發、自主測試的環境。通常就是讓項目和依賴在我們自己的電腦上運行,比如數據庫、緩存、隊列等各種服務,可能需要自己在本地搭建。
本地環境
開發環境
一般用 dev 標識,是指前端和后端(或者多個程序員)一起協作開發、聯調的環境。通常將項目和依賴放在員工電腦可以直接訪問的開發機上,不用自己搭建,直接跑起來項目,提高開發和協作效率。
對規模不大的團隊來說,開發和本地環境其實有一套就夠了,畢竟本地也可以連接公用的數據庫等服務。
開發環境
測試環境
一般用 test 標識,是指前端和后端開發和聯調完成,做出完整的新功能后,交給測試同學去找 Bug 的環境。
通常在測試環境需要有獨立的測試數據庫和其他服務,讓測試同學大顯身手。每次修改完 Bug 后,也都要再次發布項目到測試環境,讓測試同學重新驗證。
測試環境
預發布環境
一般用 pre 標識,這是和線上項目最接近的環境,一般是測試驗證通過、產品經理體驗過后,才能將項目發布到這個環境。
實際上,預發布環境的項目調用的后端接口、連接的數據庫、服務等都 和線上項目一致 ,和線上唯一的區別就是前端訪問的域名不同。
正因如此,預發布環境看到的都是真實的用戶數據,可以發現更多測試環境因為數據不足而沒查出來的 Bug。
預發布環境
生產環境
一般用 prod 標識,又叫線上環境,是給所有真實用戶使用的環境。
因此不能隨意修改,且發布項目到該環境時必須格外小心。線上的數據庫、機器等資源一般也是由專業的運維來負責,想要登錄機器、修改配置,都需要經過嚴格審批。
生產環境
如何實現?
最后再介紹下多環境的實現方式,其實大同小異,遵循 3 個步驟:抽象配置類 + 配置文件化 + 注入環境參數,就能輕松實現。
抽象配置類
將項目代碼中需要根據環境的變化而更改的變量整理到一個或多個配置類中,集中管理。
舉個例子,連接數據庫時,我們需要數據庫 IP、端口、配置等信息,代碼如下:
- // 數據庫基本信息
- DB db = new DB();
- db.setIp("10.0.0.1");
- db.setPort(3306);
- // 數據庫連接配置
- DBConnection c = new DBConncetion();
- c.setTimeout(1000);
我們可以將這些代碼中寫死的值全部替換成變量,將同類變量放到一個類中:
- // 數據庫配置類
- class DBConfig {
- String ip = "10.0.0.1";
- int port = 3306;
- long timeout = 1000L;
- }
然后從這個類中讀取變量的值:
- DB db = new DB();
- DBConfig cf= new DBConfig()
- // 從類中獲取
- db.setIp(cf.getIp());
- db.setPort(cf.getPort());
- DBConnection c = new DBConncetion();
- c.setTimeout(cf.getTimeout());
這樣的好處是,如果代碼中還有其他地方用到了這些變量,也都可以從同一個類去獲取,而不是把 死值 重復寫多次,難以維護。
配置文件化
我們可以用專門的配置文件來維護配置,從而讓用戶修改配置更方便,不用再去找代碼、改代碼。
常見的配置文件格式有 properties、yaml、yml、json 等,比如新建一個數據庫配置文件 db.properties :
- db.ip=10.0.0.1
- db.port=3306
- db.timeout=1000
接下來在初始化數據庫時,就可以將配置文件中的值加載到上一步寫好的配置類中,然后讀取啦:
- // 從文件讀取配置的值
- DBConfig cf = new DBConfig("db.properties");
- db.setIp(cf.getIp());
- db.setPort(cf.getPort());
- ...
其實只不過是把配置的值從代碼中移到了文件中而已。
但這樣一來,我們想加載哪個配置文件就能加載哪個!
比如要搞一套測試環境的配置,只需再新建一個 db-test.properties 文件(文件名中加個環境名稱),就能在這個文件中編寫獨立的配置了,然后在代碼中加載該文件即可:
- new DBConfig("db-test.properties");
無論是前端還是后端,大部分的多環境實現都是這個原理 —— 搞多套配置,所以總能在項目中看到類似的配置文件:
多環境配置文件
注入環境參數
到目前為止,其實我們還是在代碼中寫了 死值 ,來告訴程序應該加載哪個名稱的配置文件。
比如在本地開發時,加載 db-dev.properties ,開發完成后、正式上線前,再改代碼為加載 db-prod.properties。
但這樣不僅麻煩,而且可能忘了修改,把開發環境的項目發布到了線上。
最理想的效果應該是:無論項目要切換到哪個環境,整個項目都完全不用修改。
因此,我們可以將 指定環境 這件事放到最后,在通過命令去打包或者啟動項目時,將環境參數寫進去。
舉個例子,我們在啟動 java 項目時,給 env 系統變量傳遞不同參數:
- # 測試環境
- java -jar -Denv=test dist.jar
- # 生產環境
- java -jar -Dend=prod dist.jar
然后在程序中讀取該參數,加載對應的配置即可:
- // 讀取 env 參數
- String env = System.getProperty("env");
- new DBConfig("db-" + env + ".properties");
同理,對于前端項目,可以在打包構建時傳入環境變量,然后自己在代碼中讀取,或者交給 Webpack 之類的打包工具處理:
- {
- "scripts": {
- "serve": "env=dev serve",
- "build:test": "env=test build"
- "build": "env=prod build"
- }
- }