[翻譯] C# 8.0 新特性 Redis基本使用及百億數據量中的使用技巧分享(附視頻地址及觀看指南) 【由淺至深】redis 實現發佈訂閱的幾種方式 .NET Core開發者的福音之玩轉Redis的

[翻譯] C# 8.0 新特性

2018-11-13 17:04 by Rwing, 1179 閱讀, 24 評論, 收藏編輯javascript

原文: Building C# 8.0
[譯註:原文主標題如此,但內容大部分爲新特性介紹,因此意譯標題爲 "C# 8.0 新特性"]html

C# 的下一個主要版本是 8.0。咱們已經爲它工做了很長一段時間,即便咱們構建併發布了次要版本 C# 7.1, 7.2 和 7.3,我仍然對 8.0 將帶來的新特性感到很是興奮。java

目前的計劃是 C# 8.0 將與 .NET Core 3.0 同時發佈。然而,隨着咱們正在開發的 Visual Studio 2019 的預覽版,這些特性將開始活躍起來。當這些出來的時候,您就能夠開始嘗試它們,咱們將提供有關各個特性的更多細節。這篇文章的目的是向您簡述預期的內容,以及如何理解它們。git

C# 8.0 新特性

下面是 C# 8.0 中最重要的新特性的概述。還有一些較小的改進正在進行中,這些改進將在將來幾個月逐漸顯現出來。程序員

Nullable reference types 可空引用類型

此特性的目的是幫助處理無處不在的空引用異常,這種異常已經困擾了半個世紀的面向對象編程。github

這個特性阻止您將 null 放入普通引用類型中(如字符串),從而使這些類型不可爲 null!不過它是溫和的提示警告,而不是錯誤。因此,它會讓現有代碼出現新的警告,所以您必須有選擇的使用該功能 (您能夠在項目、文件甚至行級別執行此操做)。面試

string s = null; // Warning: Assignment of null to non-nullable reference type

若是您確實想要 null 怎麼辦?可使用一個可空引用類型,例如 string? 這樣:redis

string? s = null; // Ok

當您嘗試使用可空引用類型時,你首先須要檢查是否爲空。編譯器會分析代碼流,以查看 null 值是否能夠將其用於當前位置:sql

