2013-05-16 76 views
6

我一直在研究一個網頁,該網頁顯示我在天藍色雲中的數據庫中的表格。 爲了減少對數據庫的直接調用以提高性能,我想爲頁面構建一個緩存。目前,我持有表中的的內存中高速緩存(進程內)。現在我想創建一個進程外高速緩存,它應該從寫入的時候更新,這意味着插入或更新(因爲在更新或添加值之後,內存中的高速緩存將不再有效)。在Windows Azure中使用Redis實現進程外高速緩存

我在Redis上被推薦使用,特別是Book Sleeve,我的問題是我可以找到一些代碼示例以幫助我找出如何開始使用它創建進程外緩存,並將其結合到我當前的項目中。

在此先感謝

+0

對不起,我沒有看到這一個來 - 我加入了一個例子今天晚些時候 –

+0

似乎有一些很好的答案[這裏](http://stackoverflow.com/questions/10140669/howto-get-redis-running-on-azure) –

+0

非常感謝@MarcGravell,也感謝你,Brian – DanielY

回答

8

如果你想純粹外的過程,那麼這是很簡單的 - 類似於以下,但指出對於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級高速緩存 - 即使用本地內存進程外緩存,因爲現在您需要緩存失效。 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"); 
    } 
} 

請注意,在全面實施,你可能要處理偶爾斷開連接,用稍微延遲重新連接等

+0

請注意,這裏的「睡眠」只是模擬你繼續做你的生意;關鍵在於在編輯發生後大約0.5ms內,所有節點都會知道它 –

+0

關於此實現:您發送失效消息而不是數據。發佈PubSub頻道中的全部數據有什麼缺點嗎?謝謝 – Cybermaxs

+0

@ Cyber​​maxs-Betclic這將意味着您正在向可能不需要它的客戶端發送潛在的大塊數據 –