背景
客戶端同學(xué)與服務(wù)端同學(xué)進(jìn)行聯(lián)調(diào)時(shí),總是會(huì)遇到各種各樣的問題蚕冬。其中免猾,以服務(wù)端的線下環(huán)境不穩(wěn)定是最為頭疼,聯(lián)調(diào)過程中囤热,一會(huì)應(yīng)用要重啟了猎提,一會(huì)某個(gè)依賴的公共線下環(huán)境掛了,或者為了讓服務(wù)端增加某個(gè)字段需要等半天才行旁蔼。
這些直接導(dǎo)致的結(jié)果就是锨苏,聯(lián)調(diào)開發(fā)效率非常低下!陷入了各種相互等的循環(huán)造成了各種碎片的垃圾時(shí)間棺聊。
于是就想想辦法讓客戶端同學(xué)開發(fā)的時(shí)候盡量少依賴伞租,甚至不依賴服務(wù)端的運(yùn)行環(huán)境,這樣就可以完美高效開發(fā)了限佩。
原始方案
最開始的做法也是相當(dāng)普通的做法葵诈,就是把服務(wù)端的內(nèi)容結(jié)果json字符串以json文件的形式加入app安裝包中,app啟動(dòng)后會(huì)根據(jù)app當(dāng)前的網(wǎng)絡(luò)情況判斷是獲取本地的json文件中的內(nèi)容還是直接獲取真實(shí)接口內(nèi)容祟同。
這種做法其實(shí)已經(jīng)很大程度的解決了最核心問題作喘,如果是這個(gè)方案我不需要專門寫一篇文章來介紹。
有人吐槽
產(chǎn)品的不斷迭代其實(shí)就是一直被吐槽晕城,被吐槽說明被需要泞坦,有人用,只是覺得不爽砖顷。
有了原始方案后贰锁,客戶端同學(xué)們已經(jīng)大量從服務(wù)端依賴中解救出來主之,陷入了新的煩惱,原因是只要需要對(duì)返回結(jié)果做一些哪怕是一點(diǎn)點(diǎn)改動(dòng)李根,都需要重新編譯壓包槽奕,這是很惱火的事情。
我自己開發(fā)過程中也深刻體會(huì)到了這一點(diǎn)房轿,并也開始思考粤攒,怎么樣不重新編譯壓包就可以修改mock數(shù)據(jù)。
思考了幾個(gè)方案:
- 代理:使用Charles囱持,通過map功能將請(qǐng)求返回結(jié)果通過map到的本地文本文件返回夯接,就可以完美實(shí)現(xiàn)實(shí)時(shí)修改mock結(jié)果。但需要查看電腦ip纷妆,還要手機(jī)設(shè)置代理盔几,還要配置ssl證書等一堆破事,作為程序員總覺的還是要操作很多步驟非常不爽掩幢。
- 遠(yuǎn)程修改安裝包中的mock文件內(nèi)容:通過本機(jī)命令行操作模擬器和真機(jī)逊拍,讀寫app安裝包中的mock文件內(nèi)容來達(dá)到目的,這條路探索了一陣子沒有找到落地的方案际邻。
新的思路
那天的一件事突然激發(fā)了靈感芯丧,想出了目前最完美的方案。那天具體目的是啥忘了世曾,總之想要在本機(jī)開啟一個(gè)http靜態(tài)服務(wù)器缨恒,設(shè)置到任意一個(gè)本地目錄為root,把里面的內(nèi)容可以被別人下載訪問轮听,看了下mac自帶的Apache骗露,需要設(shè)置虛擬機(jī)等配置,并不是很方便血巍,沒法達(dá)到我最簡(jiǎn)單最純粹的目的萧锉。
最終還是探索出了一行命令在任意目錄開啟http服務(wù)的途徑,后面會(huì)詳細(xì)介紹藻茂。
由此突然想到驹暑,如果我把靜態(tài)服務(wù)器root置為項(xiàng)目目錄下的mock文件夾,再將本機(jī)ip讓app感知到辨赐,那在運(yùn)行的時(shí)候就可以將請(qǐng)求轉(zhuǎn)發(fā)給ip指向的機(jī)器优俘,來做到實(shí)時(shí)mock數(shù)據(jù)的功能!
全新設(shè)計(jì)方案
由此設(shè)計(jì)出一套不需要任何輔助app支援掀序,不需要手機(jī)設(shè)置代理帆焕,而且可以不用重新編譯就可以實(shí)時(shí)修改mock數(shù)據(jù)的網(wǎng)絡(luò)數(shù)據(jù)模擬方案。
我本身是開發(fā)iOS所以下面實(shí)施方案都以iOS為例,安卓上思路可以參考叶雹。
1. 讓app知道哪臺(tái)機(jī)器開啟了mock數(shù)據(jù)的http服務(wù)
說的明白一點(diǎn)就是讓編譯的app知道當(dāng)前壓包的機(jī)器是什么ip财饥,這個(gè)思路就很多了,我也是采用了最普通的做法,通過在編譯過程中插入腳本,讀取本機(jī)ip蒲跨,寫入當(dāng)前app的Info.plist文件。
ifconfig | grep "inet " | grep -Fv 127.0.0.1 | awk '{print $2}'
將獲得ip寫入Info.plist
plist="${PROJECT_DIR}/${INFOPLIST_FILE}"
key="XM_MOCK_IP"
value="192.168.2.101"
/usr/libexec/Plistbuddy -c "Delete :${key}" "${plist}"
/usr/libexec/Plistbuddy -c "Add :${key} string ${value}" "${plist}"
2. 在編譯機(jī)上工程項(xiàng)目Mock目錄下啟動(dòng)http靜態(tài)服務(wù)
腳本cd到Mock目錄下谦炒,執(zhí)行下面命令就可以。
lsof -ti:7777 | xargs kill
SCREEN -d -m python -m SimpleHTTPServer 7777
第一行命令清理一下占用7777端口的應(yīng)用风喇。
第二行命令可以以當(dāng)前執(zhí)行命令的目錄為root開啟一個(gè)7777端口的靜態(tài)服務(wù)器宁改,這句話試了目前主流的macOS版本都支持這樣啟動(dòng)。
ps. 解釋下Mock目錄魂莫,Mock目錄我的設(shè)計(jì)是目錄中存放的是所有以apiname.json為文件名的請(qǐng)求結(jié)果字符串文本文件还蹲。
這個(gè)目錄存放在項(xiàng)目的Resource目錄下,直接以物理目錄在Xcode下可見耙考。
目的是為了在開發(fā)過程中方便隨時(shí)新增和修改內(nèi)容谜喊,不需要再切換其他編輯器來修改。
3. 讓app發(fā)起網(wǎng)絡(luò)請(qǐng)求時(shí)轉(zhuǎn)發(fā)到mock服務(wù)器
這里沒什么代碼可以提供琳骡,大體就是app上得有個(gè)開關(guān)锅论,可以隨時(shí)開關(guān)app的mock和非mock狀態(tài),根據(jù)這個(gè)標(biāo)志位楣号,網(wǎng)絡(luò)請(qǐng)求生成的請(qǐng)求URL會(huì)有所不同:
在mock模式下
生成的請(qǐng)求url會(huì)變成http://192.168.2.101:7777/xmmock/xxx.json
網(wǎng)絡(luò)請(qǐng)求正常發(fā)出,或得到的內(nèi)容就是mock內(nèi)容怒坯,此時(shí)修改macOS里Xcode項(xiàng)目中mock文件夾下的json文件內(nèi)容就可以實(shí)時(shí)生效修改炫狱。
這里的xmmock/xxx.json
就是root目錄下的mock文件結(jié)構(gòu),可根據(jù)規(guī)則動(dòng)態(tài)生成剔猿。在非mock模式下
正常生成url請(qǐng)求
4. 局部數(shù)據(jù)模擬
這里還可以做一下容錯(cuò)视译,大體是如果mock模式下,請(qǐng)求http://192.168.2.101:7777/xmmock/xxx.json
后如果是非200归敬,說明這個(gè)mock文件不存在酷含,則繼續(xù)發(fā)送正常請(qǐng)求,以確保沒有在mock范圍內(nèi)的接口在app中還是可以正常訪問汪茧。
這樣就做到了app接口整體可用椅亚,只有在mock模式下,在mock范圍內(nèi)的接口會(huì)被mock舱污。
5. 清理尾巴
這里有點(diǎn)小尾巴呀舔,比如之前生成的在plist文件中加入了一個(gè)鍵值標(biāo)明當(dāng)前壓包的機(jī)器ip,這個(gè)鍵值在git中顯示是一個(gè)修改扩灯,這樣每個(gè)人修改這個(gè)代碼很容易沖突媚赖。
這個(gè)也很好辦霜瘪,在寫入編譯包后,編譯完成的環(huán)節(jié)再把這個(gè)鍵值從plist中自動(dòng)移除即可
plist="${PROJECT_DIR}/${INFOPLIST_FILE}"
/usr/libexec/Plistbuddy -c "Delete :XM_MOCK_IP" "${plist}"
全新的聯(lián)調(diào)流程
做到這樣以后惧磺,可以想象一下颖对,在項(xiàng)目進(jìn)入開發(fā)后,客戶端和服務(wù)端同學(xué)只要事先約定好接口各種情況數(shù)據(jù)返回的格式磨隘,客戶端就可以利用這套方案直接當(dāng)做服務(wù)端已經(jīng)提供接口那樣進(jìn)行開發(fā)缤底,當(dāng)客戶端通過本mock方案聯(lián)調(diào)自測(cè)各種情況通過后,再與服務(wù)端真實(shí)接口對(duì)接琳拭,成本可以說是非常的小训堆,完全消除的之前的相互等待的垃圾碎片時(shí)間。
目前我們團(tuán)隊(duì)的iOS組內(nèi)已經(jīng)使用這套方案成熟運(yùn)行了好幾個(gè)版本白嘁,開發(fā)效率提升明顯坑鱼。
附上完整腳本
將以下deploy腳本插入到"Build Phases"里盡可能前面的環(huán)節(jié),實(shí)際操作總我放在"Copy Bundle Resources"的前面絮缅。
#!/bin/sh
## 給plist賦值函數(shù)
setValueToPlist() {
plist="${PROJECT_DIR}/${INFOPLIST_FILE}"
key="$1"
value="$2"
/usr/libexec/Plistbuddy -c "Delete :${key}" "${plist}"
/usr/libexec/Plistbuddy -c "Add :${key} string ${value}" "${plist}"
echo "XMNetworkMock::在PLIST設(shè)定${key}值為${value}"
}
echo "XMNetworkMock::CONFIGURATION:${CONFIGURATION}"
echo "XMNetworkMock::PROJECT_DIR:${PROJECT_DIR}"
if [[ $CONFIGURATION == "Debug" ]]; then
## 獲取主機(jī)IP地址
ASB_HOST_IP=`ifconfig | grep "inet " | grep -Fv 127.0.0.1 | awk '{print $2}'`
setValueToPlist "XM_MOCK_IP" $ASB_HOST_IP
echo "XMNetworkMock::主機(jī)IP地址${ASB_HOST_IP}"
## 清理端口進(jìn)程
lsof -ti:7777 | xargs kill
echo "XMNetworkMock::端口占用清理完畢"
## 進(jìn)入目錄
ASB_DIR_MOCK_API="${PROJECT_DIR}/Resources/xmmock"
mkdir -p $ASB_DIR_MOCK_API
echo "XMNetworkMock::打開目錄${ASB_MOCK_API}"
## 開啟網(wǎng)絡(luò)服務(wù)
cd $ASB_DIR_MOCK_API
SCREEN -d -m python -m SimpleHTTPServer 7777
echo "XMNetworkMock::啟動(dòng)網(wǎng)絡(luò)服務(wù) ${ASB_DIR_MOCK_API}:7777"
else
echo "XMNetworkMock::檢測(cè)到非開發(fā)模式鲁沥,已清理調(diào)試選項(xiàng)ASC_HOST_IP的值"
fi
以下clean腳本放置在"Build Phases"的最后一個(gè)流程即可
#!/bin/sh
deleteValue() {
plist="${PROJECT_DIR}/${INFOPLIST_FILE}"
key="$1"
/usr/libexec/Plistbuddy -c "Delete :${key}" "${plist}"
}
if [[ $CONFIGURATION == "Debug" ]]; then
deleteValue "XM_MOCK_IP"
fi
ps. 注意腳本中相關(guān)目錄。