天天看點

Redis 詳解 (一) 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,同步模式會直接阻塞調用者,但是顯然不會阻塞其他線程。

db.StringIncrement(pageKey, flags: CommandFlags.FireAndForget);           
Qt

繼續閱讀