C# 在WindowsAzure中使用Redis实现进程外缓存
我一直在制作一个网页,其中显示了我在azure云中拥有的数据库中的一个表。 为了减少对数据库的直接调用以提高性能,我想为页面构建一个缓存。目前,我持有一个内存缓存(进程中),用于表的读取。现在,我想创建一个进程外缓存,它应该在写入时更新,这意味着插入或更新(因为更新或添加值后,内存内缓存将不再有效) 我被推荐使用Redis,特别是Book Sleeve,我的问题是在哪里可以找到一些代码示例来帮助我找到如何开始使用它构建进程外缓存,并将其结合到我当前的项目中 提前感谢如果您想要完全脱离进程,那么它非常简单-类似于以下内容,但请注意,书架是为共享而设计的:它是完全线程安全的,可以作为多路复用器使用-您不应该为每次调用创建/处理它们。还要注意,在此上下文中,我假设您将单独处理序列化,因此我只是公开一个C# 在WindowsAzure中使用Redis实现进程外缓存,c#,azure,redis,azure-sql-database,booksleeve,C#,Azure,Redis,Azure Sql Database,Booksleeve,我一直在制作一个网页,其中显示了我在azure云中拥有的数据库中的一个表。 为了减少对数据库的直接调用以提高性能,我想为页面构建一个缓存。目前,我持有一个内存缓存(进程中),用于表的读取。现在,我想创建一个进程外缓存,它应该在写入时更新,这意味着插入或更新(因为更新或添加值后,内存内缓存将不再有效) 我被推荐使用Redis,特别是Book Sleeve,我的问题是在哪里可以找到一些代码示例来帮助我找到如何开始使用它构建进程外缓存,并将其结合到我当前的项目中 提前感谢如果您想要完全脱离进程,那么它
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);
}
}
有趣的是,如果您需要两层缓存,即使用本地内存和进程外缓存,就像现在需要缓存失效一样。Pub/sub让这变得很方便——下面显示了这一点。这可能并不明显,但这将大大减少对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");
}
}
请注意,在完整的实现中,您可能希望处理偶尔断开的连接,以及稍微延迟的重新连接等。对不起,我没有看到这一个出现-今天晚些时候我将添加一个示例。似乎有一些很好的答案,非常感谢@MarcGravel,也感谢您,请注意,这里的
睡眠
只是为了模拟你继续做你的生意;关键是,在编辑发生后的大约0.5毫秒内,所有节点都会知道关于该实现的信息:您发送的是失效消息,而不是数据。在子频道中发送完整数据有什么缺点吗?Thanks@Cybermaxs-Betclic这意味着您正在向可能永远不需要它的客户端发送潜在的大量数据是的,但是让我们假设所有客户端都具有相同的拓扑结构,例如WebFarm中的唯一网站。好的,我们不能发送太多的数据(只有几KB?)这种发布子系统适合Redis吗?