2013-05-16 11 views
6

Pracuję na stronie internetowej, która wyświetla tabelę z bazy danych, którą mam w mojej lazurowej chmurze. Aby zmniejszyć liczbę wywołań do bazy danych bezpośrednio w celu poprawy wydajności, chciałbym zbudować pamięć podręczną dla strony. Obecnie przechowuję pamięć podręczną w pamięci (w procesie) dla czytającą tabeli. Teraz chciałbym stworzyć pamięć podręczną poza procesem, która powinna być aktualizowana od kiedy są tworzone pisze, co oznacza wstawianie lub aktualizowanie (ponieważ po zaktualizowaniu lub dodaniu wartości pamięć podręczna w pamięci nie będzie już poprawna).wdrażanie pamięci podręcznej post-process przy użyciu usługi Redis w Windows Azure

Polecono mnie w Redis, a szczególnie w Book Sleeve, gdzie mogę znaleźć próbki kodu, które pomogą mi znaleźć sposób na zbudowanie pamięci podręcznej poza procesem i połączenie jej w moim bieżącym projekcie .

góry dzięki

+0

Niestety, nie widzę w tym jeden przybyć - dodam przykład później dzisiaj –

+0

Wydaje się, że jest kilka dobrych odpowiedzi [tutaj] (http://stackoverflow.com/questions/10140669/howto-get-redis-running-on -azure) –

+0

Dzięki za dużo @MarcGravell, dzięki tobie też, Brian – DanielY

Odpowiedz

8

Jeśli chcesz czysto out-of-process, to jest całkiem prosta - coś jak poniżej, ale zauważając, że BookSleeve jest przeznaczony do wspólne: Jest w pełni Wątek bezpieczny i działa jako multiplekser - nie powinieneś tworzyć/wyrzucać ich za każde połączenie. Należy również zauważyć, że w tym kontekście jestem przy założeniu będzie obsługiwać serializacji osobno, więc ja po prostu wystawiając 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); 
    } 
} 

Co dostaje interesujący jest, jeśli chcesz cache 2-tier - czyli za pomocą pamięć lokalna i pamięć podręczną poza procesem, ponieważ teraz potrzebujesz unieważnienia pamięci podręcznej. Pub/sub sprawia, że ​​jest to przydatne - pokazuje to poniższe. To może nie być oczywiste, ale byłoby to o wiele mniej połączeń do redis (można użyć monitor, aby zobaczyć to) - ponieważ większość żądań są obsługiwane z lokalnej pamięci podręcznej.

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

pamiętać, że pełne wdrożenie prawdopodobnie chcesz obsługiwać okazjonalnych zerwane połączenia, z lekko opóźnionym ponownym połączeniu itp

+0

Należy pamiętać, że 'Sleep' jest tutaj po to, aby zasymulować prowadzenie firmy; chodzi o to, że w ciągu około 0,5 ms edycji wszystkie węzły będą o tym wiedzieć. –

+0

O tej implementacji: wysyłasz komunikaty o unieważnieniu, a nie dane. Czy jest jakaś wada, aby wysłać pełne dane w kanale PubSub? Dzięki – Cybermaxs

+0

@ Cybermaxs-Betclic będzie to oznaczało, że wysyłasz potencjalnie duże porcje danych do klientów, które mogą nigdy nie być potrzebne –

Powiązane problemy