c# - implementing out-of-process cache using Redis in windows azure -
i've been working on webpage displays table database have in azure cloud. in order reduce calls db directly performance improvement build cache page. currently, hold in-memory cache (in-process) reads of table. make out-of-process cache, should updated when writes made, meaning inserts or updates (because after value updated or added, in-memory cache no longer valid).
i recommended on redis, , book sleeve, question can find code samples me figure out how start build out-of-process cache , combine in current project.
thanks in advance
if want purely out-of-process, pretty simple - following, noting booksleeve designed shared: thread-safe , works multiplexer - shouldn't create / dispose them every call. note in context i'm assuming handle serialization separately, i'm exposing 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 not possible connect 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); } }
what gets interesting if want 2-tier cache - i.e. using local memory and out-of-process cache, need cache invalidation. pub/sub makes handy - following shows this. might not obvious, doing lot fewer calls redis (you can use monitor
see this) - since requests handled out of local cache.
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 not possible connect redis", "configuration"); sub = conn.getopensubscriberchannel(); cacheinvalidationchannel = db.tostring() + ":inval"; // note pub/sub server-wide; use // 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 "nix" sentinel value object found = localcache[key]; if (found != null) { return found == nix ? null : (byte[])found; } // fetch , 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); // pub/sub fast, not *instant* showresult(cache0, cache1, somerandomkey, "write 0"); cache1.set(somerandomkey, encoding.utf8.getbytes("world")); thread.sleep(10); // pub/sub fast, not *instant* showresult(cache0, cache1, somerandomkey, "write 1"); } }
note in full implementation want handle occasional broken connections, delayed reconnect, etc.
Comments
Post a Comment