在 windows azure 中使用 Redis 实现进程外缓存 [英] implementing out-of-process cache using Redis in windows azure

查看:27
本文介绍了在 windows azure 中使用 Redis 实现进程外缓存的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在开发一个网页,该网页显示了我在蔚蓝云中拥有的数据库中的表格.为了减少直接调用数据库以提高性能,我想为页面构建一个缓存.目前,我为表的读取持有一个内存缓存(进程内).现在我想创建一个进程外缓存,应该从 写入 开始更新,这意味着插入或更新(因为在更新或添加值后,内存缓存将不再有效).

I've been working on a webpage that displays a table from a database I have in my azure cloud. In order to reduce calls to the DB directly for performance improvement I would like to build a cache for the page. Currently, I hold an in-memory cache (in-process) for the reads of the table. Now I would like to make an out-of-process cache, that should be updated from when writes are made, meaning inserts or updates (because after a value is updated or added, the in-memory cache will be no longer valid).

有人推荐我使用 Redis,特别是 Book Sleeve,我的问题是我可以在哪里找到一些代码示例来帮助我弄清楚如何使用它开始构建进程外缓存并将其合并到我当前的项目中.

I was recommended on Redis, and specifically Book Sleeve, my question is where I can find some code samples to help me figure out how to start build the out-of-process cache with it and combine it in my current project.

提前致谢

推荐答案

如果您希望纯粹在进程外,那么它非常简单 - 类似于以下内容,但请注意 BookSleeve被设计为共享:它是完全线程安全的并且作为一个多路复用器工作——你不应该为每个调用创建/处理它们.另请注意,在这种情况下,我假设您将单独处理序列化,因此我只是公开了一个 byte[] API:

If you want purely out-of-process, then it is pretty simple - something like the following, but noting that a BookSleeve is designed to be shared: it is fully thread-safe and works as a multiplexer - you shouldn't create / dispose them for every call. Note also that in this context I'm assuming you will handle serialization separately, so I'm simply exposing a byte[] API:

class MyCache : IDisposable
{
    public void Dispose()
    {
        var tmp = conn;
        conn = null;
        if (tmp != null)
        {
            tmp.Close(true);
            tmp.Dispose();
        }
    }
    private RedisConnection conn;
    private readonly int db;
    public MyCache(string configuration = "127.0.0.1:6379", int db = 0)
    {
        conn = ConnectionUtils.Connect(configuration);
        this.db = db;
        if (conn == null) throw new ArgumentException("It was not possible to connect to redis", "configuration");
    }
    public byte[] Get(string key)
    {
        return conn.Wait(conn.Strings.Get(db, key));
    }
    public void Set(string key, byte[] value, int timeoutSeconds = 60)
    {
        conn.Strings.Set(db, key, value, timeoutSeconds);
    }
}

有趣是如果您想要一个 2 层缓存 - 即使用本地内存进程外缓存,因为现在您需要缓存失效.发布/订阅使这很方便 - 下面显示了这一点.这可能并不明显,但这会减少对 redis 的调用(您可以使用 monitor 来查看这一点)——因为大多数请求都是在本地缓存之外处理的.

What gets interesting is if you want a 2-tier cache - i.e. using local memory and the out-of-process cache, as now you need cache invalidation. Pub/sub makes that handy - the following shows this. It might not be obvious, but this would be doing a lot fewer calls to redis (you can use monitor to see this) - since most requests are handled out of the local cache.

using BookSleeve;
using System;
using System.Runtime.Caching;
using System.Text;
using System.Threading;

class MyCache : IDisposable
{
    public void Dispose()
    {
        var tmp0 = conn;
        conn = null;
        if (tmp0 != null)
        {
            tmp0.Close(true);
            tmp0.Dispose();
        }

        var tmp1 = localCache;
        localCache = null;
        if (tmp1 != null)
            tmp1.Dispose();

        var tmp2 = sub;
        sub = null;
        if (tmp2 != null)
        {
            tmp2.Close(true);
            tmp2.Dispose();
        }

    }
    private RedisSubscriberConnection sub;
    private RedisConnection conn;
    private readonly int db;
    private MemoryCache localCache;
    private readonly string cacheInvalidationChannel;
    public MyCache(string configuration = "127.0.0.1:6379", int db = 0)
    {
        conn = ConnectionUtils.Connect(configuration);
        this.db = db;
        localCache = new MemoryCache("local:" + db.ToString());
        if (conn == null) throw new ArgumentException("It was not possible to connect to redis", "configuration");
        sub = conn.GetOpenSubscriberChannel();
        cacheInvalidationChannel = db.ToString() + ":inval"; // note that pub/sub is server-wide; use
                                                             // a channel per DB here
        sub.Subscribe(cacheInvalidationChannel, Invalidate);   
    }

    private void Invalidate(string channel, byte[] payload)
    {
        string key = Encoding.UTF8.GetString(payload);
        var tmp = localCache;
        if (tmp != null) tmp.Remove(key);
    }
    private static readonly object nix = new object();
    public byte[] Get(string key)
    {
        // try local, noting the "nix" sentinel value
        object found = localCache[key];
        if (found != null)
        {
            return found == nix ? null : (byte[])found;
        }

        // fetch and store locally
        byte[] blob = conn.Wait(conn.Strings.Get(db, key));
        localCache[key] = blob ?? nix;
        return blob;
    }

    public void Set(string key, byte[] value, int timeoutSeconds = 60, bool broadcastInvalidation = true)
    {
        localCache[key] = value;
        conn.Strings.Set(db, key, value, timeoutSeconds);
        if (broadcastInvalidation)
            conn.Publish(cacheInvalidationChannel, key);
    }
}

static class Program
{
    static void ShowResult(MyCache cache0, MyCache cache1, string key, string caption)
    {
        Console.WriteLine(caption);
        byte[] blob0 = cache0.Get(key), blob1 = cache1.Get(key);
        Console.WriteLine("{0} vs {1}",
            blob0 == null ? "(null)" : Encoding.UTF8.GetString(blob0),
            blob1 == null ? "(null)" : Encoding.UTF8.GetString(blob1)
            );
    }
    public static void Main()
    {
        MyCache cache0 = new MyCache(), cache1 = new MyCache();
        string someRandomKey = "key" + new Random().Next().ToString();
        ShowResult(cache0, cache1, someRandomKey, "Initially");
        cache0.Set(someRandomKey, Encoding.UTF8.GetBytes("hello"));
        Thread.Sleep(10); // the pub/sub is fast, but not *instant*
        ShowResult(cache0, cache1, someRandomKey, "Write to 0");
        cache1.Set(someRandomKey, Encoding.UTF8.GetBytes("world"));
        Thread.Sleep(10); // the pub/sub is fast, but not *instant*
        ShowResult(cache0, cache1, someRandomKey, "Write to 1");
    }
}

请注意,在完整的实现中,您可能希望处理偶尔断开的连接、稍微延迟的重新连接等.

Note that in a full implementation you probably want to handle occasional broken connections, with a slightly delayed reconnect, etc.

这篇关于在 windows azure 中使用 Redis 实现进程外缓存的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