溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

.NET客戶端如何實(shí)現(xiàn)Redis中的管道與事物

發(fā)布時間:2021-07-12 09:54:51 來源:億速云 閱讀:136 作者:小新 欄目:開發(fā)技術(shù)

這篇文章主要為大家展示了“.NET客戶端如何實(shí)現(xiàn)Redis中的管道與事物”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“.NET客戶端如何實(shí)現(xiàn)Redis中的管道與事物”這篇文章吧。

序言

Redis中的管道(PipeLine)特性:簡述一下就是,Redis如何從客戶端一次發(fā)送多個命令,服務(wù)端到客戶端如何一次性響應(yīng)多個命令。

Redis使用的是客戶端-服務(wù)器模型和請求/響應(yīng)協(xié)議的TCP服務(wù)器,這就意味著一個請求要有以下步驟才能完成:1、客戶端向服務(wù)器發(fā)送查詢命令,然后通常以阻塞的方式等待服務(wù)器相應(yīng)。2、服務(wù)器處理查詢命令,并將相應(yīng)發(fā)送回客戶端。這樣便會通過網(wǎng)絡(luò)連接,如果是本地回環(huán)接口那么就能特別迅速的響應(yīng),但是如果走外網(wǎng),甚至外網(wǎng)再做一系列的層層轉(zhuǎn)發(fā),那就顯的格外蛋疼。無論網(wǎng)絡(luò)延時是多少,那么都將占用整體響應(yīng)的時間。這樣一來如果一次發(fā)送1個命令,網(wǎng)絡(luò)延時為100ms,我們不得不做。那么如果1次發(fā)1000個命令,那么網(wǎng)絡(luò)延時100*1000ms就很難容忍啦。

針對與上面的問題,Redis在2.6版本以后就都提供啦管道(Pipeline)功能。他可以使客戶端在沒有讀取舊的響應(yīng)時,處理新的請求。這樣便可以向服務(wù)器發(fā)送多個命令,而不必等待答復(fù),直到最后一個步驟中讀取答復(fù)。這被稱為管線(PipeLine),并且是幾十年來廣泛使用的技術(shù)。例如,許多POP3協(xié)議實(shí)現(xiàn)已經(jīng)支持此功能,大大加快了從服務(wù)器下載新電子郵件的過程。

那么事務(wù)這個詞匯,經(jīng)常遇到,就不多唧唧啦,目標(biāo)要一致就好,便是一組操作怎么做成原子性操作,使他去不了終點(diǎn),回到原點(diǎn)。

簡述wireshark抓包工具

為啦讓大家對管線有更形象的感觀,這一節(jié)我們先說說Wireshark抓包工具,他會讓你看到客戶端到服務(wù)器通過tcp協(xié)議發(fā)送的redis命令的過程與詳細(xì)。

wireshark能夠撲捉到系統(tǒng)發(fā)送和接受的每一個報文,我們這里只做一些過濾的簡述。下圖就是他的樣子,你打開后可以可以摸索下他的用法。

.NET客戶端如何實(shí)現(xiàn)Redis中的管道與事物

簡述幾個過濾規(guī)則:

1、ip過濾:目標(biāo)ip過濾:ip.dst==172.18.8.11,源ip地址過濾:ip.src==192.168.1.12;

2、端口過濾:tcp.port==80,這條規(guī)則是把源端口和目的端口為80的都過濾出來。使用tcp.dstport==80只過濾目的端口為80的,tcp.srcport==80只過濾源端口為80的包;

3、協(xié)議過濾:直接在fiter框中輸入?yún)f(xié)議名稱即可,如:http,tcp,udp,...

4、http模式過濾:過濾get包,http.request.method=="GET",過濾post包,http.request.method=="POST";

5、如果使用多條件過濾,則需要加連接符號,and。比如 ip.src==192.168.1.12 and http.request.method=="POST" and tcp.srcport==80

StackExchange.Redis實(shí)現(xiàn)Redis管線(Pipeline)

上兩張圖片管線便一目了然啦。

客戶端對redis服務(wù)器進(jìn)行多次請求的話,一般普通模式是這樣子的

