如何更優雅地切換測試、正式環境?

初學者是怎麼做的?

小明一個剛入行安卓的小萌新,剛剛在測試小姐姐那裏交過學費(捱過罵)瞭解到軟件開發過程中是需要區分正式、測試環境的。但是他稍加思考就能想到測試、正式環境的區別僅僅是host不一樣而已,其他的比如接口名、參數名、返回的json格式均一模一樣。於是他馬上找到了解決方案,平時都用測試環境的,到上線的時候再換回正式環境不就可以了?在一次開發中需要請求三個不同接口,說幹就幹,於是小明就寫出了以下代碼,準備在上線時全局搜索www.test.com改成www.release.com,提交以後開開心心下班撩妹去了。

//請求接口1
NetWorkUtil.request("http://www.test.com?action=a1")

//請求接口2
NetWorkUtil.request("http://www.test.com?action=a2")

//請求接口3
NetWorkUtil.request("http://www.test.com?action=a3")

三天以後,產品經理跑過來說要加一個需求,新增了好幾個接口,小明表示自己表現的機會到了,一股腦兒全部包下來了。可是做着做着發現有點不對勁啊,每次請求接口的時候都需要複製http://www.test.com這個域名,可是產品錦鯉追的緊啊,沒辦法先就這麼辦吧,實現了需求上線以後再說。

一週過去了,上線時間到了。測試小姐姐又跑過來問小明這個正式包的數據怎麼不對,還是測試環境的?小明趕緊道歉,想起來了上線前需要修改域名這個事情,自己居然忘記了。心裏一萬隻草泥馬飛過,小明趕緊的匆匆忙忙地改完項目裏所有接口請求的地方,這才鬆一口氣,給測試小姐姐買了個奶茶打了個新包,這個版本終於成功上線了,還真是不容易啊。

一天以後,小明被項目經理叫到了辦公室,把小明狠狠批了一頓。原來是小明有個地方忘記改了,線上用戶的操作被記錄到了測試數據庫了。項目經理爲了解決這個問題,將最近一天測試環境的該數據全部導入到了正式環境才解決,當然還收到了不少的投訴。不過還好這個數據不是核心數據,不是那麼重要,不然小明的機票估摸着差不多就到手了。

小明痛定思痛,堅決要杜絕這種低級錯誤。於是他把需要改域名這個事情已經記錄到備忘錄裏每天提醒了,除此之外,聰明的他還想到了一個辦法,就是用一個全局的變量對域名進行保存,在上線前只需要切換一次就行了,類似於這種:

companion object {    
    const val HOST = "http://www.test.com"
}
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {    

    setContentView(R.layout.activity_main)

    //請求接口1
    NetWorkUtil.request("$HOST?action=a1")

    //請求接口2
    NetWorkUtil.request("$HOST?action=a2")

    //請求接口3    NetWorkUtil.request("$HOST?action=a3")
}

小明終於沒有犯線上的低級錯誤了,但是後面業務需求越來越繁雜,服務端使用的域名也越來越多,並且很多第三方的api比如推送、bugly監測等也都需要切換id。每次上線需要修改一堆的域名和id。小明每天心態都跟高考一樣,緊張又害怕,生怕自己再出問題,被強制送機票。而且就算在測試階段,測試小姐姐偶爾也會讓他打一個release包測試,雖然心裏千萬只草泥馬不願意,但是也沒辦法,先改成線上的域名吧,打完以後再改回來唄!於是小小明逐漸地熟悉了這一套切換方式,直到他看到了那一篇技術博客,小明他。。。哭了。

熟練開發者是怎麼做的

小明看到的文章正是一篇關於測試和正式環境切換的技術文章,該文通俗易懂,還提供了完整的方案,小明看完覺得這不就是爲自己準備的麼,於是按照文章裏的方式嘗試了起來。文章中說到可以根據當前app是debug還是release來切換host,大概實現如下,首先在Application的onCreate()中獲取到當前是否是debug模式,並且用靜態變量進行記錄,接下來需要區分測試、正式環境的時候就根據這個flag來判斷即可。

class MyApplication: Application() {

    companion object {
        var IS_DEBUG = true
    }

    override fun onCreate() {
        super.onCreate()
        IS_DEBUG =  (applicationInfo.flags != 0 && ApplicationInfo.FLAG_DEBUGGABLE != 0)
    }

}

小明收到了該文章的啓發,於是在項目所有需要區分測試、正式環境的地方都對上面的flag進行了判斷,其代碼大致如下:

companion object {

    val HOST1 = if(MyApplication.IS_DEBUG)"http://www.test1.com" else "http://www.release1.com"

    val HOST2 = if(MyApplication.IS_DEBUG)"http://www.test2.com" else "http://www.release2.com"

    val HOST3 = if(MyApplication.IS_DEBUG)"http://www.test3.com" else "http://www.release3.com"

}

