靈巧無負擔的共享雲端代理

導讀

雲端代理是在日常抓包工具如Fiddler、Charles等應用受限情況下,自主思考、設計及研發的一套雲端共享代理工具,該工具日常應用過程中切實提高了團隊配合效率。


背景

軟件開發及軟件測試工作中,通常檢驗接口及返回數據的方法就是抓包,藉助抓包工具可以一目瞭然將信息展現在眼前。查閱和分析請求與返回數據、設置斷點修改模擬數據,修改DNS切換環境等等,幫助開發及測試人員快速完成接口調試,接口測試,頁面效果驗證工作。抓包工具Fiddler、Charles在團隊配合中必不可少,但在工作過程中,仍有如下強烈的困擾:

1、多人合作研發/測試的項目,環境配置及測試模擬數據無法快速共享;

2、單人同時支持不同需求及多機型適配/多場景驗證時,環境配置及測試數據無法快速切換/隔離;

3、實時請求中依據真實數據進行Mock配置時,斷點方式要求實時高,強依賴操作速度,Map Local方式又操作複雜;

4、請求流被清屏後,偶現問題沒有留存的數據可供還原現場;

5、非技術人員發佈前參與功能體驗,代理配置操作成本高,無法快速連接到測試環境。

回到需求本質,我們只要有一個代理,可以支持多人配置共享、多機不同配置隔離,並同時記錄所有請求數據即可解決上述困擾。考慮到代理工具選型及研發成本,能在現有工具上進行二次開發是比較好的選擇,所以著眼點也是從最經常使用的兩款抓包工具分析:

1、Fiddler,擁有一個強大擴展腳本CustomRules.js,可以解決配置問題,但它的侷限性就是不可跨平臺,僅適用於Windows用戶;

2、Charles,雖然可跨平臺使用,但沒有提供顯式可擴展腳本,對它改造的成本過重。

基於以上兩款常用工具所存在的問題,選擇藉助業內其他輕量開源工具進行二次開發,搭建雲端代理,並完善常用功能,將Mock&Host配置(後續若無特殊指明,“配置”即代表“Mock&Host配置”的簡稱)、數據統一化管理,解決上述問題。


項目架構

靈巧無負擔的共享雲端代理

項目從整體設計上分為圖示的五部分。服務層的代理服務是最核心功能,它完成請求的捕獲,並通過查詢Host及Mock配置實現請求區別轉發;捕獲的請求及配置管理提供可視化操作界面,並有相應數據庫獨立存儲。整體功能模塊可劃分為代理服務、配置數據隔離與共享、數據持久化、數據分析、監控,針對每一個功能與實現方案後續會分別講述。

1、代理服務器

代理服務器包含了代理服務及請求展示。市面上開源代理服務器已有很多成熟版本,基於其進行二次開發可以節省大量成本。現提供基於兩種不同工具進行二次開發的方案,兩種方案各有千秋,熟悉AnyProxy,Nodejs與React的讀者可以參考方案一,熟悉Mitmproxy與Python或其他Web開發語言的讀者可以參考方案二。

如下將詳細闡述兩種方案細節,供閱讀者各取所需。

方案一:

選用阿里開源代理工具AnyProxy進行二次開發,AnyProxy輕量且開源,已提供代理基礎功能和請求可視化查看界面,二次開發成本較低並能滿足預期。重構後的基礎架構如下圖:

靈巧無負擔的共享雲端代理

AnyProxy初始化啟動涉及代理服務,證書管理,界面等模塊,用戶使用時只需安裝好證書並將代理配置指向特定端口;代理服務模塊監聽到特定端口請求後,請求會順次通過requestHandler和recordHandler模塊進行處理、組裝,組裝完畢以WebSocket方式實時傳遞至前端界面顯示;前端界面提供根據URL進行請求的過濾展示。基於此,為滿足多人使用、配置隔離共享等功能,在界面增加了Mock與Host環境的常規配置入口及快速新增查看編輯入口;在服務層通過requestHandler集成了Mock與Host環境配置功能,並同時改造了recordHandler完善數據記錄,最終實現雲端代理完整功能。

