Monitoring Redis key events in .NET Core

Some time ago I wrote an article about Reloading the cache automatically once expired is ASP.NET MVC. The piece of code represented in that article is build on top of .NET Framework 4.7.2. It works fine in case you have a single instance of the application as System.Web.HttpRuntime.Cache relies on in memory caching and therefor it is bond to the same VM where application instance is running. This is fine as long as you are running only one application instance. Once horizontal scaling and increasing of instances kicks in due to high traffic, this approach hits the wall.

In case of scaled and distributed applications, in-memory caching is not that much of a use. In order to keep cache state consistency among the instances you have to switch to distributed type of caching. The first thing that comes into the scope is for sure Redis.

Redis is much more than a simple cache with automatic key expiry. Starting from version 2.8.0, Redis supports something called Redis Keyspace Notifications.

Switching on Redis Keyspace Notifications in Redis

By default Redis Keyspace Notifications feature is off. This means there will be no notifications sent to the subscribed clients on any key change.

Note

Redis notifications is working in fire and forget manner. This means that in case the client is for some reason disconnected once the notification is published, it will not receive notification upon the reconnecting. In simple words, notifications are not persisted or queued. Because of this it is not the most reliable to use them directly and you should consider some workarounds for more reliable delivery if required for your specific case.

To switch on the key namespace notifications in Redis you need to modify the redis.config file or through redis-cli using CONFIG SET command. Depending on which key events you want to create notifications for, the command parameter will defer

K     Keyspace events, published with __keyspace@<db>__ prefix.
E     Keyevent events, published with __keyevent@<db>__ prefix.
g     Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...
$     String commands
l     List commands
s     Set commands
h     Hash commands
z     Sorted set commands
x     Expired events (events generated every time a key expires)
e     Evicted events (events generated when a key is evicted for maxmemory)
A     Alias for g$lshzxe, so that the "AKE" string means all the events.

Since we want to achieve same functionality as in the article Reloading the cache automatically once expired is ASP.NET MVC but with distributed cache storage, we'll only create notifications for key expiry.

CONFIG SET notify-keyspace-events KEx

Once the command is executed, Redis will start sending notification to all subscribed clients when keys expire in the Redis database.

Subscribing to Redis notifications in .NET Core C# code

In Microsoft docuemntation related to Distributed caching in ASP.NET Core, NuGet package StackExchange.Redis is used, so we'll stick to it and uset it to subscribe to the key notifications of our Redis instance. For the simplicity reason, I used a simple .NET Core Console application to monitor key events in Redis.

To make things easier for testing on local, I spun up Redis instance in Docker 

docker run -d --net=host redis:latest

And just set the configuration to publish key expiry notifications

CONFIG SET notify-keyspace-events KEx

Now before we write any code, we need to reference the StackExchange.Redis NuGet package in .NET Core project.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="StackExchange.Redis" Version="2.0.601" />
  </ItemGroup>

</Project>
    

We are ready now to write the actual notification subscription code which will register a delegate to be invoked once notification is pushed from the Redis instance once key gets expired

using StackExchange.Redis;
using System;

namespace RedisTestsConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string EXPIRED_KEYS_CHANNEL = "__keyevent@0__:expired";
            String host = "192.168.80.128";

            ConnectionMultiplexer connection = ConnectionMultiplexer.Connect(host);
            ISubscriber subscriber = connection.GetSubscriber();
            subscriber.Subscribe(EXPIRED_KEYS_CHANNEL, (channel, key) =>
                {
                    Console.WriteLine($"EXPIRED: {key}");
                }
            );
            Console.WriteLine("Listening for events...");
            Console.ReadKey();
        }
    }
}
    

Note that that constant EXPIRED_KEYS_CHANNEL is set to __keyevent@0__:expired. This way subscriber is explicitly subscribed to EXPIRED key notification which is set in Redis config with KEx parameter value in CONFIG SET redis-cli command. We can also use __keyevent@0__:* to subscribe to all key events, for example if you set Redis to fire events for generic key notifications set with KEg parameter in Redis config.

Now to test this piece of code, I will add break point to a line in notification handler delegate and run the code from Visual Studio. Second thing is to insert Redis key with a dummy value and set it expiry to 3 seconds with SETEX command in redis-cli.

SETEX mykey 10 "Hello"

As soon as key mykey expires after 3 seconds, the breakpoint in the code will be hit.

References

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值