一. 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
CONFIG
/INFO
/TIME
SLAVEOF
SAVE
/BGSAVE
/LASTSAVE
SCRIPT
(不要与EVAL
/ 混淆EVALSHA
)SHUTDOWN
SLOWLOG
PUBSUB
(不是发布订阅,不要混淆PUBLISH
/SUBSCRIBE
)
那么如何使用它们呢?,因该是从服务器而不是数据库开始。像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产品中,使用该组件时,好像不能指定数据库序号。