由於AnyProxy最初定義是一個本地代理,代理雲端化後不再像本地代理一樣單人使用場景多,而是會捕獲所有團隊成員的請求。選定一個唯一標識用以區分不同請求用戶,是雲端化的先決條件,這也是實現不同用戶配置隔離與共享的基礎,在此選定了請求的客戶端IP地址作為唯一標識。

此代理捕獲的請求主要包含HTTP以及HTTPS兩種,AnyProxy監聽請求後,在requestHandler內提供方法通過解析Socket可獲取客戶端IP。但此方法針對HTTP的請求可獲取真實客戶端IP,HTTPS的請求得到的IP均為127.0.0.1,以至於所有的HTTPS請求都無法區分請求用戶。為解決此問題,從HTTP與HTTPS請求發起和代理原理的差異點進行分析:

靈巧無負擔的共享雲端代理

Proxy初始化僅啟動HTTP代理服務器,監聽特定端口請求。HTTP代理原理詳見圖示上半部分。當客戶端發起HTTP請求後,由於協議的明文傳輸性,HTTP代理接收並解析請求可直接獲取客戶端真實IP。

HTTPS代理原理詳見圖示下半部分。當客戶端發起HTTPS請求時,瀏覽器會先主動發出一個HTTP CONNECT請求建立連接,此請求仍由初始啟動的HTTP代理服務監聽。監聽到此類請求後,HTTP代理服務會佔用當前容器空閒端口啟動HTTPS代理服務,並通過隧道技術在真實客戶端與HTTPS代理間完成加密數據轉發。HTTPS代理的requestHandler內解析Socket獲取的客戶端實際是同容器上的HTTP代理服務,即解釋了為什麼HTTPS代理同方法取到的都是127.0.0.1的IP地址。

解決方案可採用:在隧道建立好後,從HTTP代理-HTTPS代理連接Socket中獲取雙端端口作為Key,從客戶端-HTTP代理連接Socket中獲取客戶端IP作為Value存入請求處理上下文中。CONNECT請求成功建立連接後,實際包含加密數據的GET/POST等請求被HTTP代理再捕獲後通過隧道傳遞至HTTPS代理,HTTPS代理的requestHanlder內解析加密請求時,使用與HTTP代理連接Socket的雙方端口組裝Key,從上下文中便可獲取真實客戶端IP,從而支持HTTPS請求下實現不同用戶的配置隔離。

解決了以上基本問題後,此代理已實現雲端代理配置隔離的基礎:區分不同用戶,用戶以設備IP做界定(即同一人若擁有多臺設備,會被判定為多個用戶)。在實際工作中,大量的工作場景為:一人多臺手機進行適配或團隊合作項目,每個設備間配置隔離當然不是預期的提效效果,配置共享功能應運而生--提供查詢全用戶配置的頁面,點擊單用戶配置後可依據該用戶當前配置生成一個二維碼,其他用戶通過掃碼方式可快速將配置拷貝給自己,無需重複錄入(針對此處功能,配置隔離與共享模塊的配置共享小節有更詳細的講述)。

此代理實現方案前臺界面默認會展示所有成員的請求,好處是數據透明,開發與測試可方便查看對方操作步驟,請求數據等,互相協助快速分析問題。但全量數據展示不方便使用者聚焦自身的請求數據或特定場景的抓包,針對此問題所做優化為:在AnyProxy自身具備根據請求URL進行請求流實時過濾功能的基礎上,擴展了根據客戶端IP及請求URL進行多重組合的實時過濾功能(此部分功能依據Redux框架思想進行開發,不再過多闡述),同時擴展將此過濾條件保存進緩存,後續再次使用,緩存過濾直接生效,無需重複配置。

方案二:

此方案在設計上各模塊的耦合度較低,代理服務及Web服務兩模塊可根據需要靈活選型。重構後的架構如下圖所示。

靈巧無負擔的共享雲端代理

