.NET客戶端實(shí)現(xiàn)Redis中的管道(PipeLine)與事物(Transactions)
序言
Redis中的管道(PipeLine)特性:簡(jiǎn)述一下就是,Redis如何從客戶端一次發(fā)送多個(gè)命令,服務(wù)端到客戶端如何一次性響應(yīng)多個(gè)命令。
Redis使用的是客戶端-服務(wù)器模型和請(qǐng)求/響應(yīng)協(xié)議的TCP服務(wù)器,這就意味著一個(gè)請(qǐng)求要有以下步驟才能完成:1、客戶端向服務(wù)器發(fā)送查詢命令,然后通常以阻塞的方式等待服務(wù)器相應(yīng)。2、服務(wù)器處理查詢命令,并將相應(yīng)發(fā)送回客戶端。這樣便會(huì)通過(guò)網(wǎng)絡(luò)連接,如果是本地回環(huán)接口那么就能特別迅速的響應(yīng),但是如果走外網(wǎng),甚至外網(wǎng)再做一系列的層層轉(zhuǎn)發(fā),那就顯的格外難受。無(wú)論網(wǎng)絡(luò)延時(shí)是多少,那么都將占用整體響應(yīng)的時(shí)間。這樣一來(lái)如果一次發(fā)送1個(gè)命令,網(wǎng)絡(luò)延時(shí)為100ms,我們不得不做。那么如果1次發(fā)1000個(gè)命令,那么網(wǎng)絡(luò)延時(shí)100*1000ms就很難容忍啦。
針對(duì)與上面的問(wèn)題,Redis在2.6版本以后就都提供啦管道(Pipeline)功能。他可以使客戶端在沒(méi)有讀取舊的響應(yīng)時(shí),處理新的請(qǐng)求。這樣便可以向服務(wù)器發(fā)送多個(gè)命令,而不必等待答復(fù),直到最后一個(gè)步驟中讀取答復(fù)。這被稱(chēng)為管線(PipeLine),并且是幾十年來(lái)廣泛使用的技術(shù)。例如,許多POP3協(xié)議實(shí)現(xiàn)已經(jīng)支持此功能,大大加快了從服務(wù)器下載新電子郵件的過(guò)程。
那么事務(wù)這個(gè)詞匯,經(jīng)常遇到,就不多唧唧啦,目標(biāo)要一致就好,便是一組操作怎么做成原子性操作,使他去不了終點(diǎn),回到原點(diǎn)。
簡(jiǎn)述wireshark抓包工具
為啦讓大家對(duì)管線有更形象的感觀,這一節(jié)我們先說(shuō)說(shuō)Wireshark抓包工具,他會(huì)讓你看到客戶端到服務(wù)器通過(guò)tcp協(xié)議發(fā)送的redis命令的過(guò)程與詳細(xì)。
wireshark能夠撲捉到系統(tǒng)發(fā)送和接受的每一個(gè)報(bào)文,我們這里只做一些過(guò)濾的簡(jiǎn)述。下圖就是他的樣子,你打開(kāi)后可以可以摸索下他的用法。
簡(jiǎn)述幾個(gè)過(guò)濾規(guī)則:
1、ip過(guò)濾:目標(biāo)ip過(guò)濾:ip.dst==172.18.8.11,源ip地址過(guò)濾:ip.src==192.168.1.12;
2、端口過(guò)濾:tcp.port==80,這條規(guī)則是把源端口和目的端口為80的都過(guò)濾出來(lái)。使用tcp.dstport==80只過(guò)濾目的端口為80的,tcp.srcport==80只過(guò)濾源端口為80的包;
3、協(xié)議過(guò)濾:直接在fiter框中輸入?yún)f(xié)議名稱(chēng)即可,如:http,tcp,udp,...
4、http模式過(guò)濾:過(guò)濾get包,http.request.method=="GET",過(guò)濾post包,http.request.method=="POST";
5、如果使用多條件過(guò)濾,則需要加連接符號(hào),and。比如 ip.src==192.168.1.12 and http.request.method=="POST" and tcp.srcport==80
StackExchange.Redis實(shí)現(xiàn)Redis管線(Pipeline)
上兩張圖片管線便一目了然啦。
客戶端對(duì)redis服務(wù)器進(jìn)行多次請(qǐng)求的話,一般普通模式是這樣子的
客戶端對(duì)redis服務(wù)器進(jìn)行多次請(qǐng)求的話,管道模式是這樣子的
一般模式我們上代碼:
public static void GetNoPipelining() { for (var i = 0; i < 3; i++) { var key = "name:" + i; db.StringAppend(key, "張龍豪"); } }
查看tcp請(qǐng)求報(bào)文的data
這樣你自己做的過(guò)程中,可以看到我圈起來(lái)的3個(gè)tcp請(qǐng)求的key分別為name:0,name:1,name:2這樣子。
那么我們使用管道模式
public static void GetPipelining() { var batch = db.CreateBatch(); for (int i = 0; i < 3; i++) { var key = "mename:" + i; batch.StringAppendAsync(key, "張龍豪"); } batch.Execute(); }
再來(lái)看下請(qǐng)求
這樣很明顯就能看出來(lái)是1個(gè)請(qǐng)求發(fā)送出來(lái)啦多個(gè)命令。那么我們不用createBatch()也是可以實(shí)現(xiàn)這樣的效果的。
var a = db.StringAppendAsync("zlh:1", "zhanglonghao1"); var b = db.StringAppendAsync("zlh:2", "zhanglonghao2"); var c = db.StringAppendAsync("zlh:3", "zhanglonghao3"); var aa = db.Wait(a); var bb = db.Wait(a); var cc = db.Wait(a);
在接下來(lái)我們做一個(gè)簡(jiǎn)單的性能比較。代碼如下:
static void Main(string[] args) { Stopwatch watch = new Stopwatch(); Stopwatch watch1 = new Stopwatch(); watch.Start(); GetNoPipelining(); Console.WriteLine("一般循環(huán)耗時(shí):" + watch.ElapsedMilliseconds); watch.Stop(); watch1.Start(); GetPipelining(); Console.WriteLine("Pipelining插入耗時(shí):" + watch1.ElapsedMilliseconds); watch1.Stop(); Console.ReadLine(); } public static void GetNoPipelining() { for (var i = 0; i < 5000; i++) { var key = "name:" + i; db.StringAppend(key, "張龍豪"); } } public static void GetPipelining() { var batch = db.CreateBatch(); for (int i = 0; i < 5000; i++) { var key = "mename:" + i; batch.StringAppendAsync(key, "張龍豪"); } batch.Execute(); }
結(jié)果如下:
到此我還要說(shuō)一下StackExchange.Redis的三種命令模式,其中使用2和3的模式發(fā)送命令,會(huì)默認(rèn)被封裝在管道中,不信的話,你可以做個(gè)小demo測(cè)試下:
1、sync:同步模式,會(huì)直接阻塞調(diào)用者,但不會(huì)阻塞其他線程。
2、async:異步模式,使用task模型封裝。
3、fire-and-forget:發(fā)送命令,然后完全不關(guān)心最終什么時(shí)候完成命令操作。在Fire-and-Forget模式下,所有命令都會(huì)立即得到返回值,該值都是該返回值類(lèi)型的默認(rèn)值,比如操作返回類(lèi)型是bool將會(huì)立即得到false,因?yàn)閒alse = default(bool)。
此節(jié)參考redis官方文檔與StackExchange.Redis官方文檔,連接如下:
https://redis.io/topics/pipelining
https://github.com/StackExchange/StackExchange.Redis/blob/master/Docs/PipelinesMultiplexers.md
StackExchange.Redis實(shí)現(xiàn)Redis事務(wù)(Transactions)
這個(gè)看官方文檔,我只能說(shuō)實(shí)現(xiàn)的很奇怪吧。我先描述下我的環(huán)境,就是準(zhǔn)備一個(gè)空redis庫(kù),然后一步一步往下走,我們寫(xiě)代碼看結(jié)果,來(lái)搞一搞這個(gè)事務(wù)。
static void Main(string[] args) { var tran = db.CreateTransaction(); tran.AddCondition(Condition.ListIndexNotEqual("zlh:1",0,"zhanglonghao")); tran.ListRightPushAsync("zlh:1", "zhanglonghao"); bool committed = tran.Execute(); Console.WriteLine(committed); Console.ReadLine(); }
執(zhí)行結(jié)果為:true。數(shù)據(jù)庫(kù)中結(jié)果如下,說(shuō)明我們插入成功。
即:如果key為:zlh:1的list集合在索引0初的value!=zhanglonghao的話,我們從鏈表右側(cè)插入一條數(shù)據(jù)key為zlh:1value為zhanglonghao,成功。因?yàn)榈谝淮尾僮鳛榭諑?kù)。0處確實(shí)不為張龍豪。
數(shù)據(jù)不清空,繼續(xù)上代碼。
static void Main(string[] args) { var tran = db.CreateTransaction(); tran.AddCondition(Condition.ListIndexNotEqual("zlh:1",0,"zhanglonghao")); tran.ListRightPushAsync("zlh:1", "zhanglonghao1"); bool committed = tran.Execute(); Console.WriteLine(committed); Console.ReadLine(); }
結(jié)果為false,數(shù)據(jù)庫(kù)沒(méi)有增減數(shù)據(jù)。已久與上圖的數(shù)據(jù)保持一致。
原因分析:0處此時(shí)為zhanglonghao,所以ListIndexNotEqual("zlh:1",0,"zhanglonghao")為假命題,直接回滾,不執(zhí)行下面的插入命令。
數(shù)據(jù)不清空,繼續(xù)上代碼:
static void Main(string[] args) { var tran = db.CreateTransaction(); tran.AddCondition(Condition.ListIndexEqual("zlh:1",0,"zhanglonghao")); tran.ListRightPushAsync("zlh:1", "zhanglonghao1"); bool committed = tran.Execute(); Console.WriteLine(committed); Console.ReadLine(); }
結(jié)果為true,數(shù)據(jù)結(jié)果如下,增長(zhǎng)一條值為zhanglonghao1的數(shù)據(jù):
原因分析:ListIndexEqual("zlh:1",0,"zhanglonghao")為真命題,執(zhí)行下面的操作,提交事物。
數(shù)據(jù)不刪繼續(xù)上代碼:
static void Main(string[] args) { var tran = db.CreateTransaction(); tran.AddCondition(Condition.ListIndexEqual("zlh:1",0,"zhanglonghao")); tran.ListRightPushAsync("zlh:1", "zhanglonghao2"); tran.AddCondition(Condition.ListIndexNotEqual("zlh:1", 0, "zhanglonghao")); tran.ListRightPushAsync("zlh:1", "zhanglonghao3"); bool committed = tran.Execute(); Console.WriteLine(committed); Console.ReadLine(); }
結(jié)果為false,數(shù)據(jù)庫(kù)數(shù)據(jù)已久與上面的保持一致,不增不減。
分析原因:Condition.ListIndexEqual("zlh:1",0,"zhanglonghao")為true,但是到下面的ListIndexNotEqual("zlh:1", 0, "zhanglonghao")為false。故整個(gè)事物的操作回滾,不予執(zhí)行,故數(shù)據(jù)庫(kù)沒(méi)有變化。
到此,我就不寫(xiě)多余的代碼啦,但我要說(shuō)幾個(gè)注意點(diǎn):
1、執(zhí)行命令的操作需為異步操作。
2、在事物中執(zhí)行的命令,都不會(huì)直接看到結(jié)果,故此結(jié)果也不能用于下面代碼做判斷,因?yàn)楫?dāng)前的異步命令在Execute()之前是不會(huì)對(duì)數(shù)據(jù)庫(kù)產(chǎn)生任何影響的。
3、參考文檔:https://github.com/StackExchange/StackExchange.Redis/blob/master/Docs/Transactions.md
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
- 詳解redis大幅性能提升之使用管道(PipeLine)和批量(Batch)操作
- Redis基礎(chǔ)學(xué)習(xí)之管道機(jī)制詳析
- Python redis操作實(shí)例分析【連接、管道、發(fā)布和訂閱等】
- SpringBoot整合Redis管道的示例代碼
- redis批量操作pipeline管道操作方法
- Redis內(nèi)存碎片產(chǎn)生原因及Pipeline管道原理解析
- Springboot下使用Redis管道(pipeline)進(jìn)行批量操作
- Redis中pipeline(管道)的實(shí)現(xiàn)示例
- Redis中管道操作的項(xiàng)目實(shí)踐
相關(guān)文章
基于.Net的單點(diǎn)登錄(SSO)實(shí)現(xiàn)解決方案
SSO的解決方案很多,但搜索結(jié)果令人大失所望,大部分是相互轉(zhuǎn)載,并且描述的也是走馬觀花,本文對(duì)此進(jìn)行詳細(xì)介紹,需要了解的朋友可以參考下2012-11-11詳解免費(fèi)開(kāi)源的DotNet二維碼操作組件ThoughtWorks.QRCode(.NET組件介紹之四)
本篇文章主要介紹了免費(fèi)開(kāi)源的DotNet二維碼操作組件ThoughtWorks.QRCode,非常具有實(shí)用價(jià)值,有興趣的同學(xué)可以來(lái)了解一下。2016-12-12如何使用ASP.NET創(chuàng)建網(wǎng)站并設(shè)計(jì)web頁(yè)面
這篇文章主要介紹了如何使用ASP.NET創(chuàng)建網(wǎng)站,幫助大家更好的理解和學(xué)習(xí)使用ASP.NET技術(shù),感興趣的朋友可以了解下2021-04-04ASP.NET 運(yùn)行時(shí)錯(cuò)誤: 沒(méi)有為擴(kuò)展名“.asax”注冊(cè)的生成提供程序修正版
ASP.NET 運(yùn)行時(shí)錯(cuò)誤: 沒(méi)有為擴(kuò)展名“.asax”注冊(cè)的生成提供程序。可以在 machine.config 或 web.config 中的 <compilation><buildProviders> 節(jié)注冊(cè)一個(gè)。2009-01-01讓Asp.NET的DataGrid可排序、可選擇、可分頁(yè)
讓Asp.NET的DataGrid可排序、可選擇、可分頁(yè)...2007-02-02如何使用ASP.NET MiniAPI 調(diào)試未匹配請(qǐng)求路徑
ASP.NET MiniAPI是一個(gè)輕量級(jí)的Web API框架,它可以讓我們快速地構(gòu)建和部署RESTful服務(wù),本文給大家介紹使用ASP.NET MiniAPI 調(diào)試未匹配請(qǐng)求路徑的方法,感興趣的朋友一起看看吧2024-01-01ASP.NET實(shí)現(xiàn)QQ、微信、新浪微博OAuth2.0授權(quán)登錄
本文主要介紹了QQ、微信、新浪微博OAuth2.0授權(quán)登錄的示例,主要就是GET、POST遠(yuǎn)程接口,返回相應(yīng)的數(shù)據(jù),這里列出相關(guān)的代碼,供大家參考。2016-03-03ASP.NET web.config中 數(shù)據(jù)庫(kù)連接字符串加密解密
本文主要介紹利用aspnet_regiis.exe工具對(duì)web.config中connectionStrings節(jié)點(diǎn)進(jìn)行加密和解密的過(guò)程,希望對(duì)大家有所幫助。2016-05-05ASP.NET?Core獲取正確查詢字符串參數(shù)示例
這篇文章主要為大家介紹了ASP.NET?Core正確獲取查詢字符串參數(shù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05