void M(string? s) { Console.WriteLine(s.Length); // Warning: Possible null reference exception if (s != null) { Console.WriteLine(s.Length); // Ok: You won't get here if s is null } }

這個特性的要點是,C# 容許您表達「可空的意圖」,而且在您不遵照它時候發出警告。shell

Async streams 異步流

C# 5.0 的 async/await 特性使您能夠用很是簡單的代碼消費(或生產)異步結果, 而無需回調:

async Task<int> GetBigResultAsync() { var result = await GetResultAsync(); if (result > 20) return result; else return -1; }

若是您想要消費(或生產)連續的結果流(例如您可能從物聯網設備或雲服務得到),則沒有那麼有用。 異步流就是爲此而存在的。

若是您想要消費(或生產)連續的結果流(例如您可能從物聯網設備或雲服務得到),則沒有那麼有用。 異步流就是爲此而存在的。

咱們如今介紹一下您所指望的 IAsyncEnumerable<T>,即 IEnumerable<T> 的異步版本。容許您 await foreach 以消費它們的元素,並 yield return 以生產元素。

async IAsyncEnumerable<int> GetBigResultsAsync() { await foreach (var result in GetResultsAsync()) { if (result > 20) yield return result; } }

Ranges and indices 範圍和索引

咱們正在添加一個類型 Index,可用於索引。您能夠建立一個整型來表示從頭開始的索引,或者一個 ^ 前綴的從結尾表示的索引:

Index i1 = 3; // number 3 from beginning Index i2 = ^4; // number 4 from end int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"

咱們還引入了一個 Range 類型,它由兩個 Index 組成,一個用於開始,一個用於結束,而且能夠用 x..y 這樣的範圍表達式來編寫。而後,您可使用 Range 進行索引來生成切片:

var slice = a[i1..i2]; // { 3, 4, 5 }

Default implementations of interface members 接口成員的默認實現

如今,一旦你發佈了一個接口,遊戲就結束了:你不能在不破壞它的全部現有實現的狀況下向它添加成員。

在 C# 8.0 中,咱們容許您爲接口成員提供一個默認實現。所以,若是某人沒有實現該成員(可能由於他們編寫代碼時尚未該成員),他們將只獲得默認的實現。

interface ILogger { void Log(LogLevel level, string message); void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload } class ConsoleLogger : ILogger { public void Log(LogLevel level, string message) { ... } // Log(Exception) gets default implementation }

ConsoleLogger 類沒必要實現 ILogger 中 Log(Exception) 重載函數,由於它已經定義了默認實現。如今只要提供了一個默認實現,您就能夠添加新的成員到已經存在的公開接口中了。

Recursive patterns 遞歸的模式匹配

在模式匹配中,如今容許模式中包含其餘模式。

IEnumerable<string> GetEnrollees() { foreach (var p in People) { if (p is Student { Graduated: false, Name: string name }) yield return name; } }

這個模式 Student { Graduated: false, Name: string name } 會檢查 Person 是不是 Student,而後將常量模式 false 應用於 Graduated 屬性以查看它們是否已畢業,並將模式字符串 name 添加到其 Name 屬性中,獲得他們的名字(若是非空)。所以,若是 p 是 Student,沒有畢業而且具備非空的名字,則返回該名字。

Switch expressions Switch 表達式

帶有模式的 switch 語句在 C# 7.0 中很是強大,但編寫起來很麻煩。switch 表達式是一個「輕量級」版本,其中全部狀況都是表達式:

var area = figure switch { Line _ => 0, Rectangle r => r.Width * r.Height, Circle c => c.Radius * 2.0 * Math.PI, _ => throw new UnknownFigureException(figure) };

Target-typed new-expressions 已知目標類型的新表達式

在許多狀況下,當您建立新對象時,類型已經能夠從上下文中知道。在這些狀況下,能夠省略類型:

Point[] ps = { new (1, 4), new (3,-2), new (9, 5) }; // all Points

該功能的實現由社區成員提供,謝謝!

平臺依賴性

大多數 C# 8.0 語言特性均可以在任何版本的 .NET 上運行。可是,其中一些具備平臺依賴性。

Async streams, Index 和 Range 都依賴於 .NET Standard 2.1 的新類型。正如 Immo 在他的文章《公佈.NET Standard 2.1》所說的那樣,.NET Core 3.0 、Xamarin 、Unity 和 Mono 都將實現 .NET Standard 2.1,但 .NET Framework 4.8 不會。這意味着當您將 C# 8.0 指向到 .NET Framework 4.8 時,使用這些功能所需的類型將不可用。

與往常同樣,C# 編譯器對它所依賴的類型很是寬容。若是它能找到具備正確的名字和形態的類型,則很樂意將它們做爲目標。

默認接口實現依賴於新的加強運行時,咱們也不會在 .NET Runtime 4.8 中實現這些。所以,此特性不適用於 .NET Framework 4.8 和舊版本的 .NET。

十餘年間,爲了保持運行時的穩定,咱們沒法在其中實現新的語言特性。隨着現代化運行時的並行性和開源性,咱們以爲能夠負責任地去從新開發它們,並在考慮到這一點時進行語言設計。 Scott 在其 .NET Core 3.0 和 .NET Framework 4.8 更新中解釋說,.NET Framework 未來會看到較少的創新,而是關注穩定性和可靠性。考慮到這一點,咱們認爲,直接忽略某些語言特性會好一些。

想要了解更多?

C# 語言的設計過程是開源的,在這個repo中。若是您不常常跟進,可能會有點混亂和力不從心。語言設計的核心是語言設計會議,記錄在 C# 語言設計日記

大約一年前,我寫了一篇介紹C#中的可空引用類型的文章。您仍然能夠閱讀它並獲得一些信息。。

您還能夠觀看視頻,例如 Microsoft Build 2018 大會上的 C# 將來,或者 .NET Conf 2018 大會上的即將到來的 C#,它展現了其中一些特性。

Kathleen 有一篇很好的帖子來闡述了 .Net Core 3.0 中的 Visual Basic 的計劃。

當咱們開始將這些功能做爲 Visual Studio 2019 預覽版的一部分發布時,咱們還將發佈有關各個功能的更多詳細信息。

就我的而言,我火燒眉毛地要把它們交到大家全部人手中!

Happy hacking,

Mads Torgersen, Design Lead for C#

 

 

 

Redis基本使用及百億數據量中的使用技巧分享(附視頻地址及觀看指南)

 

做者:依樂祝
原文地址:http://www.javashuo.com/article/p-btlvrxjc-do.html

  • 主講人:大石頭
  • 時間:2018-11-10 晚上20:00
  • 地點:釘釘羣(組織代碼BKMV7685)QQ羣:1600800
  • 內容:Redis基本使用及百億數據量中的使用技巧分享
  • 記錄人:依樂祝

熱場準備

熟悉的開場白,你們晚上好啊,今天給你們分享的是Redis在大數據中的使用,可能真正講的是一些redis的使用技巧,Redis基本的一些東西。

首先給你們個地址,源碼以及實例都在裏面,固然今天的分享也是按照裏面的實例來進行的,你們能夠先進行下載。
http://git.newlifex.com/NewLife/NewLife.Redis
固然這裏也附上Redis的下載地址:
windows:https://github.com/MicrosoftArchive/redis/releases
http://x.newlifex.com/Redis-x64-3.2.100.msi
Linux:https://redis.io/download

開始

Redis封裝架構講解

實際上NewLife.Redis是一個完整的Redis協議的功能的實現,可是redis的核心功能並無在這裏面,Redis的核心功能的實現是在NewLife.Core裏面。這裏能夠打開看一下,NewLife.Core裏面有一個NewLife.Caching的命名空間,裏面有一個Redis類裏面實現了Redis的基本功能,另外一個類是RedisClient是Redis的客戶端。Redis的核心功能就是有這兩個類實現。RedisClient表明着Redis客戶端對服務器的一個鏈接。Redis真正使用的時候有一個Redis鏈接池,裏面存放着不少個RedisClient對象。

1541862210472
因此咱們Redis的封裝有兩層,一層是NewLife.Core裏面的Redis以及RedisClient。另外一層就是NewLife.Redis。這裏面的FullRedis是對Redis的實現了Redis的全部的高級功能。這裏你也能夠認爲NewLife.Redis是Redis的一個擴展。

Test實例講解Redis的基本使用

實例

打開Program.cs看下代碼

1541862821768

這裏XTrace.UseConsole();是向控制檯輸出日誌,方便調試使用查看結果。

接下來看第一個例子Test1。具體的我都在代碼中進行了註釋,你們能夠看下

static void Test1() { var ic = Redis.Create("127.0.0.1:6379", 3);//建立Redis實例,獲得FullRedis對象 //var ic = new FullRedis();//另外一種實例化的方式 //ic.Server = "127.0.0.1:6379"; //ic.Db = 3;//Redis中數據庫 ic.Log = XTrace.Log;//顯示日誌,進行Redis操做把日誌輸出,生產環境不用輸出日誌 // 簡單操做 Console.WriteLine("共有緩存對象 {0} 個", ic.Count);//緩存對象數量 ic.Set("name", "大石頭");//Set K-V結構,Set第二個參數能夠是任何類型 Console.WriteLine(ic.Get<String>("name"));//Get泛型,指定獲取的類型 ic.Set("time", DateTime.Now, 1);//過時時間秒 Console.WriteLine(ic.Get<DateTime>("time").ToFullString()); Thread.Sleep(1100); Console.WriteLine(ic.Get<DateTime>("time").ToFullString()); // 列表 var list = ic.GetList<DateTime>("list"); list.Add(DateTime.Now); list.Add(DateTime.Now.Date); list.RemoveAt(1); Console.WriteLine(list[list.Count - 1].ToFullString()); // 字典 var dic = ic.GetDictionary<DateTime>("dic"); dic.Add("xxx", DateTime.Now); Console.WriteLine(dic["xxx"].ToFullString()); // 隊列 var mq = ic.GetQueue<String>("queue"); mq.Add(new[] { "abc", "g", "e", "m" }); var arr = mq.Take(3); Console.WriteLine(arr.Join(",")); // 集合 var set = ic.GetSet<String>("181110_1234"); set.Add("xx1"); set.Add("xx2"); set.Add("xx3"); Console.WriteLine(set.Count); Console.WriteLine(set.Contains("xx2")); Console.WriteLine("共有緩存對象 {0} 個", ic.Count); }
  1. Set的時候若是是字符串或者字符數據的話Redis會直接保存起來(字符串內部機制也是保存二進制),若是是其餘類型會默認進行json序列化而後再保存起來

  2. Get的時候若是是字符串或者字符數據會直接獲取,若是是其餘類型會進行json反序列化

  3. Set第三個參數過時時間單位是秒。

  4. vs調試小技巧,按F5或者直接工具欄「啓動」會編譯整個解決方案會很慢(VS默認),能夠選中項目而後右鍵菜單選擇調試->啓動新實例。會只編譯將會用到的項目,這樣對調試來講會快不少。

  5. 你們運行調試後能夠看到控制檯輸出的內容:向右的箭頭=》是ic.Log=XTrace.Log輸出的日誌

    1541865736212

  6. 字典的使用:對象的話須要把json所有取出來而後轉換成對象,而字典的話就能夠直接取某個字段。

  7. 隊列是List結構實現的,使用場景能夠上游數據太多,下游處理不過來的時候,那麼就可使用這個隊列。上游的數據發到隊列,而後下游慢慢的消費。另外一個應用,跨語言的協同工做,比方說其餘語言實現的程序往隊列裏面塞數據,而後另外一種語言來進行消費處理。哈,這種方式相似mq的概念,雖然有點low,可是也很好用。

  8. 集合,用的比較多的是用在一個須要精確判斷的去重功能。像咱們天天有三千萬訂單,這三千萬訂單能夠有重複,這時候我想統計下一共有訂單,這時候直接數據庫group by是不大可能的,由於數據庫中分了十幾張表,這裏分享個實戰經驗:比方說攬收,商家發貨了,網點要把件收回來,可是收回來以前網點不知道本身有多少貨啊,這時候咱們作了一個功能,也就是訂單會發送到咱們公司來,咱們會建一個time_site的key的集合,並且集合自己有去重的功能,並且咱們能夠很方便的經過set.Count功能來統計數量,當件被攬收之後,咱們後臺把這個件從集合中Remove掉.而後這個Set中存在的就是網點尚未攬收的件,這時候經過Count就會知道這個網點今天還有多少件沒有攬收。實際使用中這個數量比較大,由於有幾萬個網點。

  9. Redis中布隆過濾器,去重的,面試的時候問的比較多

  10. 小經驗分享:

    • 數據庫中不合法的時間處理:判斷時間中的年份,是否大於2000年。若是小於2000就認爲不合法。習慣大於小於號不習慣用等於號,這樣能夠處理不少意外的數據
    • Set的時候最好指定過時時間防止有些須要刪除的數據,咱們忘記刪了
    • Redis異步儘可能不用,由於Redis延遲自己很小,大概在100us-200us,再一個就是Redis自己是單線程的,異步任務切換的耗時比網絡耗時還要大。
    • List用法:物聯網中數據上傳,量比較大時,咱們能夠把這些數據先放在Redis的List中,好比說一秒鐘1萬條,而後再批量取出來而後批量插入數據庫中。這時候要設置好key,能夠前綴+時間,對於已經處理的List能夠進行remove移除。

壓力測試

接下來看第四個例子,咱們直接作壓力測試,代碼以下:

static void Main(String[] args) { XTrace.UseConsole(); // 激活FullRedis,不然Redis.Create會獲得默認的Redis對象 FullRedis.Register(); Test4(); Console.ReadKey(); } static void Test4() { var ic = Redis.Create("127.0.0.1:6379", 5); //var ic = new MemoryCache(); ic.Bench(); }

運行的結果以下圖所示:

1541867419541

測試就是進行get,set remove,累加等的操做。你們能夠看到在我本機上輕輕鬆鬆的到了六十萬,多線程的時候甚至到了一百多萬。爲何會達到這麼高的ops呢,下面給你們說一下。

  • Bench 會分根據線程數分多組進行添刪改壓力測試。
  • rand 參數,是否隨機產生key/value。
  • batch 批大小,分批執行讀寫操做,藉助GetAll/SetAll進行優化。

Redis中NB的函數來提高性能

上面的操做若是你們都掌握的基本算Redis入門了,接下來進行進階。會了基本比別人更勝一籌了。

  1. GetAll()與SetAll()

GetAll:比方說我要取十個key,這個時候能夠用getall。這時候redis就執行了一次命令。比方說我要取10個key那麼用get的話要取10次,若是用getall的話要用1次。一次getall時間大概是get的一點幾倍,可是10次get的話就是10倍的時間,這個帳你應該會算吧。強烈推薦你們用getall。

setall 跟getall類似。批量設置K-V.

setall與getall性能很恐怖,官方公佈的ops也就10萬左右,爲何咱們的測試輕輕鬆鬆到五十萬甚至上百萬,由於咱們就用了setall,getall。

若是get,set兩次以上,建議用getall,setall

  1. Redis管道Pipeline

好比執行10次命令會打包成一個包集體發過去執行,這裏實現的方式是StartPipeline()開始,StopPipeline()結束中間的代碼就會以管道的形式執行。這裏推薦使用咱們的更強的武器,AutoPipeline自動管道屬性。管道操做到必定數量時,自動提交,默認0。使用了AutoPipeline,就不須要StartPipeline,StopPipeline指定管道的開始結束了!

  1. Add與Replace
  • Add:Redis中沒有這個Key就添加,有了就不要添加,返回false
  • Replace:有則替換,還會返回原來的值,沒有則不進行操做

Add跟Replace就是實現Redis分佈式鎖的關鍵

Redis使用技巧,經驗分享

在項目的Readme中,這裏摘錄下:

特性

  • 在ZTO大數據實時計算普遍應用,200多個Redis實例穩定工做一年多,天天處理近1億包裹數據,日均調用量80億次
  • 低延遲,Get/Set操做平均耗時200~600us(含往返網絡通訊)
  • 大吞吐,自帶鏈接池,最大支持1000併發
  • 高性能,支持二進制序列化(默認用的json,json很低效,轉成二進制性能會提高不少)

Redis經驗分享

  • 在Linux上多實例部署,實例個數等於處理器個數,各實例最大內存直接爲本機物理內存,避免單個實例內存撐爆(比方說8核心處理器,那麼就部署8個實例)
  • 把海量數據(10億+)根據key哈希(Crc16/Crc32)存放在多個實例上,讀寫性能成倍增加
  • 採用二進制序列化,而很是見的Json序列化
  • 合理設計每一對Key的Value大小,包括但不限於使用批量獲取,原則是讓每次網絡包控制在1.4k字節附近,減小通訊次數(實際經驗幾十k,幾百k也是沒問題的)
  • Redis客戶端的Get/Set操做平均耗時200~600us(含往返網絡通訊),以此爲參考評估網絡環境和Redis客戶端組件(達不到就看一下網絡,序列化方式等等)
  • 使用管道Pipeline合併一批命令
  • Redis的主要性能瓶頸是序列化、網絡帶寬和內存大小,濫用時處理器也會達到瓶頸
  • 其它可查優化技巧
    以上經驗,源自於300多個實例4T以上空間一年多穩定工做的經驗,並按照重要程度排了前後順序,可根據場景須要酌情采用!

緩存Redis的兄弟姐妹

Redis實現ICache接口,它的孿生兄弟MemoryCache,內存緩存,千萬級吞吐率。
各應用強烈建議使用ICache接口編碼設計,小數據時使用MemoryCache實現;
數據增大(10萬)之後,改用Redis實現,不須要修改業務代碼。

提問環節聊聊大數據中Redis使用的經驗,問題

  1. 一條數據多個key怎麼設置比較合理?
    若是對性能要求不是很高直接用json序列化實體就好,不必使用字典進行存儲。

  2. 隊列跟List有什麼區別?左進右出的話用List仍是用隊列比較好?
    隊列其實就是用List實現的,也是基於List封裝的。左進右出的話直接隊列就好。Redis的List結構比較有意思,既能夠左進右出,也能右進左出。因此它既能夠實現列表結構,也能隊列,也能實現棧

  3. 存放多個字段的類性能同樣嗎?
    大部分場景都不會有誤差,可能對於大公司數據量比較大的場景會有些誤差

  4. 能否介紹一下使用Redis進行數據計算、統計的場景?
    略。本身看視頻吧!o(∩_∩)o 哈哈!(由於我沒聽清!)

  5. 大數據寫入到數據庫以後 好比數據到億以上的時候 統計分析這塊 查詢這塊 能不能分享些經驗。
    分表分庫,拆分到一千萬之內。

  6. CPU爲什麼暴漲?

程序員終極理念:CPU達到百分百,而後性能達到最優,儘可能不要浪費。最痛恨的是:若是cpu不到百分百,性能無法提高了,說明代碼有問題!

視頻地址

視頻已經上傳至百度雲,你們能夠自行下載觀看
連接:https://pan.baidu.com/s/1sOW_PLjxQE8C2msbDfizeA
提取碼:c7dp
觀看指南(笑笑提供)

總結

雖然Redis會用,可是沒有像大石頭這樣的大數據使用場景。今天的視頻收穫頗豐,可能大部分人跟我同樣,沒有大石頭的使用場景,可是值得借鑑的經驗仍是很豐富的!期待下一次的精彩分享。同時附上QQ羣:1600800。能夠共同交流使用經驗!

 
 

【由淺至深】redis 實現發佈訂閱的幾種方式

 

很是感謝依樂祝發表文章《.NET Core開發者的福音之玩轉Redis的又一傻瓜式神器推薦》,對csredis做了一次完整的詮釋。

前言

提到消息隊列,最熟悉無疑是 rabbitmq,它基本是業界標準的解決方案。本文詳細介紹 redis 多種實現輕訂閱方法,做者認爲很是有趣並加以總結,但願對有須要的朋友學習 redis 功能有必定的帶入做用。

方法一:SUBSCRIBE + PUBLISH

//程序1:使用代碼實現訂閱端 var sub = RedisHelper.Subscribe(("chan1", msg => Console.WriteLine(msg.Body))); //sub.Disponse(); //中止訂閱 //程序2:使用代碼實現發佈端 RedisHelper.Publish("chan1", "111");

優點:支持多端訂閱、簡單、性能高;
缺點:數據會丟失;

參考資料:http://doc.redisfans.com/pub_sub/subscribe.html

方法二:BLPOP + LPUSH(爭搶)

//程序1:使用代碼實現訂閱端 while (running) { try { var msg = RedisHelper.BLPop(5, "list1"); if (string.IsNullOrEmpty(msg) == false) { Console.WriteLine(msg); } } catch (Exception ex) { Console.WriteLine(ex.Message); } } //程序2:使用代碼實現發佈端 RedisHelper.LPush("list1", "111");

優點:數據不會丟失、簡單、性能高;
缺點:不支持多端(存在資源爭搶);

總結:爲了解決方法一的痛點,咱們實現了本方法,而且很漂亮的製造了一個新問題(不支持多端訂閱)。

學習使用 BLPOP

BLPOP key [key ...] timeout

BLPOP 是列表的阻塞式(blocking)彈出原語。

它是 LPOP 命令的阻塞版本,當給定列表內沒有任何元素可供彈出的時候,鏈接將被 BLPOP 命令阻塞,直到等待超時或發現可彈出元素爲止。

當給定多個 key 參數時,按參數 key 的前後順序依次檢查各個列表,彈出第一個非空列表的頭元素。

非阻塞行爲

當 BLPOP 被調用時,若是給定 key 內至少有一個非空列表,那麼彈出遇到的第一個非空列表的頭元素,並和被彈出元素所屬的列表的名字一塊兒,組成結果返回給調用者。

當存在多個給定 key 時, BLPOP 按給定 key 參數排列的前後順序,依次檢查各個列表。

假設如今有 job 、 command 和 request 三個列表,其中 job 不存在, command 和 request 都持有非空列表。考慮如下命令:

BLPOP job command request 0

BLPOP 保證返回的元素來自 command ,由於它是按」查找 job -> 查找 command -> 查找 request 「這樣的順序,第一個找到的非空列表。

redis> DEL job command request # 確保key都被刪除 (integer) 0  redis> LPUSH command "update system..." # 爲command列表增長一個值 (integer) 1  redis> LPUSH request "visit page" # 爲request列表增長一個值 (integer) 1  redis> BLPOP job command request 0 # job 列表爲空,被跳過,緊接着 command 列表的第一個元素被彈出。 1) "command" # 彈出元素所屬的列表 2) "update system..." # 彈出元素所屬的值

阻塞行爲

若是全部給定 key 都不存在或包含空列表,那麼 BLPOP 命令將阻塞鏈接,直到等待超時,或有另外一個客戶端對給定 key 的任意一個執行 LPUSH 或 RPUSH 命令爲止。

超時參數 timeout 接受一個以秒爲單位的數字做爲值。超時參數設爲 0 表示阻塞時間能夠無限期延長(block indefinitely) 。

redis> EXISTS job # 確保兩個 key 都不存在 (integer) 0 redis> EXISTS command (integer) 0  redis> BLPOP job command 300 # 由於key一開始不存在,因此操做會被阻塞,直到另外一客戶端對 job 或者 command 列表進行 PUSH 操做。 1) "job" # 這裏被 push 的是 job 2) "do my home work" # 被彈出的值 (26.26s) # 等待的秒數  redis> BLPOP job command 5 # 等待超時的狀況 (nil) (5.66s) # 等待的秒數

更多學習資料:http://doc.redisfans.com/list/blpop.html

方法三:BLPOP + LPUSH(非爭搶)

本方法根據方法二演變而來,設計圖以下:

如何實現三端訂閱,均可收到消息,三端分別爲 sub3, sub4, sub5:

一、sub3, sub4, sub5 使用【方法二】訂閱 listkey:list1_sub3,list1_sub4,list1_sub5;

二、總訂閱端訂閱 listkey:list1,總訂閱端收到消息後,執行 lpush list1_sub1 msg, lpush list1_sub2 msg, lpush list1_sub3 msg;

總訂閱端訂閱原始消息,隨後將消息分發給其餘訂閱端,從而解決【方法二】不支持多端同時訂閱的缺點。

最終實現的邏輯爲:多端先爭搶 list1 消息,搶到者再向其餘端轉發消息。

測試代碼

nuget Install-Package CSRedisCore

var rds = new CSRedis.CSRedisClient("127.0.0.1:6379,password=,poolsize=50,ssl=false,writeBuffer=10240"); //sub1, sub2 爭搶訂閱(只可一端收到消息) var sub1 = rds.SubscribeList("list1", msg => Console.WriteLine($"sub1 -> list1 : {msg}")); var sub2 = rds.SubscribeList("list1", msg => Console.WriteLine($"sub2 -> list1 : {msg}")); //sub3, sub4, sub5 非爭搶訂閱(多端均可收到消息) var sub3 = rds.SubscribeListBroadcast("list2", "sub3", msg => Console.WriteLine($"sub3 -> list2 : {msg}")); var sub4 = rds.SubscribeListBroadcast("list2", "sub4", msg => Console.WriteLine($"sub4 -> list2 : {msg}")); var sub5 = rds.SubscribeListBroadcast("list2", "sub5", msg => Console.WriteLine($"sub5 -> list2 : {msg}")); //sub6 是redis自帶的普通訂閱 var sub6 = rds.Subscribe(("chan1", msg => Console.WriteLine(msg.Body))); Console.ReadKey(); sub1.Dispose(); sub2.Dispose(); sub3.Dispose(); sub4.Dispose(); sub5.Dispose(); sub6.Dispose(); rds.Dispose(); return;

測試功能時,發佈端可使用 redis-cli 工具。

結語

redis 功能何其多且至關好玩有趣 ,你們應儘量多帶着興趣愛好去學習它。

若文中有很差的地方,請提出批評與改正方法,謝謝觀賞。

本文使用到 CSRedisCore 的開源地址:https://github.com/2881099/csredis

 

 

.NET Core開發者的福音之玩轉Redis的又一傻瓜式神器推薦

 

做者:依樂祝
本來連接:http://www.javashuo.com/article/p-xigxefmp-kv.html

引子

爲何寫這篇文章呢?由於.NET Core的生態愈來愈好了!以前玩轉.net的時候操做Redis相信大夥都使用過一些組件,但都有一些缺點,如ServiceStack.Redis 是商業版,免費版有限制;StackExchange.Redis 是免費版,可是內核在 .NETCore 運行時常常有 Timeout的問題,暫沒法解決(據農碼一輩子大佬說:https://github.com/StackExchange/StackExchange.Redis/issues/871 試試StackExchange.Redis 2.0 呢,超時問題好像解決了。但仍是有朋友說,2.0也仍是會出現超時的問題)有興趣的能夠試試;csredis做者在 2014 年之後就沒有更新了,它不支持 .net core,可是它的源碼可讀性很強很是乾淨,幾乎無任何依賴。可是隨着.NET Core生態的愈來愈好,又涌現了一批咱們國內大牛開發的支持.Net Core的Redis組件,供咱們選擇。

  1. NewLife.Redis 他是NewLife團隊開發的,已經在ZTO大數據實時計算中普遍應用,200多個Redis實例穩定工做一年多,天天處理近1億包裹數據,日均調用量80億次。

  2. CSRedis (這裏我更喜歡把它叫作CSRedisCore)這是另外一個國內大牛nicye 開發的,爲人很低調,因此瞭解他的人不多!目前我項目中普遍使用的也是這個。做者前不久剛作了一個幾大Redis組件的性能測試.net core 2.0 redis驅動性能比拼 有興趣的能夠打開連接看一下。

    注:此CSRedis(今天本文的主角CSRedisCore) 非彼CSRedis(.net 時代的組件,好久沒更新了,不支持.net core)

NewLife.Redis的使用方法在前兩天的Redis基本使用及百億數據量中的使用技巧分享(附視頻地址及觀看指南)文章中已經分享了!文章也有視頻教程。因此今天的文章將介紹另外一個玩轉Redis的神器-CSRedis了!

基本使用

CSRedisCore的使用很簡單,就須要實例化一個CSRedisClient(集羣鏈接池)對象而後初始化一下RedisHelper就能夠了,他的方法名與redis-cli基本保持一致。因此說你能夠像使用redis-cli命令同樣來使用它。做者最近也支持了Pipeline功能以及MGet,MSet等提升效率的功能!話很少少下面咱們將經過一個個實例來看下他的操做吧。

簡單使用

  1. 獲取Nuget包(目前版本3.0.18)!哈,沒錯,使用前要經過Nuget來安裝下引用,什麼?你不知道怎麼使用Nuget包?對不起,右上角點下「X」 關掉網頁就能夠了。

    nuget Install-Package CSRedisCore
  2. 幾種啓動模式介紹:

    • 普通模式:

      var csredis = new CSRedis.CSRedisClient("127.0.0.1:6379,password=123,defaultDatabase=13,poolsize=50,ssl=false,writeBuffer=10240,prefix=key前輟");
    • 官方集羣模式:假設你已經配置好 redis-trib 集羣,定義一個【普通模式】的 CSRedisClient 對象,它會根據 redis-server 返回的 MOVED | ASK 錯誤記錄slot,自動增長節點 Nodes 屬性。

      127.0.0.1:6379,password=123,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=

      其餘節點在運行過程當中自動增長,確保每一個節點密碼一致。

      警告:本模式與【分區模式】同時使用時,切記不可設置「prefix=key前輟」(或者所有設置成同樣),不然會致使 keySlot 計算結果與服務端不匹配,沒法記錄 slotCache。

      注意:官方集羣不支持多 keys 的命令、【管道】、Eval(腳本)等衆多殺手級功能。

    • 分區模式:本功能實現多個服務節點分擔存儲(做者本身實現的一種方式),與官方的分區、集羣、高可用方案不一樣。

      例如:緩存數據達到500G,若是使用一臺redis-server服務器光靠內存存儲將很是吃力,使用硬盤又影響性能。
      可使用此功能自動管理N臺redis-server服務器分擔存儲,每臺服務器只需約 (500/N)G 內存,且每臺服務器勻能夠配置官方高可用架構。

      var csredis = new CSRedis.CSRedisClient(null, "127.0.0.1:6371,password=123,defaultDatabase=11,poolsize=10,ssl=false,writeBuffer=10240,prefix=key前輟", "127.0.0.1:6372,password=123,defaultDatabase=12,poolsize=11,ssl=false,writeBuffer=10240,prefix=key前輟", "127.0.0.1:6373,password=123,defaultDatabase=13,poolsize=12,ssl=false,writeBuffer=10240,prefix=key前輟", "127.0.0.1:6374,password=123,defaultDatabase=14,poolsize=13,ssl=false,writeBuffer=10240,prefix=key前輟"); //實現思路:根據key.GetHashCode() % 節點總數量,肯定連向的節點 //也能夠自定義規則(第一個參數設置)
  3. 今天我只給你們演示怎麼來進行使用,因此採用了普通模式,代碼以下所示:

    static void Main(string[] args)
            {
                //普通模式 var csredis = new CSRedis.CSRedisClient("127.0.0.1:6379,password=123,defaultDatabase=1,poolsize=50,ssl=false,writeBuffer=10240"); //初始化 RedisHelper RedisHelper.Initialization(csredis); //Install-Package Caching.CSRedis (本篇不須要) //註冊mvc分佈式緩存 //services.AddSingleton<IDistributedCache>(new Microsoft.Extensions.Caching.Redis.CSRedisCache(RedisHelper.Instance)); Test(); Console.ReadKey(); } static void Test() { RedisHelper.Set("name", "祝雷");//設置值。默認永不過時 //RedisHelper.SetAsync("name", "祝雷");//異步操做 Console.WriteLine(RedisHelper.Get<String>("name")); RedisHelper.Set("time", DateTime.Now, 1); Console.WriteLine(RedisHelper.Get<DateTime>("time")); Thread.Sleep(1100); Console.WriteLine(RedisHelper.Get<DateTime>("time")); // 列表 RedisHelper.RPush("list", "第一個元素"); RedisHelper.RPush("list", "第二個元素"); RedisHelper.LInsertBefore("list", "第二個元素", "我是新插入的第二個元素!"); Console.WriteLine($"list的長度爲{RedisHelper.LLen("list")}"); //Console.WriteLine($"list的長度爲{RedisHelper.LLenAsync("list")}");//異步 Console.WriteLine($"list的第二個元素爲{RedisHelper.LIndex("list",1)}"); //Console.WriteLine($"list的第二個元素爲{RedisHelper.LIndexAsync("list",1)}");//異步 // 哈希 RedisHelper.HSet("person","name", "zhulei"); RedisHelper.HSet("person", "sex", "男"); RedisHelper.HSet("person", "age", "28"); RedisHelper.HSet("person", "adress", "hefei"); Console.WriteLine($"person這個哈希中的age爲{RedisHelper.HGet<int>("person","age")}"); //Console.WriteLine($"person這個哈希中的age爲{RedisHelper.HGetAsync<int>("person", "age")}");//異步 // 集合 RedisHelper.SAdd("students","zhangsan", "lisi"); RedisHelper.SAdd("students", "wangwu"); RedisHelper.SAdd("students", "zhaoliu"); Console.WriteLine($"students這個集合的大小爲{RedisHelper.SCard("students")}"); Console.WriteLine($"students這個集合是否包含wagnwu:{RedisHelper.SIsMember("students", "wangwu")}"); } 

    經過上面的代碼你們能夠看到對於Redis的操做都是使用RedisHelper這個類來實現的。並且,對Redis的全部操做名稱都跟Redis-Cli命令高度一致!這樣就會方便不少!同時對全部的方法在實現上都有同步異步的操做!這裏建議進行Redis操做的話都儘可能使用同步操做。緣由在上篇也進行了介紹!這裏就再也不次進行介紹了!。

  4. 執行的結果以下所示:

    1542015107575

大#家能夠摘錄代碼而後拷貝到一個新的控制檯程序中運行便可!

高級使用

上面給你們介紹了一些通用的使用方法,接下來呢咱們進行一些高級方法的使用。包括訂閱/發佈,PipeLine,緩存殼等等。

訂閱與發佈

//普通訂閱 RedisHelper.Subscribe( ("chan1", msg => Console.WriteLine(msg.Body)), ("chan2", msg => Console.WriteLine(msg.Body))); //模式訂閱(通配符) RedisHelper.PSubscribe(new[] { "test*", "*test001", "test*002" }, msg => { Console.WriteLine($"PSUB {msg.MessageId}:{msg.Body} {msg.Pattern}: chan:{msg.Channel}"); }); //模式訂閱已經解決的難題: //一、分區的節點匹配規則,致使通配符最大可能匹配所有節點,因此所有節點都要訂閱 //二、本組 "test*", "*test001", "test*002" 訂閱所有節點時,須要解決同一條消息不可執行屢次 //發佈 RedisHelper.Publish("chan1", "123123123"); //不管是分區或普通模式,RedisHelper.Publish 均可以正常通訊
//不加緩存的時候,要從數據庫查詢 var t1 = Test.Select.WhereId(1).ToOne(); //通常的緩存代碼,如不封裝還挺繁瑣的 var cacheValue = RedisHelper.Get("test1"); if (!string.IsNullOrEmpty(cacheValue)) { try { return JsonConvert.DeserializeObject(cacheValue); } catch { //出錯時刪除key RedisHelper.Remove("test1"); throw; } } var t1 = Test.Select.WhereId(1).ToOne(); RedisHelper.Set("test1", JsonConvert.SerializeObject(t1), 10); //緩存10秒 //使用緩存殼效果同上,如下示例使用 string 和 hash 緩存數據 var t1 = RedisHelper.CacheShell("test1", 10, () => Test.Select.WhereId(1).ToOne()); var t2 = RedisHelper.CacheShell("test", "1", 10, () => Test.Select.WhereId(1).ToOne()); var t3 = RedisHelper.CacheShell("test", new [] { "1", "2" }, 10, notCacheFields => new [] { ("1", Test.Select.WhereId(1).ToOne()), ("2", Test.Select.WhereId(2).ToOne()) });

Pipeline及MGet,MSet

使用管道模式,打包多條命令一塊兒執行,從而提升性能。

var ret1 = RedisHelper.StartPipe().Set("a", "1").Get("a").EndPipe(); var ret2 = RedisHelper.StartPipe(p => p.Set("a", "1").Get("a")); var ret3 = RedisHelper.StartPipe().Get("b").Get("a").Get("a").EndPipe(); //與 RedisHelper.MGet("b", "a", "a") 性能相比,經測試差之毫釐 

壓力測試對比

到這裏你可能要問了,CSRedisCore性能如何呢?跟其餘的Redis組件相比又如何呢、這裏給出一個連接.net core 2.0 redis驅動性能比拼?.net core 2.0 redis驅動性能比拼,上面有做者作的測試,大夥能夠看下,我也作個截圖分享

1542015308886

做者交流羣

做者交流QQ羣:8578575

總結

今天給你們介紹了.NET Core玩轉Redis的又一傻瓜式神器CSRedisCore的使用,因爲篇幅有限,因此還有不少方法沒有進行演示。大夥能夠按照本文的方法自行進行測試!(基本RedisCli裏面有的命令,都有對應的方法實現!)看到.net core的生態愈來愈好!有不少優秀的工具以及框架在開源!做爲.net Corer的你開森嘛?

相關文章
相關標籤/搜索