標籤:
StackExchange.Redis Client
這期我們來看StackExchange.Redis,這是redis 的.net用戶端之一。Redis是一個開源的記憶體資料存放區,可以用來做資料庫,緩衝或者訊息代理服務。目前有不少人在使用ServiceStack.Redis這個.net用戶端,但是這個的最新版本目前已經變成了商業軟體。對於ServiceStack.Redis這種行為,我們沒有什麼好說的,留給我們的選擇是使用低版本的開源版本或者轉向其他的用戶端。
要說到StackExchange.Redis,就不得不說它和BookSleeve的關係。BookSleeve已經是比較完善的redis sdk,但是為什麼 BookSleeve 的作者要重新寫一個redis 的用戶端sdk呢? 有興趣的同學可以看這裡why i wrote another redis client 歸納起來其實就一句話:覺得不爽就推倒重來。
(╯?????‵)╯︵ ┴─┴ (╯-_-)╯╧╧ (╯‵□′)╯︵┴─┴ (╯‘ - ‘)╯︵ ┻━┻ ┬─┬ ノ
StackExchange.Redis 安裝
直接命令或者手動NuGet。PM> Install-Package StackExchange.Redis
如果需要強簽名的版本走下面的命令,當然作者對於強簽名的事也是充滿了怨念
PM> Install-Package StackExchange.Redis.StrongName
ConnectionMultiplexer
ConnectionMultiplexer對象是StackExchange.Redis最中樞的對象。這個類的執行個體需要被整個應用程式定義域共用和重用的,你不要在每個操作中不停的建立該對象的執行個體,所以使用單例來建立和存放這個對象是必須的。
public static ConnectionMultiplexer Manager { get { if (_redis == null) { lock (_locker) { if (_redis != null) return _redis; _redis = GetManager(); return _redis; } } return _redis; } } private static ConnectionMultiplexer GetManager(string connectionString = null) { if (string.IsNullOrEmpty(connectionString)) { connectionString = GetDefaultConnectionString(); } return ConnectionMultiplexer.Connect(connectionString); }
雖然ConnectionMultiplexer是實現了IDisposable介面的,但是我們基於重用的考慮,一般不需要去釋放它。
當作記憶體資料庫使用
IDatabase db = redis.GetDatabase();
這裡的GetDatabase() 返回的db對象是很輕量層級的,不需要被緩衝起來,每次用每次拿即可。IDatabase 的所有方法都有同步和非同步實現。其中的非同步實現都是可以await的。
一些基礎的操作的封裝。
public bool Remove(string key) { key = MergeKey(key); var db = RedisManager.Manager.GetDatabase(Database); return db.KeyDelete(key); } public string Get(string key) { key = this.MergeKey(key); var db = RedisManager.Manager.GetDatabase(Database); return db.StringGet(key); } public bool Set(string key, string value, int expireMinutes = 0) { key = MergeKey(key); var db = RedisManager.Manager.GetDatabase(Database); if (expireMinutes > 0) { db.StringSet(key, value, TimeSpan.FromMinutes(expireMinutes)); } else { db.StringSet(key, value); } return db.StringSet(key, value); } public bool HasKey(string key) { key = MergeKey(key); var db = RedisManager.Manager.GetDatabase(Database); return db.KeyExists(key); }
這裡的MergeKey用來拼接Key的首碼,具體不同的業務模組使用不同的首碼。
這裡有個和ServiceStack.Redis大的區別是沒有預設的串連池管理了。沒有串連池自然有其利弊,最大的好處在於等待擷取串連的等待時間沒有了,也不會因為串連池裡面的串連由於沒有正確釋放等原因導致無限等待而處於死結狀態。缺點在於一些低品質的代碼可能導致伺服器資源耗盡。不過提供串連池等阻塞和等待的手段是和作者的設計理念相違背的。StackExchange.Redis這裡使用管道和多工技術來實現減少串連,這裡後續展開再討論。
當作訊息代理中介軟體使用
訊息組建中,重要的概念便是生產者,消費者,訊息中介軟體。
ISubscriber sub = redis.GetSubscriber();
首先,先拿到一個ISubscriber對象。在生產者端我們發布一條訊息:
sub.Publish("messages", "hello");
,在消費者端得到該訊息並輸出
sub.Subscribe("messages", (channel, message) => { Console.WriteLine((string)message);});
一般使用更專業的訊息佇列來處理這種業務情境,因此這裡就略過了。
三種命令模式
Sync vs Async vs Fire-and-Forget
最後,這裡有三種命令模式分別對應StackExchange.Redis的三類不同的使用情境。
Sync,同步模式會直接阻塞調用者,但是顯然不會阻塞其他線程。
Async,非同步模式直接走的是Task模型。
Fire-and-Forget,就是發送命令,然後完全不關心最終什麼時候完成命令操作。
db.StringIncrement(pageKey, flags: CommandFlags.FireAndForget);
這裡值得注意的是,在Fire-and-Forget模式下,所有命令都會立即得到傳回值,當然該值都是該傳回值類型的預設值,比如操作傳回型別是bool將會立即得到false,因為false = default(bool)。
StackExchange.Redis Client