到了這裏終於小明終於可以鬆口氣不用設置備忘錄,每次上線不用爲了改域名問題而提心吊膽了,域名會智能地根據當前是debug包還是release包還自動賦值。但是後面加在這裏的域名和第三方api越來越多,於是小明還在此基礎上舉一反三,進行了一波優化。小明瞭解到在系統打包的時候,如果在build.gradle文件中的buildTypes裏添加debug和release的相應配置,系統在build/generated/source/buildConfig目錄下會自動生成BuildConfig類,系統自動生成的類大概如下:

/**
 * Automatically generated file. DO NOT MODIFY
 */
package xx.xx.xx;

public final class BuildConfig {
  public static final boolean DEBUG = false;
  public static final String APPLICATION_ID = "xx.xx.xx";
  public static final String BUILD_TYPE = "debug";
  public static final String FLAVOR = "";
  public static final int VERSION_CODE = 1;
  public static final String VERSION_NAME = "1.0";
}

這裏面的字段是可以添加的,比如在build.gradle中設置好需要區分測試、正式環境的host,可以先按如下規則定義好

buildTypes {
    debug {
        buildConfigField "String", "URL", "\"www.test.com\""
    }
    release {
        buildConfigField "String", "URL", "\"www.release.com\""
    }
}

則在編譯的時候,系統會自動在BuildConfig中加入以下代碼

public static final boolean DEBUG = true;

public static final String URL= “www.test.com”;//如果是release包中會自動生成www.release.com

   我們可以看到,在實際開發的時候根本不需要去設置當前是哪個域名,而是系統會自動來判斷,從而在實際的業務需求開發時我們只需要使用BuildConfig.URL即可,小明將所有的域名以及第三方sdk需要的appkey都放到了buildTypes裏,於是小明的代碼可以改成這樣子了:
//請求接口1
NetWorkUtil.request("${BuildConfig.URL}?action=a1")

//請求接口2
NetWorkUtil.request("${BuildConfig.URL}?action=a2")

//請求接口3
NetWorkUtil.request("${BuildConfig.URL}?action=a3")

當然這裏URL最好封裝到Common層中,這裏就不多說,不是本文的重點。經過這一波的修改終於不用在每次上線時都修改URL了,而是系統會自動選擇好URL,我們直接使用就可以了。

大型項目是怎麼做的

按照上面的方式,一些小型的項目基本就沒問題了。但是用在大型項目裏問題就比較明顯了,我這裏隨便列舉幾個吧。

** 1.大項目的測試環境有可能不止一個,那麼具體用哪個還得做一個手動切換入口**

** 2.大廠的測試人員經常測試出指定response數據下的bug,開發人員拿到bug以後要連測試的代理,恢復現場才能復現,影響測試人員測試其他的bug,而且不方便查看抓包信息**

** 3.在日常的開發測試時安裝的是debug包,所以在上線以後,想看一下線上的效果又需要卸載重新安裝release包,等下個版本開發時又卸載線上的安裝debg版,這一波操作非常的麻煩**

   那麼,有沒有辦法可以解決以上的問題呢,答案當然是有的,這就是筆者今天要給大家重點介紹的host映射法。

什麼是IP地址

在此之前,首先給大家簡單介紹一下IP地址。大家肯定都知道,每臺在因特網的電腦都會有一個唯一的IP地址,IP地址相當於是你電腦的名字,其他電腦就通過IP地址來訪問你的地址,就相當於大家通過喊你的名字來將你和其他人進行區分一樣。

什麼是域名

接下來再給大家介紹一下域名,域名就相當於別人給你起的外號,別人嫌棄你的本名叫起來不順口,就喜歡叫你外號。做過服務端開發的同學肯定知道,就算沒有域名,只憑ip地址一樣可以訪問到服務器。但是如果讓你們訪問淘寶京東的時候,每次輸入xx.xx.xx.xx這種ip地址大家肯定不會喜歡的,也沒人能記得住,但是taobao.com就不一樣了,朗朗上口,過目不忘。

什麼是DNS解析

最後再給大家介紹一下DNS解析,這個名詞聽上去很高大上,但是其實理解起來特別簡單。DNS解析就是將域名和IP給匹配上的過程,不然誰知道你的外號是給誰起的,肯定會有公證人來統計這一一對應的信息。在我們訪問網址的時候,表現上是訪問的域名,但其實中間會經過一層dns解析,最終變成了訪問ip地址。

如上圖,例如小明想要訪問種子網站1,於是小明在瀏覽器裏輸入了www.zzwz1.com,接下來瀏覽器會拿着小明輸入的域名訪問DNS解析中心,詢問小明的網站ip是多少,詢問到了以後才能真正地訪問種子網站1,而不是錯誤地訪問到種子網站2。

知道了上面那些概念以後,有一個想法油然而生,那我是不是可以通過修改解析方式來區分測試環境和正式環境呢?如果可以的話,我們項目裏就讓它一直是www.release.com好了,在我需要測試的時候,我把這個域名解析到測試機器上,這樣不就訪問到了測試環境了麼?

如何手動修改hosts

事實證明這樣做是可以的,而且是合法正規渠道就可以做到了。以windows爲例,在c:\windows\System32\drivers\etc下面有一個hosts文件,該文件就是Windows官方提供用來修改DNS解析用的,凡是在該文件裏定義的解析方式將不再走入正規的解析流程。

