浅谈C#在网络波动时防重复提交
????前幾天,公司數據庫出現了兩條相同的數據,而且時間相同(毫秒也相同)。排查原因,發現是網絡波動造成了重復提交。
????由于網絡波動而重復提交的例子也比較多:
????網絡上,防重復提交的方法也很多,使用redis鎖,代碼層面使用lock。
????但是,我沒有發現一個符合我心意的解決方案。因為網上的解決方案,第一次提交返回成功,第二次提交返回失敗。由于兩次返回信息不一致,一次成功一次失敗,我們不確定客戶端是以哪個返回信息為準,雖然我們希望客戶端以第一次返回成功的信息為準,但客戶端也可能以第二次失敗信息運行,這是一個不確定的結果。
在重復提交后,如果客戶端的接收到的信息都相同,都是成功,那客戶端就可以正常運行,就不會影響用戶體驗。
????我想到一個緩存類,來源于PetaPoco。
Cache<TKey,?TValue>代碼如下:
????Cache<TKey,?TValue>符合我的要求,第一次運行后,會將值緩存,第二次提交會返回第一次的值。
??? 但是,細細分析Cache<TKey, TValue> 類,可以發現有以下幾個缺點
?????????1、?不會自動清空緩存,適合一些key不多的數據,不適合做為網絡接口。
?????????2、?由于_lock.EnterWriteLock,多線程會變成并單線程,不適合做為網絡接口。
?????????3、?沒有過期緩存判斷。
????于是我對Cache<TKey,?TValue>進行改造。
AntiDupCache代碼如下:
代碼分析:
??????使用兩個ReaderWriterLockSlim鎖?+?一個AntiDupLockSlim鎖,實現并發功能。
????? Dictionary<TKey, Tuple<long, TValue>> _map實現緩存,long類型值記錄時間,實現緩存過期
??????int?_maxCount?+?Queue<TKey>?_queue,_queue?記錄key列隊,當數量大于_maxCount,清除多余緩存。
????? AntiDupLockSlim繼承ReaderWriterLockSlim,實現垃圾回收,
代碼使用?:
測試性能數據:
-----------------------?開始??從1到100???重復次數:1?單位:?ms?-----------------------
??????并發數量:?1????2????3????4????5????6????7????8????9????10???11???12
??????普通并發:?188??93???65???46???38???36???28???31???22???20???18???19
??AntiDupCache:?190??97???63???48???37???34???29???30???22???18???17???21
??AntiDupQueue:?188??95???63???46???37???33???30???25???21???19???17???21
?????DictCache:?185??96???64???47???38???33???28???29???22???19???17???21
?????????Cache:?185??186??186??188??188??188??184??179??180??184??184??176
第二次普通并發:?180??92???63???47???38???36???26???28???20???17???16???20
-----------------------?開始??從1到100???重復次數:2?單位:?ms?-----------------------
??????并發數量:?1????2????3????4????5????6????7????8????9????10???11???12
??????普通并發:?368??191??124??93???73???61???55???47???44???37???34???44
??AntiDupCache:?180??90???66???48???37???31???28???24???21???17???17???22
??AntiDupQueue:?181??93???65???46???39???31???27???23???21???19???18???19
?????DictCache:?176??97???61???46???38???30???31???23???21???18???18???22
?????????Cache:?183??187??186??182??186??185??184??177??181??177??176??177
第二次普通并發:?366??185??127??95???71???62???56???48???43???38???34???43
-----------------------?開始??從1到100???重復次數:4?單位:?ms?-----------------------
??????并發數量:?1????2????3????4????5????6????7????8????9????10???11???12
??????普通并發:?726??371??253??190??152??132??106??91???86???74???71???69
??AntiDupCache:?189??95???64???49???37???33???28???26???22???19???17???18
??AntiDupQueue:?184??97???65???51???39???35???28???24???21???18???17???17
?????DictCache:?182??95???64???45???39???34???29???23???21???18???18???16
?????????Cache:?170??181??180??184??182??183??181??181??176??179??179??178
第二次普通并發:?723??375??250??186??150??129??107??94???87???74???71???67
-----------------------?開始??從1到100???重復次數:12?單位:?ms?-----------------------
??????并發數量:?1????2????3????4????5????6????7????8????9????10???11???12
??????普通并發:?2170?1108?762??569??450??389??325??283??253??228??206??186
??AntiDupCache:?182??95???64???51???41???32???28???25???26???20???18???18
??AntiDupQueue:?189??93???67???44???37???35???29???30???27???22???20???17
?????DictCache:?184??97???59???50???38???29???27???26???24???19???18???17
?????????Cache:?174??189??181??184??184??177??182??180??176??176??180??179
第二次普通并發:?2190?1116?753??560??456??377??324??286??249??227??202??189
仿線上環境,性能測試數據:
-----------------------?仿線上環境??從1到1000??單位:?ms?-----------------------
??????并發數量:?1????2????3????4????5????6????7????8????9????10???11???12
??????普通并發:?1852?950??636??480??388??331??280??241??213??198??181??168
??AntiDupCache:?1844?949??633??481??382??320??267??239??210??195??174??170
??AntiDupQueue:?1835?929??628??479??386??318??272??241??208??194??174??166
?????DictCache:?1841?935??629??480??378??324??269??241??207??199??176??168
?????????Cache:?1832?1854?1851?1866?1858?1858?1832?1825?1801?1797?1788?1785
第二次普通并發:?1854?943??640??468??389??321??273??237??209??198??177??172
項目:
??????Github:https://github.com/toolgood/ToolGood.AntiDuplication
??????Nuget:?Install-Package?ToolGood.AntiDuplication
后記:
?????嘗試添加?一個Queue<AntiDupLockSlim>?或Stack<AntiDupLockSlim>?用來緩存鎖,后發現性能效率相差不大,上下浮動。
?????使用?lock關鍵字加鎖,速度相差不大,代碼看似更簡單,但隱藏了一個地雷:一般人使用唯一鍵都是使用string,就意味著可能使用lock(string),鎖定字符串尤其危險,因為字符串被公共語言運行庫?(CLR)“暫留”。?這意味著整個程序中任何給定字符串都只有一個實例,就是這同一個對象表示了所有運行的應用程序域的所有線程中的該文本。因此,只要在應用程序進程中的任何位置處具有相同內容的字符串上放置了鎖,就將鎖定應用程序中該字符串的所有實例。
原文地址:https://www.cnblogs.com/toolgood/p/10700828.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?
總結
以上是生活随笔為你收集整理的浅谈C#在网络波动时防重复提交的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在Windows上使用Docker运行.
- 下一篇: C#并行编程(4):基于任务的并行