2013-05-16 1 views
6

Ich habe auf einer Webseite gearbeitet, die eine Tabelle aus einer Datenbank in meiner azurblauen Wolke anzeigt. Um Aufrufe an die DB direkt zur Leistungsverbesserung zu reduzieren, möchte ich einen Cache für die Seite erstellen. Derzeit halte ich einen In-Memory-Cache (in Bearbeitung) für die liest der Tabelle. Jetzt möchte ich einen prozessexternen Cache machen, der aktualisiert werden sollte, wenn schreibt, was Einfügungen oder Aktualisierungen bedeutet (denn nachdem ein Wert aktualisiert oder hinzugefügt wurde, ist der In-Memory-Cache nicht mehr gültig).Implementieren von Out-of-Process-Cache mit Redis in Windows Azur

Ich wurde auf Redis empfohlen, und speziell Book Sleeve, meine Frage ist, wo ich einige Codebeispiele finden kann, um mir zu helfen herauszufinden, wie ich den Out-of-Process-Cache damit zu erstellen und es in meinem aktuellen Projekt zu kombinieren .

Vielen Dank im Voraus

+0

Sorry, ich habe diese nicht kommen - ich werde ein Beispiel hinzufügen später heute –

+0

Es scheint einige gute Antworten zu geben [hier] (http://stackoverflow.com/questions/10140669/howto-get-redis-running-on-azure) –

+0

Vielen Dank @MarcGravell, danke an Sie auch, Brian – DanielY

Antwort

8

Wenn Sie rein out-of-Prozess wollen, dann ist es ziemlich einfach - so etwas wie die folgenden, aber darauf hingewiesen, dass ein BookSleeve geteilt werden soll: es voll ist threadsicher und arbeitet als Multiplexer - Sie sollten sie nicht bei jedem Anruf anlegen/entsorgen. Beachten Sie auch, dass in diesem Zusammenhang Ich nehme an, Sie Serialisierung separat behandelt, so bin ich einfach ein byte[] API aussetzt:

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

Was interessant bekommt, wenn Sie ein 2-Ebene-Cache wollen - dh mit lokaler Speicher und der Out-of-Process-Cache, wie Sie jetzt Cache-Invalidierung benötigen. Pub/Sub macht das praktisch - das Folgende zeigt dies. Es ist vielleicht nicht offensichtlich, aber das würde viel weniger Anrufe zu redis tun (Sie können monitor verwenden, um dies zu sehen) - da die meisten Anfragen aus dem lokalen Cache behandelt werden.

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

Beachten Sie, dass in einer vollständigen Implementierung Sie wahrscheinlich gelegentlich unterbrochene Verbindungen behandeln wollen, mit einem leicht verzögerten Reconnect usw.

+0

Beachten Sie, dass das "Sleep" hier nur simuliert, dass Sie weitermachen und Ihr Geschäft machen; Der Punkt ist, dass innerhalb von etwa 0,5 ms der Editiervorgang alle Knoten darüber wissen –

+0

Über diese Implementierung: Sie senden Ungültigkeitsnachrichten und keine Daten. Gibt es einen Nachteil, die vollständigen Daten im PubSub-Kanal zu senden? Danke – Cybermaxs

+0

@ Cybermaxs-Betclic es bedeutet, dass Sie möglicherweise große Datenmengen an Clients senden, die es möglicherweise nie brauchen –

Verwandte Themen