.NET客戶端如何實(shí)現(xiàn)Redis中的管道與事物

客戶端對redis服務(wù)器進(jìn)行多次請求的話,管道模式是這樣子的

.NET客戶端如何實(shí)現(xiàn)Redis中的管道與事物

一般模式我們上代碼:

public static void GetNoPipelining()
  {
   for (var i = 0; i < 3; i++)
   {
    var key = "name:" + i;
    db.StringAppend(key, "張龍豪");
   }
  }

.NET客戶端如何實(shí)現(xiàn)Redis中的管道與事物

查看tcp請求報文的data

.NET客戶端如何實(shí)現(xiàn)Redis中的管道與事物

這樣你自己做的過程中,可以看到我圈起來的3個tcp請求的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();
  }

再來看下請求

.NET客戶端如何實(shí)現(xiàn)Redis中的管道與事物

這樣很明顯就能看出來是1個請求發(fā)送出來啦多個命令。那么我們不用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);

在接下來我們做一個簡單的性能比較。代碼如下:

static void Main(string[] args)
  {
   Stopwatch watch = new Stopwatch();
   Stopwatch watch2 = new Stopwatch();
   watch.Start();
   GetNoPipelining();
   Console.WriteLine("一般循環(huán)耗時:" + watch.ElapsedMilliseconds);
   watch.Stop();
   watch2.Start();
   GetPipelining();
   Console.WriteLine("Pipelining插入耗時:" + watch2.ElapsedMilliseconds);
   watch2.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é)果如下:

.NET客戶端如何實(shí)現(xiàn)Redis中的管道與事物

到此我還要說一下StackExchange.Redis的三種命令模式,其中使用2和3的模式發(fā)送命令,會默認(rèn)被封裝在管道中,不信的話,你可以做個小demo測試下:

1、sync:同步模式,會直接阻塞調(diào)用者,但不會阻塞其他線程。

2、async:異步模式,使用task模型封裝。

3、fire-and-forget:發(fā)送命令,然后完全不關(guān)心最終什么時候完成命令操作。在Fire-and-Forget模式下,所有命令都會立即得到返回值,該值都是該返回值類型的默認(rèn)值,比如操作返回類型是bool將會立即得到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)

這個看官方文檔,我只能說實(shí)現(xiàn)的很奇怪吧。我先描述下我的環(huán)境,就是準(zhǔn)備一個空redis庫,然后一步一步往下走,我們寫代碼看結(jié)果,來搞一搞這個事務(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ù)庫中結(jié)果如下,說明我們插入成功。

.NET客戶端如何實(shí)現(xiàn)Redis中的管道與事物

即:如果key為:zlh:1的list集合在索引0初的value!=zhanglonghao的話,我們從鏈表右側(cè)插入一條數(shù)據(jù)key為zlh:1value為zhanglonghao,成功。因?yàn)榈谝淮尾僮鳛榭諑臁?處確實(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ù)庫沒有增減數(shù)據(jù)。已久與上圖的數(shù)據(jù)保持一致。

原因分析:0處此時為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é)果如下,增長一條值為zhanglonghao1的數(shù)據(jù):

.NET客戶端如何實(shí)現(xiàn)Redis中的管道與事物

原因分析: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ù)庫數(shù)據(jù)已久與上面的保持一致,不增不減。

分析原因:Condition.ListIndexEqual("zlh:1",0,"zhanglonghao")為true,但是到下面的ListIndexNotEqual("zlh:1", 0, "zhanglonghao")為false。故整個事物的操作回滾,不予執(zhí)行,故數(shù)據(jù)庫沒有變化。

到此,我就不寫多余的代碼啦,但我要說幾個注意點(diǎn):

1、執(zhí)行命令的操作需為異步操作。

2、在事物中執(zhí)行的命令,都不會直接看到結(jié)果,故此結(jié)果也不能用于下面代碼做判斷,因?yàn)楫?dāng)前的異步命令在Execute()之前是不會對數(shù)據(jù)庫產(chǎn)生任何影響的。

以上是“.NET客戶端如何實(shí)現(xiàn)Redis中的管道與事物”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI