本文來自網易雲社區前端
做者:劉靜媛
java
前言
數據庫
從事金融業務領域的測試工做過程已經有半年時間了,在此期間遇到了各類類型的問題,我的認爲在平常測試工做中,特別是涉及資金的業務領域內,測試人員須要格外關注的方面有:系統的併發問題、冪等問題、性能問題及資損風險等。下面就是這段時間以來,在併發問題和冪等問題上站在測試角色上的一些積累和總結。編程
1、併發問題
後端
一、什麼是併發?設計模式
併發(Concurrency)是指系統在同一時間段內,同時執行多個計算程序。爲了更好的解釋併發的概念,引入並行(Parallelism)概念來進行對比分析。緩存
下圖較爲形象的給出二者的工做機制:安全
在多個線程同時工做時,單個CPU硬件環境下不可能真正同時的有多個線程在工做,此時併發機制其實是將CPU的運行時間劃分紅若干時間段,不一樣的時間段交由不一樣的線程使用,而其餘線程此時屬於掛起狀態。在多個CPU的硬件環境下才有可能實現多個線程真正的同時工做。服務器
二、併發與並行特色
網絡
併發 | 並行 |
宏觀層面同時執行 | 嚴格的同時執行 |
解決多個不相關的任務 | 通常解決同一個任務 |
資源和數據共享 | 資源和數據隔離 |
單、多處理器都可 | 必須多處理器 |
彼此須要交互通訊 | 不須要交互通訊 |
重點是組合協調 | 重點是執行 |
三、併發的優缺點
優勢:
(1)提高資源利用:這個優勢是最容易理解的,多線程併發能夠提高cpu的利用率,好比在一個線程等待磁盤IO資源時,CPU能夠去執行另外的任務。在任務量大的狀況下,併發機制可以明顯減小系統耗時。
(2)模型邊界清晰:可以併發的任務,應該是儘可能沒有依賴關係的,是相互獨立的,可以同時執行而不相互影響的。例如設計一個文件處理器,就可使用多個線程,每一個線程負責一個文件的讀取和操做,在多個線程同時工做時,對每一個文件的操做是相互獨立的。
(3)異步事件響應:在處理異步任務時,併發編程的優勢更是顯而易見的,它可以避免單線程狀況下資源閒置和過分等待。創建單獨的監聽器,在獲得異步響應時隨時處理響應,高效合理的利用系統資源。
缺點:
(1)安全風險:因爲同一進程下的多個線程會共享地址空間和數據,而多個線程訪問資源的順序具備不肯定性,所以在多線程併發時容易出現髒讀、幻讀等問題。(線程安全)
(2)活躍度風險(飢餓、死鎖、活鎖)
(3)性能風險:線程頻繁調度切換一樣會帶來資源浪費。
(4)分析、編程和測試複雜
4 、併發編程常見的應用場景
定時任務、分佈式任務、WS服務、Servlet、異步消息
多見於:多進程併發、集羣中機器之間併發
5 、併發安全措施
數據庫行級鎖:悲觀鎖,樂觀鎖
全局分佈式鎖
應用同步和鎖機制:——Synchronized\CAS;——java.util.concurrent.locks;——java.util.concurrent.atomic
使用線程安全的容器:java.util.concurrent
6 、測試角度看併發問題:
6 .1 難分析、難測試:
有些併發問題,是很難在平時測試過程當中發現的,特別是系統底層存在的併發問題,例如:
緩存更新設計不合理致使的髒數據問題——若是緩存更新的設計模式是先清除舊的緩存再更新數據庫,首先這個設計是不合理的,排除設計的正確性不談,若是在測試人員不瞭解緩存更新策略的狀況下,執行上層業務層面的併發測試,有很大的機率是在測試過程當中並無出現問題,可是仔細分析一下,在一個外部條件觸發了緩存更新的同時,有可能出現另外兩個併發操做:一個更新,一個查詢。在刪除緩存後,有一個查詢操做訪問緩存沒有命中,會去查數據庫,並把數據庫中老的數據放進緩存中,同時更新操做會更新數據庫。這樣致使緩存中的數據一直是老的數據,是髒數據而且會一直髒下去。而在現實測試過程當中,緩存的更新以及查詢操做都發生的很是之快,很難人爲造出相似或者更爲複雜的併發場景。
6 .2 難定位、難復現:
你們在開發測試過程中可能都會遇到的一個場景就是在屢次執行同一個操做時,不按期的會有一個操做是執行失敗了,很難找到失敗的規律,測試同窗提bug時也會很難再復現出bug並保留現場給開發人員排查。甚至此類狀況直接出如今了生產環境,用戶反饋說操做失敗,可是更難說清楚在什麼狀況下會失敗。這種問題發生的緣由多是多種多樣的,多是多線程設計的漏洞,也多是系統交互的異常狀況欠考慮引發的。當遇到這種狀況時,靠碰運氣和屢次嘗試是很笨拙的方法,關鍵仍是分析出可能存在的併發問題,再有針對的嘗試驗證,肯定是某種緣由並分析解決後,再進行併發嘗試,看是否還存在問題。
6 .3 預防和發現手段:
a.業務場景分析:實際應用場景下某功能是否會出現併發狀況,對於會出現併發狀況,重點關心代碼設計,並準備作併發測試。
b.靜態代碼分析:能夠藉助工具 Contemplate's ThreadSafe Solo,參考 http://www.contemplateltd.com/threadsafe
c.併發測試的注意事項:
(1)不放過偶發的錯誤和失敗
(2)作好測試分析,完成非併發的功能測試後,針對測試分析時分析到的容易出併發問題的部分作獨立的併發測試
(3)併發測試時,注意併發測試設置的併發線程數須要作到可調節,一般要大於CPU數量
2、冪等問題
一、冪等的概念
冪等來源於數學概念:單目運算中,x爲某集合中的任意數,f爲運算子,若是知足f(x)=f(f(x)),那麼f運算就是冪等的。
冪等性是系統接口的一種承諾,承諾只要調用接口成功,屢次相同的輸入會有相同的結果反饋和等同一次處理的影響力。聲明爲冪等的接口會認爲外部調用失敗是常態,而且失敗後必然會有重試。
二、引起冪等問題的常見緣由
用戶重複提交——很是容易發生,前端、後端均須要控制
網絡重發——容易遺漏,但有可能發生
消息重發——容易遺漏,但有可能發生
系統間重試——須要根據業務狀況來判斷是否須要重試,哪些狀況哪些系統須要重試
三、冪等問題的控制關鍵
在設計資金的系統中,冪等問題有着十分重要的地位,好比咱們定義一個接口withdraw
bool withdraw(accountId, amount)
withdraw的語義是從accountId對應的帳戶中扣除amount數額的錢;若是扣除成功則返回true,帳戶餘額減小amount;若是扣除失敗則返回false,帳戶餘額不變。
一種典型的狀況是withdraw請求已經被服務器端正確處理,但服務器端的返回結果因爲網絡等緣由被丟掉了,致使客戶端沒法得知處理結果。若是是在交互設計或者前端展現上處理的不夠好,用戶會覺得此次操做執行失敗,而後刷新頁面或者重複提交請求,這就致使了withdraw被調用兩次,帳戶也被多扣了一次錢。整個過程以下圖所示:
爲解決此問題,須要對接口進行冪等性改造,增長一個惟一ID參數,如:
bool idempotent_withdraw(uniqId,accountId,amount)
這個ID須要全局惟一的標識一次請求,客戶端的同一個業務請求只有一個uniqId,服務端在收到請求後去檢查一下是否已存在這個 uniqId而且執行成功了,若是執行成功就不會再處理第二次的調用請求,如此就避免了重複扣款的問題。
整體而言,在技術實現上,控制冪等問題的關鍵在於惟一鍵+狀態機。
首先,調用者在請求中攜帶一個惟一ID,這個ID惟一的標識一個工做單元,這個工做單元只容許被成功執行一次。
其次,接受者在收到請求,得到惟一ID時,要先去查詢這個ID所標識的工做單元是否被執行過。 檢查是否執行的邏輯一般是根據惟一請求ID ,在服務端查詢請求是否有記錄,是否有對應的響應信息,若是有,直接把響應信息查詢後返回;若是沒有,那麼就當作新請求去處理。
四、測試角度看冪等
(1)須要更多的關注業務性質和產品設計上,是否須要作到冪等,是時間維度的冪等仍是空間維度的冪等。
(2)接口的冪等測試必定不能遺漏,因爲冪等場景相對容易製造出來,冪等測試的難度遠遠小於併發測試,所以在作接口測試時不妨對每一個接口都思考一下是否須要冪等,須要的話就測試一下其冪等性
(3)業務場景,特別是涉及到資金流動的業務場景,對失敗重試機制必定要慎重。
網易雲大禮包:https://www.163yun.com/gift
本文來自網易雲社區,經做者劉靜媛受權發佈
相關文章:
【推薦】 SpringBoot入門(五)——自定義配置