Web服務主要負責與用戶交互,功能包括抓包請求流的展示、Mock&Host相關設置等,其中請求流實時展示功能通過Ajax請求定時從後端讀取新的記錄實現。Web服務與代理服務本身並沒有直接交互,通過數據庫連接,因此在選型上也比較自由,可以根據需要自由選擇,前後端分離方案或是傳統MVC模式都可以滿足需要。

代理服務器主要負責根據配置對數據流的各種修改工作。用戶/服務器信任根證書並設置代理,此時所有HTTP/HTTPS請求將經過代理服務器,代理服務器會作為中間人,將所有HTTP報文內容保存在數據庫中。對於代理服務器的選擇,目前有很多優秀的開源項目可以使用,筆者基於擴展性、二次開發成本、性能等因素的考慮,選擇了Mitmporxy。

此方案在改造成雲端代理後,支持以人或者IP的方式進行Mock及Host設置。一般情況下如果客戶端為靜態IP,如服務器等,可使用IP進行區分;如果為動態IP則更推薦以人進行區分。

針對IP區分方式,由於代理服務器在任意協議下都可以直接獲取到Client_IP,因此無需進行額外的開發。以IP的方式進行區分雖能夠滿足大部分應用場景,但同時也存在以下問題:

a)展示自己的抓包數據需要額外過濾操作。

b)多臺設備進行相同設置時需要重複操作。

c) IP變更後需要重新設置。

d) Mock及環境設置數據查找&維護不方便。

針對以上問題,提供了以人為維度的區分方式。對於以用戶進行區分主要存在以下困難:

首先,要解決的問題是抓包信息需要區分用戶,用戶A的請求不能在用戶B的頁面顯示,但是從代理的層面來說並不知道這個請求是哪個用戶發送過來的,代理能夠獲取到的只有Client_IP,即手機的IP,所以需要做的就是將IP和用戶唯一標識(如OA賬號)進行綁定。於是在Web服務中,接入了58OA系統用以標識唯一用戶,用戶&IP通過掃碼即可完成綁定。但這樣帶來另外一個問題:當用戶連上代理後,掃碼後發起的請求實際都由代理服務發出,在Web服務中獲取到的Client_IP都為代理服務器的地址,為了解決這個問題,可將代理服務器作為中間人,在請求中添加特定Header(Mock-RealIP),後端通過解析此Header便可獲取用戶真實IP從而將用戶及IP進行綁定。

用戶&IP通過掃碼完成綁定後,即便IP發生變化,重新掃碼後即可重新將用戶&IP進行綁定,對於Mock及Host設置的數據,則可以直接生效,無需重新配置。

此方案實現後,實時請求流數據默認僅顯示當前OA賬戶下的關聯IP請求,使得用戶更聚焦自己的請求。

最初在代理模式上計劃的方案是使用透明代理的方式,如下圖所示,將透明代理部署在智能路由器上,這樣用戶甚至不需要手動去設置代理地址,僅需要連上對應的Wifi熱點即可使用。但是由於智能路由器存儲空間及CPU性能問題,最終沒有實現,後來也嘗試過在樹莓派上搭建,在少量用戶情況下表現尚可,但在用戶達到一定數量後出現CPU使用率過高,響應速度慢的問題。最終還是先實現了基本功能,後期再對易用性進行優化。

靈巧無負擔的共享雲端代理

總結:上述兩個方案雖在代理選型及用戶區分上有所不同,但殊途同歸,都針對本地代理在工作中實際使用場景的不足進行了改進,為雲端代理功能實現做好鋪墊。

方案一偏重代理服務與界面的一體性,利用開源工具省去兩者交互層面開發工作。以終端設備IP做用戶劃分,無明確的用戶邊界,可查看任意終端設備的訪問數據;各終端設備配置獨立互不影響,靈活地支持單人多套配置的場景,單人/多人同配置的使用場景可通過配置共享掃碼快速實現。

方案二無需對代理層進行額外修改,代理服務及Web服務可自由定製。可根據不同場景選擇使用IP及賬號的方式進行用戶區分,更強調賬號區分方式,對他人的抓包及數據自動進行隔離,多臺設備無需重複設置即可共享配置,手機動態分配IP過期後無需重新設置。

閱讀者可依據自身使用場景,從兩套方案中取長補短,靈活組裝。

