python單鏈路性能測(cè)試實(shí)踐
引言
在經(jīng)歷過(guò)一些嘗試之后,覺(jué)得在當(dāng)下的項(xiàng)目中運(yùn)用鏈路壓測(cè)的能力,不等著其他人了。
鏈路這個(gè)詞其實(shí)不如路徑通俗易懂,跟產(chǎn)品溝通這個(gè)比較有效率。具體的操作路徑,產(chǎn)品會(huì)給一份出來(lái),但是這都是基于UI
和產(chǎn)品思維的文檔,跟接口測(cè)試區(qū)別還是很大的,只能提供參考依據(jù)。
需要端上測(cè)試協(xié)作,有些業(yè)務(wù)細(xì)節(jié)還得端上測(cè)試同學(xué)幫忙補(bǔ)充一下。還需要運(yùn)維同事幫忙理一下各個(gè)接口的請(qǐng)求量比例,這次的比例我是依據(jù)靈光一現(xiàn)
寫(xiě)出來(lái),然后大家一起調(diào)整的。
本次由于比較初級(jí),所以這塊文檔就不寫(xiě)出來(lái)了,放一個(gè)圖來(lái)表達(dá)一下這個(gè)鏈路做了些什么,PS:我現(xiàn)在很喜歡用圖而不是文字,溝通效率太高了。推薦工具draw.io
,感興趣的可以參考文末的熱文中兩張架構(gòu)圖中的介紹。
資源庫(kù)1.4鏈路壓測(cè)方案
這次把登錄剔除了,因?yàn)樘?,?duì)測(cè)試結(jié)果影響比較大。
場(chǎng)景思路
場(chǎng)景
場(chǎng)景就是老師登錄,首先會(huì)請(qǐng)求一個(gè)知識(shí)點(diǎn)列表,然后通過(guò)知識(shí)點(diǎn)屬性篩選推薦課程列表,在對(duì)課程列表中的數(shù)據(jù)進(jìn)行收藏和取消收藏,在獲取自己當(dāng)前知識(shí)點(diǎn)下的課程列表(包含原創(chuàng)和收藏)。
思路
本次依然采取固定線程的壓測(cè)模型,本人預(yù)估線程200左右,測(cè)試用戶600備用,列表頁(yè)保證2頁(yè)數(shù)據(jù)。
每個(gè)線程綁定一個(gè)用戶,然后用戶開(kāi)始循環(huán)鏈路執(zhí)行步驟,執(zhí)行一次當(dāng)做一次Q
。單次Q
包含9次HTTP
接口請(qǐng)求(放棄了Socket
接口,以后有需求再添加Socket
接口到鏈路中),其中3次修改操作,6次查詢操作。
具體的邏輯通過(guò)內(nèi)部靜態(tài)類實(shí)現(xiàn),然后多一個(gè)K
類,用來(lái)存儲(chǔ)每次獲取的知識(shí)點(diǎn)屬性,方便調(diào)用。由于接口請(qǐng)求方法都是用基礎(chǔ)數(shù)據(jù)類型和String
作為參數(shù),所以調(diào)用時(shí)候會(huì)顯得有點(diǎn)啰嗦。但無(wú)傷大雅,腳本寫(xiě)出來(lái),本來(lái)就是用完就扔到倉(cāng)庫(kù)里面,改天再用再優(yōu)化。
Demo實(shí)現(xiàn)
package?com.okayqa.composer.performance.resource1_4 import?com.alibaba.fastjson.JSON import?com.alibaba.fastjson.JSONObject import?com.funtester.base.bean.AbstractBean import?com.funtester.base.constaint.ThreadLimitTimesCount import?com.funtester.frame.execute.Concurrent import?com.funtester.httpclient.ClientManage import?com.funtester.utils.ArgsUtil import?com.okayqa.composer.base.OkayBase import?com.okayqa.composer.function.Mirro import?com.okayqa.composer.function.OKClass class?Login_collect_uncollect?extends?OkayBase?{ ????public?static?void?main(String[]?args)?{ ????????ClientManage.init(10,?5,?0,?"",?0) ????????def?util?=?new?ArgsUtil(args) ????????def?thread?=?util.getIntOrdefault(0,?30) ????????def?times?=?util.getIntOrdefault(1,?40) ????????def?tasks?=?[] ????????thread.times?{ ????????????tasks?<<?new?FunTester(it,?times) ????????} ????????new?Concurrent(tasks,?"資源庫(kù)1.4登錄>查詢>收藏>取消收藏鏈路壓測(cè)").start() ????????allOver() ????} ????private?static?class?FunTester?extends?ThreadLimitTimesCount<Integer>?{ ????????OkayBase?base ????????def?mirro ????????def?clazz ????????FunTester(Integer?integer,?int?times)?{ ????????????super(integer,?times,?null) ????????} ????????@Override ????????void?before()?{ ????????????super.before() ????????????base?=?getBase(t) ????????????mirro?=?new?Mirro(base) ????????????clazz?=?new?OKClass(base) ????????} ????????@Override ????????protected?void?doing()?throws?Exception?{ ???????? ????????????def?klist?=?mirro.getKList()</code><code>????????????mirro.getKList() ????????????def?karray?=?klist.getJSONArray("data") ????????????K?ks ????????????karray.each?{ ????????????????JSONObject?parse?=?JSON.parse(JSON.toJSONString(it)) ????????????????if?(ks?==?null)?{ ????????????????????def?level?=?parse.getIntValue("node_level") ????????????????????def?type?=?parse.getIntValue("ktype") ????????????????????def?id?=?parse.getIntValue("id") ????????????????????ks?=?new?K(id,?type,?level) ????????????????} ????????????} ????????????JSONObject?response?=?clazz.recommend(ks.id,?ks.type,?ks.level)</code><code>????????????clazz.recommend(ks.id,?ks.type,?ks.level)????????????clazz.recommend(ks.id,?ks.type,?ks.level) ????????????def?minis?=?[] ????????????int?i?=?0 ????????????response.getJSONArray("data").each?{ ????????????????if?(i++?<?2)?{ ????????????????????JSONObject?parse?=?JSON.parse(JSON.toJSONString(it)) ????????????????????int?value?=?parse.getIntValue("minicourse_id") ????????????????????minis?<<?value ????????????????} ????????????} ????????????clazz.unCollect(random(minis)) ????????????mirro.getMiniCourseListV3(ks.id,?ks.type,?0,?ks.level)????????????mirro.getMiniCourseListV3(ks.id,?ks.type,?0,?ks.level) ????????} ????} ????private?static?class?K?extends?AbstractBean?{ ????????int?id ????????int?type ????????int?level ????????K(int?id,?int?type,?int?level)?{ ????????????this.id?=?id ????????????this.type?=?type ????????????this.level?=?level ????????} ????} }
其中AbstractBean
類是一個(gè)抽象類,用于一些bean
的方法封裝,就是為了省事兒。
package?com.funtester.base.bean import?com.alibaba.fastjson.JSON import?com.alibaba.fastjson.JSONObject import?com.funtester.frame.Save import?com.funtester.frame.SourceCode import?org.slf4j.Logger import?org.slf4j.LoggerFactory import?org.springframework.beans.BeanUtils /** ?*?bean的基類 ?*/ abstract?class?AbstractBean?{ ????static?final?Logger?logger?=?LoggerFactory.getLogger(AbstractBean.class) ????/** ?????*?將bean轉(zhuǎn)化為json,為了進(jìn)行數(shù)據(jù)處理和打印 ?????* ?????*?@return ?????*/ ????JSONObject?toJson()?{ ????????JSONObject.parseObject(JSONObject.toJSONString(this)) ????} ????/** ?????*?文本形式保存 ?????*/ ????def?save()?{ ????????Save.saveJson(this.toJson(),?this.getClass().toString()?+?SourceCode.getMark()); ????} ????/** ?????*?控制臺(tái)打印,使用WARN記錄,以便查看 ?????*/ ????def?print()?{ ????????logger.warn(this.getClass().toString()?+?":"?+?this.toString()); ????} ????def?initFrom(String?str)?{ ????????JSONObject.parseObject(str,?this.getClass()) ????} ????def?initFrom(Object?str)?{ ????????initFrom(JSON.toJSONString(str)) ????} ????def?copyFrom(AbstractBean?source)?{ ????????BeanUtils.copyProperties(source,?this) ????} ????def?copyTo(AbstractBean?target)?{ ????????BeanUtils.copyProperties(this,?target) ????} ????/** ?????*?這里bean的屬性必需是可以訪問(wèn)的,不然會(huì)返回空json串 ?????*?@return ?????*/ ????@Override ????String?toString()?{ ????????JSONObject.toJSONString(this) ????} ????@Override ????protected?Object?clone()?{ ????????initFrom(this) ????} }
控制臺(tái)輸出
~?~~?~~?~~?~~?~~?~~?~~?~~?~~?~ JSON ~?~~?~~?~~?~~?~~?~~?~~?~~?~~?~
> {
> ① . "rt":1665,
> ① . "total":1188,
> ① . "qps":18.018,
> ① . "failRate":0.0,
> ① . "threads":30,
> ① . "startTime":"2021-02-24 16:57:23",
> ① . "endTime":"2021-02-24 16:58:34",
> ① . "errorRate":1.01,
> ① . "executeTotal":1188,
> ① . "mark":"資源庫(kù)1.4登錄>查詢>收藏>取消收藏鏈路壓測(cè)241657",
> ① . "table":"eJzj5VLAD15sbXm2a8LTXZMN9Uyez9z9dO9Uu2fzl75Yv8ju2ZRtL6b32z3tn/ZsWweE83Lyvhfb1z/t6362tZvT2EChJKMoNaWYgA0KvFy8+F0RlFpckJ9XnKoQkpmbaqVQoVucWpSZmKOQV5qro1Cpm5uakpmYR8gOQq5QyM3MU4AYZWVhYqmQW6yTm1hhZWxoaQxkE9RNjA2UgEfTOoBo1JZRW2hmRSsQ0ccmsBW0tgnVQzS1DatVtLMRn3W0sPXRtCYgAlLtQITXWura/mhaMxCRYC+VXUGyv2nhmkfTGoGI0rCgrsseTWsBImLSIZ1dCA8sOjmMXNfCU9ZAupNYV8MdC4106qdA2vkAniAGq6OJ8AlVi6GB99FgSvTU8BU8egaBg6jsO3iWHwSuoZUPB4EzRn046sNRHw68M0Z9OOrDEe5DABkr1eo="
> }
~?~~?~~?~~?~~?~~?~~?~~?~~?~~?~ JSON ~?~~?~~?~~?~~?~~?~~?~~?~~?~~?~
以上就是python單鏈路性能測(cè)試實(shí)踐的詳細(xì)內(nèi)容,更多關(guān)于python單鏈路性能測(cè)試的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python列表倒序的幾種方法(切片、reverse()、reversed())
本文主要介紹了python列表倒序的幾種方法(切片、reverse()、reversed()),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08Python API 自動(dòng)化實(shí)戰(zhàn)詳解(純代碼)
今天小編就為大家分享一篇Python API 自動(dòng)化實(shí)戰(zhàn)詳解(純代碼),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-06-06Django實(shí)現(xiàn)內(nèi)容緩存實(shí)例方法
在本篇文章里小編給大家整理了關(guān)于Django實(shí)現(xiàn)內(nèi)容緩存實(shí)例方法,有需要的朋友們可以跟著學(xué)習(xí)下。2020-06-06Django 創(chuàng)建后臺(tái),配置sqlite3教程
今天小編就為大家分享一篇Django 創(chuàng)建后臺(tái),配置sqlite3教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11Python開(kāi)發(fā)之Nginx+uWSGI+virtualenv多項(xiàng)目部署教程
這篇文章主要介紹了Python系列之-Nginx+uWSGI+virtualenv多項(xiàng)目部署教程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-05-05Python使用Beautiful?Soup(BS4)庫(kù)解析HTML和XML
這篇文章介紹了Python使用Beautiful?Soup(BS4)庫(kù)解析HTML和XML的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06pycharm如何實(shí)現(xiàn)跨目錄調(diào)用文件
這篇文章主要介紹了pycharm如何實(shí)現(xiàn)跨目錄調(diào)用文件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02python3+PyQt5 自定義窗口部件--使用窗口部件樣式表的方法
今天小編就為大家分享一篇python3+PyQt5 自定義窗口部件--使用窗口部件樣式表的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-06-06