StackExchange.Redis 客户端

一. Pipelining管道

  许多redis客户允许您使用管道,是将多条消息通过管道发送的过程,而无需等待每个消息的回复,并且(通常)在稍后收到回复时对其进行处理。在.net中通过awit  async来实现。

  例如:要使用过程阻塞代码对这两个get进行管道传输

var aPending = db.StringGetAsync("a");
var bPending = db.StringGetAsync("b");
var a = db.Wait(aPending);
var b = db.Wait(bPending);

  使用管道使我们能够立即将两个请求都发送到网络上,从而消除了大部分延迟。管道还有助于减少数据包碎片:单独发送20个请求(等待每个响应)将至少需要20个数据包,但是在管道中发送的20个请求可以只需要更少的数据包(甚至可能只要一个)。

 StackExchange.Redis有3种主要使用机制:

  同步Sync ,同步模式会直接阻塞调用者,但是显然不会阻塞其他线程。

  异步Async, 异步模式直接走的是Task模型,管道就是使用这种。

string value = "abcdefg";
await db.StringSetAsync("mykey", value);

  即用即弃Fire-and-Forget, 就是发送命令,然后完全不关心最终什么时候完成命令操作。所有命令都会立即得到返回值,比如操作返回类型是bool将会立即得到false。Int64将始终返回0

  db.StringIncrement(pageKey, flags: CommandFlags.FireAndForget);

 

二.多路复用

  管道虽很好,但是通常一个代码块都只需要一个值,这意味着我们仍然存在这样一个问题:我们大部分时间都在等待数据在客户机和服务器之间传输。如果您有20个并行应用程序请求都需要数据,则可以考虑拆分20个连接,或者可以同步对单个连接的访问​​(这意味着最后一个调用者将需要等待其他19个延迟甚至都没有开始)。通过多路复用单个连接来有效利用所有这些空闲时间,需要做很多工作。当由不同的调用者同时使用时,它会自动对单独的请求进行管道传输,因此无论请求使用阻塞访问还是异步访问,工作都将通过管道进行。

    public static ConnectionMultiplexer Manager
    {
        get
        {
            if (_redis == null)
            {
                lock (_locker)
                {
                    if (_redis != null) return _redis;

                    _redis = GetManager();
                    return _redis;
                }
            }

            return _redis;
        }
    }

    private static ConnectionMultiplexer GetManager(string connectionString = null)
    {
        if (string.IsNullOrEmpty(connectionString))
        {
            connectionString = GetDefaultConnectionString();
        }

        return ConnectionMultiplexer.Connect(connectionString);
    }

     IDatabase db = redis.GetDatabase();

参考资料:

https://stackexchange.github.io/StackExchange.Redis/PipelinesMultiplexers

 

三.发布订阅

  当使用发布/订阅API,有二种消息订阅方式,一是顺序处理,二是并发处理。

  顺序处理它们意味着您不必担心(非常多)线程安全性,并且意味着您保留了事件的顺序-事件的处理顺序与接收事件的顺序完全相同(通过队列) )-但其结果是,这意味着消息可能会彼此延迟。

multiplexer.GetSubscriber().SubScribe("messages", (channel, message) => {
    Console.WriteLine((string)message);
});

  另一个选择是并发处理。这不能保证工作的顺序,并且代码完全负责确保并发消息不会破坏您的内部状态,但是它可以更快,更可扩展。如果消息通常是不相关的,这特别好用。

var channelMessageQueue = multiplexer.GetSubscriber().SubScribe("messages");
channel.OnMessage(message =>
{
    Console.WriteLine((string)message.Message);
});

  

 1  #region Redis发布订阅
 2         /// <summary>
 3         /// Redis发布订阅  订阅
 4         /// </summary>
 5         /// <param name="subChannel"></param>
 6         void RedisSub(string subChannel);
 7         /// <summary>
 8         /// Redis发布订阅  发布
 9         /// </summary>
10         /// <typeparam name="T"></typeparam>
11         /// <param name="channel"></param>
12         /// <param name="msg"></param>
13         /// <returns></returns>
14         long RedisPub<T>(string channel, T msg);
15         /// <summary>
16         /// Redis发布订阅  取消订阅
17         /// </summary>
18         /// <param name="channel"></param>
19         void Unsubscribe(string channel);
20         /// <summary>
21         /// Redis发布订阅  取消全部订阅
22         /// </summary>
23         void UnsubscribeAll();
24 
25         #endregion

    

 1  #region Redis发布订阅
 2         /// <summary>
 3         /// Redis发布订阅  订阅
 4         /// </summary>
 5         /// <param name="subChannel"></param>
 6         public void RedisSub(string subChannel)
 7         {
 8             sub.Subscribe(subChannel, (channel, message) =>
 9             {
10                 Console.WriteLine((string)message);
11             });
12         }
13         /// <summary>
14         /// Redis发布订阅  发布
15         /// </summary>
16         /// <typeparam name="T"></typeparam>
17         /// <param name="channel"></param>
18         /// <param name="msg"></param>
19         /// <returns></returns>
20         public long RedisPub<T>(string channel, T msg)
21         {
22 
23             return sub.Publish(channel, SerializeContent(msg));
24         }
25         /// <summary>
26         /// Redis发布订阅  取消订阅
27         /// </summary>
28         /// <param name="channel"></param>
29         public void Unsubscribe(string channel)
30         {
31             sub.Unsubscribe(channel);
32         }
33         /// <summary>
34         /// Redis发布订阅  取消全部订阅
35         /// </summary>
36         public void UnsubscribeAll()
37         {
38             sub.UnsubscribeAll();
39         }
40         #endregion


四.redis服务器维护
  通过多路复用,可以获取到数据库IDatabase api ,能够进行基本数据库操作。但像keys或scan方法是没有的,由于StackExchange.Redis的目标是针对集群等场景,因此了解哪些命令针对数据库(可以分布在多个节点上的逻辑数据库)以及哪些命令针对服务器非常重要以下命令均以单个服务器为目标:

  • KEYS / SCAN
  • FLUSHDB / FLUSHALL
  • RANDOMKEY
  • CLIENT
  • CLUSTER
  • CONFIGINFO/TIME
  • SLAVEOF
  • SAVEBGSAVE/LASTSAVE
  • SCRIPT(不要与EVAL混淆EVALSHA
  • SHUTDOWN
  • SLOWLOG
  • PUBSUB(不是发布订阅,不要混淆PUBLISHSUBSCRIBE

   那么如何使用它们呢?,因该是从服务器而不是数据库开始。像KEYS / SCAN会扫描整个服务器整个key,所以应该尽量避免在生产服务器上使用,因为会适成服务器阻塞,一定要用也是针对slave从库。

  如何操作指定的服务器呢,因为生产环境一般是集群的。可以使用conn.GetEndPoints()列出端点.

ConnectionMultiplexer redis = ConnectionMultiplexer("localhost:6379,localhost:6380");
foreach (var endpoint in redis.GetEndPoints(false))
{
  Console.WriteLine(endpoint.ToString());
}


//将输出
$127.0.0.1:6379
Unspecified/localhost:6379
Unspecified/localhost:6380
127.0.0.1:6380
127.0.0.1:6381
127.0.0.1:6382
127.0.0.1:6383
127.0.0.1:6384

   GetEndPoints(false)将返回DNS名称和解析的IP地址的行. 调用GetServer()以查找所需的服务器(例如,选择一个从属服务器)。

 在阿里云redis产品中,使用该组件时,好像不能指定数据库序号。

 

posted on 2022-12-27 11:00  花阴偷移  阅读(25)  评论(0编辑  收藏  举报

导航