除上面介紹的基礎功能外,雲端代理服務器由於使用者多,流量大,為解決速度問題,採用分佈式部署代理的方式解決。


2、配置數據隔離與共享

配置數據主要包括Host配置與Mock配置,其配置管理方式基本一致,且同時適應IP/OA等任意用戶體系,使用時僅需用戶標識傳入該體系的唯一標識即可。

2.1)配置隔離:通過Setting頁面,對不同用戶配置進行單獨管理;代理服務捕獲請求進行轉發前,會根據不同用戶配置進行不同的修改,實現請求區別轉發,達到配置隔離的效果。

配置隔離實現的流程圖如下。

靈巧無負擔的共享雲端代理

代理使用用戶唯一標識依次獲取該用戶的Mock及Host配置,若沒有任何配置則直接轉發請求至真實服務器並返回。若判斷有響應Mock,直接使用配置數據構造響應返回至客戶端;若判斷有請求Mock,首先會修改請求的Url,Header,Body,再進行Host配置的判斷;若有Host配置,則修改Domain及Header後再進行請求轉發後再將獲取到的返回數據返回客戶端。

Mock和Host配置除上述總功能外,後續將分別介紹兩者在此流程中各自特殊的功能與實現方案。

a)Host配置

Host若判斷生效,由於雲端代理多人共享,實現請求轉發若通過改變dns路由實現會造成用戶間互相影響,由此採用另一套解決方案:修改HTTP請求的domain和header實現,具體為:將真實請求域名寫進HTTP Header,並將請求鏈接的域名替換為NginxIP。至此不同用戶請求同一域名可分別轉發至不同服務器。

為方便用戶快速確認請求轉發的服務器地址,在請求轉發服務方後,若非Host指定的服務方,會通過查詢真實DNS路由,將服務方真實地址做保存;否則,解析並保存Host配置裡的指定服務方地址;最終將保存的服務方地址展示在請求流實時界面,使用者即可快速確認轉發情況。

b)Mock配置

Mock規則配置支持正則和通配符兩種方式,統一轉換為正則存儲,這樣提高用戶靈活配置的同時,也保證了查詢時的匹配效率。

Mock配置方式結合用戶日常使用場景提供兩種:其一保留了日常代理工具常見的Map Remote方式,但進行了優化,在配置編輯頁面會顯示同接口他人的Mock配置,雙擊可快速進行配置複用;第二種將斷點配置與Mock做結合,在Proxy實時請求流界面上,每條請求都包含請求Mock和響應Mock添加按鈕。點擊按鈕後,分別將請求/響應數據回填至Mock編輯頁面,可依據真實數據進行配置的快速修改保存。

在Mock配置保存後,請求處理時確認生效,也會將Mock配置、狀態保存,並展示在請求流實時界面(此時單條請求的Mock添加按鈕會變為編輯按鈕),此功能除便於使用者確認配置生效狀態外,也提供了基於保存後的數據二次編輯功能。


2.2)配置共享:在配置隔離的基礎上,提供全用戶配置查詢頁面。輸入用戶唯一標識,可查詢到該用戶配置,點擊用戶配置,會依據該用戶唯一標識生成一個配置拷貝二維碼,其他任意用戶掃描二維碼即可將該用戶配置全部拷貝至自己配置內,完成配置快速共享。當用戶配置代理掃碼後,仍會遇到方案二內掃碼發起的請求,服務方不能獲取用戶真實IP問題,此處同樣採用在代理中將轉發請求的Header內注入真實IP解決方法。


3、數據持久化

抓包數據通常對應操作路徑,對於偶現問題的定位有比較大的參考價值,同時也是日常不斷積累的測試數據,對此,我們對請求流數據做了持久化存儲,存儲形式及用途分為如下兩種:

a)全量請求數據:當抓包工具清屏後,所有請求數據丟失,偶現問題無法還原現場,回放操作步驟。為解決此問題我們將所有抓包數據不做任何處理全量保存,提供根據數據產生者的IP、URL路徑、請求ID進行混合範圍查詢。

