Maison > Article > développement back-end > Explication détaillée de l'implémentation client .NET de PipeLine et Transactions dans Redis
Cet article présente principalement les connaissances pertinentes du client .NET pour implémenter PipeLine et Transactions dans Redis. Il a une très bonne valeur de référence, jetons-y un œil avec l'éditeur ci-dessous
Préface
Fonctionnalités PipeLine dans Redis : Pour décrire brièvement, Redis Comment envoyer plusieurs commandes à la fois du client et comment répondre à plusieurs commandes à la fois du serveur au client.
Redis utilise le modèle client-serveur et le serveur TCP du protocole requête/réponse, ce qui signifie qu'une requête doit suivre les étapes suivantes pour être complétée : 1. Le client l'envoie à le serveur Interroge la commande puis attend une réponse du serveur, généralement de manière bloquante. 2. Le serveur traite la commande de requête et renvoie la réponse au client. De cette façon, il sera connecté via le réseau. S'il s'agit d'une interface de bouclage locale, il pourra répondre très rapidement. Cependant, s'il se dirige vers le réseau externe, voire effectue une série de transferts couche par couche sur le réseau. réseau externe, ce sera particulièrement pénible. Quel que soit le délai du réseau, il occupera le temps de réponse global. De cette façon, si une commande est envoyée à la fois, le délai du réseau est de 100 ms et nous devons le faire. Ainsi, si 1 000 commandes sont envoyées en même temps, le délai réseau de 100*1 000 ms sera difficile à tolérer.
En réponse aux problèmes ci-dessus, Redis fournit la fonction pipeline depuis la version 2.6. Il permet au client de traiter de nouvelles demandes sans lire l'ancienne réponse. Cela permet d'envoyer plusieurs commandes au serveur sans avoir à attendre une réponse jusqu'à ce que la réponse soit lue à la dernière étape. C'est ce qu'on appelle PipeLine et c'est une technologie largement utilisée depuis des décennies. Par exemple, de nombreuses implémentations du protocole POP3 prennent déjà en charge cette fonctionnalité, accélérant considérablement le processus de téléchargement de nouveaux e-mails depuis le serveur.
Ensuite, le mot transaction est souvent rencontré, il n'est donc pas nécessaire de trop insister. L'objectif doit être cohérent, c'est-à-dire comment transformer un ensemble d'opérations en opérations atomiques afin qu'il ne puisse pas atteindre la fin. point et retour au point de départ.
Une brève introduction à l'outil de capture de paquets Wireshark
Afin de donner à chacun une compréhension plus vivante du pipeline, dans cette section, nous parlerons d'abord de Wireshark outil de capture de paquets. Il vous permettra de voir le processus et les détails de la commande redis envoyée du client au serveur via le protocole TCP.
Wireshark peut capturer chaque message envoyé et reçu par le système. Nous donnerons ici seulement une brève description de certains filtrages. L'image ci-dessous montre à quoi elle ressemble. Après l'avoir ouverte, vous pouvez explorer son utilisation.
Décrivez brièvement plusieurs règles de filtrage :
1. Filtrage IP : filtrage IP cible : ip.dst==172.18.8.11, filtrage de l'adresse IP source : ip.src==192.168.1.12;
2. Filtrage de port : tcp.port==80. Cette règle filtre à la fois le port source et le port de destination de 80. Utilisez tcp.dstport==80 pour filtrer uniquement les paquets avec un port de destination de 80, et tcp.srcport==80 pour filtrer uniquement les paquets avec un port source de 80
3. nom du protocole dans la boîte de filtre. Disponible, tel que : http, tcp, udp,...
4. Filtrage en mode http : filtrer les paquets, http.request.method=="GET", filtrer la publication. paquets, http.request.method=="POST";
5. Si vous utilisez plusieurs conditions pour filtrer, vous devez ajouter le symbole de connexion, et. Par exemple, ip.src==192.168.1.12 et http.request.method=="POST" et tcp.srcport==80
StackExchange.Redis implémente le pipeline Redis (Pipeline)
Les deux pipelines d'images ci-dessus sont clairs en un coup d'œil.
Si le client fait plusieurs requêtes au serveur Redis, le mode normal ressemble à ceci
Si le client fait plusieurs requêtes au serveur Redis, le mode pipeline ressemble à ceci
Notre code pour le mode normal :
public static void GetNoPipelining() { for (var i = 0; i < 3; i++) { var key = "name:" + i; db.StringAppend(key, "张龙豪"); } }
Afficher les données du message de demande TCP
这样你自己做的过程中,可以看到我圈起来的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(); }
再来看下请求
这样很明显就能看出来是1个请求发送出来啦多个命令。那么我们不用createBatch()也是可以实现这样的效果的。
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 watch1 = new Stopwatch(); watch.Start(); GetNoPipelining(); Console.WriteLine("一般循环耗时:" + watch.ElapsedMilliseconds); watch.Stop(); watch1.Start(); GetPipelining(); Console.WriteLine("Pipelining插入耗时:" + 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(); }
结果如下:
到此我还要说一下StackExchange.Redis的三种命令模式,其中使用2和3的模式发送命令,会默认被封装在管道中,不信的话,你可以做个小demo测试下:
1、sync:同步模式,会直接阻塞调用者,但不会阻塞其他线程。
2、async:异步模式,使用task模型封装。
3、fire-and-forget:发送命令,然后完全不关心最终什么时候完成命令操作。在Fire-and-Forget模式下,所有命令都会立即得到返回值,该值都是该返回值类型的默认值,比如操作返回类型是bool将会立即得到false,因为false = default(bool)。
StackExchange.Redis实现Redis事务(Transactions)
这个看官方文档,我只能说实现的很奇怪吧。我先描述下我的环境,就是准备一个空redis库,然后一步一步往下走,我们写代码看结果,来搞一搞这个事务。
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(); }
执行结果为:true。数据库中结果如下,说明我们插入成功。
即:如果key为:zlh:1的list集合在索引0初的value!=zhanglonghao的话,我们从链表右侧插入一条数据key为zlh:1value为zhanglonghao,成功。因为第一次操作为空库。0处确实不为张龙豪。
数据不清空,继续上代码。
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(); }
结果为false,数据库没有增减数据。已久与上图的数据保持一致。
原因分析:0处此时为zhanglonghao,所以ListIndexNotEqual("zlh:1",0,"zhanglonghao")为假命题,直接回滚,不执行下面的插入命令。
数据不清空,继续上代码:
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(); }
结果为true,数据结果如下,增长一条值为zhanglonghao1的数据:
原因分析:ListIndexEqual("zlh:1",0,"zhanglonghao")为真命题,执行下面的操作,提交事物。
数据不删继续上代码:
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(); }
结果为false,数据库数据已久与上面的保持一致,不增不减。
分析原因:Condition.ListIndexEqual("zlh:1",0,"zhanglonghao")为true,但是到下面的ListIndexNotEqual("zlh:1", 0, "zhanglonghao")为false。故整个事物的操作回滚,不予执行,故数据库没有变化。
到此,我就不写多余的代码啦,但我要说几个注意点:
1、执行命令的操作需为异步操作。
2、在事物中执行的命令,都不会直接看到结果,故此结果也不能用于下面代码做判断,因为当前的异步命令在Execute()之前是不会对数据库产生任何影响的。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!