我們打開該文件,Window也有一些對該文件的介紹,大概就是說我們可以通過寫入IP地址然後空格域名的方式指定該域名解析的IP地址,從而覆蓋其真正的主機。有過一些後端基礎的同學可能知道,爲了方便我們經常將127.0.0.1爲了方便從而替換成localhost,爲的就是在訪問本機網址的時候可以通過localhost的方式,寫起來更簡單一些。

所以我們需要做的就是在測試的時候修改這個hosts文件,比如測試環境下服務端的ip地址爲yy.yy.yy.yy我們只需要在hosts文件里加上以下一句話即可

yy.yy.yy.yy release.com

這樣一旦我們app訪問release.com的時候,本機就會將該域名指向IP爲yy.yy.yy.yy的電腦。

服務端需要做什麼呢?

服務端首先需要將測試環境和正式環境分別部署在不同的電腦上,其次正式環境和測試環境的域名都保證是同一個,這兩條在稍微正規一些的項目應該都是沒問題的。接下來服務端只需要告知客戶端所有測試服務端的ip地址,客戶端同學將所有的ip都保存到hosts裏面,在使用的時候相應地放開某些限制就可以了,做法如下圖:

在需要使用測試環境1時就放開相應地限制,讓該域名走向自己想要地服務端。如果想要切換回線上,也不需要修改任何客戶端代碼,甚至不需要重新打包,而是修改一下hosts將所有測試環境地限制全部取消即可,是不是非常地方便呢!

host太多怎麼辦

在複雜項目中,經常會出現多個域名的情況,這樣每次切換環境需要改的host條數太多,還真是有點麻煩,這裏可以通過一些專業切換host的工具來解決,比如switchhosts工具就非常好用,可以自定好平時工作中常用的幾套host組合,保存到switchhosts中,可以實現一鍵切換

手機是否可以切換

以上切換host是在電腦上實現的,所以還需要測試機和電腦連同一個網,並且使用charles、fiddler等代理工具對手機網絡進行代理。如果自己只需要修改而不需要抓包的話,同樣可以下載手機上的代理app,類似app有很多,比如知名的HostGo就很好用。

切換hosts方式的弊端

1.對服務端要求更高

2.開發時不小心hosts忘記切換的話會把數據傳入到正式環境中,或者反之

3.和服務端溝通成本較高

三種方式對比

操作複雜性:第一種方式由於需要複製粘貼進行替換,所以還是有一些工作量的,第二種要看當前項目是否有多個測試環境,如果有的話則也需要測試人員去手動切換,如果沒有的話就不需要任何操作,第三種方式由於切換的時候需要修改host,也是需要操作的。

容錯性:第一種方式有可能會替換錯誤,所以容錯性差,其他兩種基本都不會有問題

規範性:規範性是從代碼質量的方面來看,第一種方式是一直修改老代碼,明顯是低分,第二種方式存在需要切換不同測試環境的可能性,需要在debug模式下加一些只用來測試的和功能無關的代碼。

溝通成本:第二種方式在和測試聯調的時候,可能會更費勁一些,比如具體是哪個接口,還要把response數據發來本地maplocal才能復現問題。而切換host的方式,也需要和測試人員溝通當前復現的測試環境是哪個,並且需要和服務端溝通獲取到測試環境的IP地址。如果測試人員測試出了接口bug,只需要把其host文件發來替換本地的host文件就可以復現問題了。

拓展性:拓展性是從功能的強大程度來對比,全局替換的方式,每新增一個host就需要多替換一個,到後面就越來越重,第二種的話由於在新增測試環境方面表現欠佳,每次新增一個測試環境就需要修改客戶端代碼來提供切換入口,所以也不能給高分。

是否需要重新安裝:在測試的時候的apk包放到線上時,由於接口需要替換,所以肯定需要重新打包,但是使用切換host的方式,如果測試的時候就是用的release包,那麼可以直接上線,就算是debug包,也一樣把hosts文件裏的dns限制註釋掉就可以訪問線上數據的。

總結

本文筆者以工作中必用的測試、正式環境切換爲題,介紹了不同經歷的同學分別是怎麼實現的,最後重點介紹了筆者推薦的切換Host的方式。大家項目裏用第二種還是第三種根據項目情況而定,如果是小項目的話用第二種就足以,但是第三種大家也還是要能看懂,除此之外,DNS也是大廠在面試的時候喜歡問的一個點。最後多謝同學們耐心地看完了文章,對裏面內容有疑義或者有不懂的地方歡迎一起探討,共同進步!

推薦閱讀:騰訊技術團隊整理,萬字長文輕鬆徹底入門 Flutter,秒變大前端

重磅來襲!阿里P7“青春修煉手冊”(全網獨家首發!)
GitHub標星6.7K+!史上最全AndroidDevTools(牆都不扶,我就服你!)
作者:蘋果味的少年
鏈接:https://juejin.im/post/5eabae5ce51d454dc93ddf06

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章