考慮到通常都是短期請求清屏後的數據還原,數據存儲量較小,在此我們選用輕量,速度快的嵌入式數據庫nedb存儲,數據總量始終保持在5w-6w條,每當大於6w條時,會將最早發生的1萬條數據進行清理。

b)真實有價值數據:全量數據中會混雜部分無效數據,對數據分析會造成一定干擾,因此我們對全量數據進行過濾後,在每天凌晨通過Job將過濾後的數據以版本或時間為維度進行分表存儲。並對外提供查詢接口,供其他使用方接入擴展功能,同時便於歷史問題追溯。


4、數據分析

此模塊相對獨立,且對實時性要求亦沒有那麼高,可以選擇了通過Job的方式定時對持久化的數據進行分析。而對報表的需求則應從業務層面進行設計,目前筆者已經在實際工作中使用的報表包括:

a)版本接口覆蓋率:此報表主要反映了當前APP版本功能覆蓋情況,對IOS、Android及總量分別計算當前版本中測試覆蓋的接口數量與近3個版本接口數量的比值,並列出未覆蓋接口的具體URL,方便使用者進行迴歸。覆蓋率可作為迴歸測試是否通過的一個量化標準。

靈巧無負擔的共享雲端代理

b)異常接口統計:主要針對測試過程中出現的4xx和5xx 的Response Code的請求URL,便於後期根據Log等定位問題。


5、監控

即便代理服務相對較為穩定,但監控仍是必不可少的一環。當前簡潔快速的方案是在代理啟動時將啟動及執行日誌寫入依據時間規範制定的Log文檔內,並在代理運行過程中通過crontab定時查詢進程,如果發現進程不存在則會在自動啟動代理服務的同時,保持日誌記錄規範,並向相關負責人發送通知,以便當問題出現時可依據啟動及運行日誌排查崩潰原因。


收益

共享雲端代理的實現,為團隊工作提效與質量保障都帶來較大收益,主要體現在以下方面:

1)使用成本降低:由於雲端性及可視化Web操作界面,應用場所更自由、可適用業務場景更多。只要處於公司內網環境下,打開手機瀏覽器也可配置Host、查看請求流詳情,調試及協助定位問題變得隨時隨地;並且僅首次連接代理需安裝證書,後續無任何操作即可使用,極大簡化非技術人員操作成本。

2)環境配置高效:在單人同時支持多個需求或單需求內模擬多場景測試時,可利用多個手機連接後代理分別配置,實現多套環境&場景共存,省去資源切換的消耗;當單人多機兼容測試或多人合作研發&測試時,單機配置後,通過掃碼方式也可快速多機共享配置。除此外,相比常見兩款代理,所有配置都會跟隨當次請求數據顯示在實時界面上,可快速確認當前Host,Mock狀態,節省環境&配置問題排查時間。

3)多維度的數據分析:目前在系統中已有200餘萬條有效數據,在28個版本中進行了使用,並在版本測試中多次發現功能遺漏迴歸的情況,避免線上問題。

4)多平臺對接:代理平臺的有價值數據,通過接口輸出到其他平臺,如對接接口文檔平臺,利用請求包含的輸入輸出,作為接口文檔Demo的完善;對接接口自動化Case平臺,補充Case。


總結

本文針對目前本地代理存在的問題,通過雲端代理的方式提供了一套針對測試時抓包、環境設置、Mock及數據分析的完整方案。在提供更便捷的服務的同時,也提升了開發同學在自測、UED&產品同學在驗收過程中工作效率。由於篇幅限制,一些實現細節並未在文中一一詳述,如果大家發現文章中的錯誤或者實現方案上有更好的選擇,歡迎在評論區留言交流指正。


參考文獻:

1. AnyProxy:https://github.com/alibaba/Anyproxy

2. Redux:https://redux.js.org/(中文翻譯教程:http://cn.redux.js.org/)

3. Mitmproxy:https://github.com/mitmproxy/mitmproxy


作者簡介:

沈暢:58集團 用戶價值增長部同城質量效率部

秦偲晟:58集團 安居客質量保障部工程效能團隊


分享到:


相關文章: