在windows azure中使用Redis实现进程外缓存

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

我被推荐在Redis上,特别是Book Sleeve,我的问题是我可以找到一些代码示例,以帮助我弄清楚如何开始构建进程外缓存并将其组合到我当前的项目中。

提前致谢

如果你想要纯粹的进程外,那么它非常简单 – 如下所示,但注意BookSleeve被设计为共享 :它是完全线程安全的并且作为多路复用器 – 你不应该创建/为每次通话处理它们。 另请注意,在此上下文中我假设您将单独处理序列化,因此我只是公开一个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来查看) – 因为大多数请求都是从本地缓存中处理的。

 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"); } } 

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