Redis 键空间通知 keyspace notification
使用redis持久化数据时,当内存资源比较紧缺时,会导致部分使用不太频繁的redis数据丢失,这种情况下,需要监控redis的数据,以便在redis数据意外丢失的情况下,及时补救。另外还有一种应用场景是倒计时功能,例如淘宝的还剩几天自动确认收货,在这种情况下,需要程序在某个倒计时的时机自动触发。下面介绍的redis 键空间通知(keyspace notifications)的功能可以完成此类功能
键空间通知使得客户端可以通过订阅频道或模式, 来接收那些以某种方式改动了 Redis 数据集的事件,例如redis的缓存过期事件,缓存移除事件等等,因为开启键空间通知功能需要消耗一些 CPU , 所以在默认配置下, 该功能处于关闭状态,可以通过修改 redis.conf 文件, 或者直接使用 CONFIG SET 命令来开启或关闭键空间通知功能:
- 当 notify-keyspace-events 选项的参数为空字符串时,功能关闭
- 另一方面,当参数不是空字符串时,功能开启
这里为了演示,我开启了所有通知,在生产环境下,只需要开启需要的通知即可,命令如下:
CONFIG SET notify-keyspace-events KEA
然后是订阅客户端代码:
using System;
namespace StackExchange.Redis
{
static class RedisKeyspaceNotifications
{
/// <summary>
/// NOTE: For this sample to work, you need to go to the Azure Portal and configure keyspace notifications with "Kxge$" to
/// 1) turn on expiration notifications (x),
/// 2) general command notices (g) and
/// 3) Evicted events (e).
/// 4) STRING operations ($).
/// IMPORTANT - MAKE SURE YOU UNDERSTAND THE PERFORMANCE IMPACT OF TURNING ON KEYSPACE NOTIFICATIONS BEFORE PROCEEDING
/// See http://redis.io/topics/notifications for more details
public static void NotificationsExample(ConnectionMultiplexer connection)
{
var subscriber = connection.GetSubscriber();
int db = 0; //what Redis DB do you want notifications on?
string notificationChannel = "__keyspace@" + db + "__:*";
//you only have to do this once, then your callback will be invoked.
subscriber.Subscribe(notificationChannel, (channel, notificationType) =>
{
// IS YOUR CALLBACK NOT GETTING CALLED????
// -> See comments above about enabling keyspace notifications on your redis instance
var key = GetKey(channel);
switch (notificationType) // use "Kxge" keyspace notification options to enable all of the below...
{
case "expire": // requires the "Kg" keyspace notification options to be enabled
Console.WriteLine("Expiration Set for Key: " + key);
break;
case "expired": // requires the "Kx" keyspace notification options to be enabled
Console.WriteLine("Key EXPIRED: " + key);
break;
case "rename_from": // requires the "Kg" keyspace notification option to be enabled
Console.WriteLine("Key RENAME(From): " + key);
break;
case "rename_to": // requires the "Kg" keyspace notification option to be enabled
Console.WriteLine("Key RENAME(To): " + key);
break;
case "del": // requires the "Kg" keyspace notification option to be enabled
Console.WriteLine("KEY DELETED: " + key);
break;
case "evicted": // requires the "Ke" keyspace notification option to be enabled
Console.WriteLine("KEY EVICTED: " + key);
break;
case "set": // requires the "K$" keyspace notification option to be enabled for STRING operations
Console.WriteLine("KEY SET: " + key);
break;
default:
Console.WriteLine("Unhandled notificationType: " + notificationType);
break;
}
});
Console.WriteLine("Subscribed to notifications...");
// setup for delete notification example
connection.GetDatabase(db).StringSet("DeleteExample", "Anything");
// key rename callbacks example
connection.GetDatabase(db).StringSet("{RenameExample}From", "Anything");
connection.GetDatabase(db).KeyRename("{RenameExample}From", "{RenameExample}To");
var random = new Random();
//add some keys that will expire to test the above callback configured above.
for (int i = 0; i < 10; i++)
{
var expiry = TimeSpan.FromSeconds(random.Next(2, 10));
connection.GetDatabase(db).StringSet("foo" + i, "bar", expiry);
}
// should result in a delete notification callback.
connection.GetDatabase(db).KeyDelete("DeleteExample");
}
private static string GetKey(string channel)
{
var index = channel.IndexOf(':');
if (index >= 0 && index < channel.Length - 1)
return channel.Substring(index + 1);
//we didn't find the delimeter, so just return the whole thing
return channel;
}
}
}
以上代码需要引用 StackExchange.Redis
延伸阅读: