原文: 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對象。
因此咱們Redis的封裝有兩層,一層是NewLife.Core裏面的Redis以及RedisClient。另外一層就是NewLife.Redis。這裏面的FullRedis是對Redis的實現了Redis的全部的高級功能。這裏你也能夠認爲NewLife.Redis是Redis的一個擴展。
Test實例講解Redis的基本使用
實例
打開Program.cs看下代碼
這裏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); }
Set的時候若是是字符串或者字符數據的話Redis會直接保存起來(字符串內部機制也是保存二進制),若是是其餘類型會默認進行json序列化而後再保存起來
Get的時候若是是字符串或者字符數據會直接獲取,若是是其餘類型會進行json反序列化
Set第三個參數過時時間單位是秒。
vs調試小技巧,按F5或者直接工具欄「啓動」會編譯整個解決方案會很慢(VS默認),能夠選中項目而後右鍵菜單選擇調試->啓動新實例。會只編譯將會用到的項目,這樣對調試來講會快不少。
你們運行調試後能夠看到控制檯輸出的內容:向右的箭頭=》是
ic.Log=XTrace.Log
輸出的日誌字典的使用:對象的話須要把json所有取出來而後轉換成對象,而字典的話就能夠直接取某個字段。
隊列是List結構實現的,使用場景能夠上游數據太多,下游處理不過來的時候,那麼就可使用這個隊列。上游的數據發到隊列,而後下游慢慢的消費。另外一個應用,跨語言的協同工做,比方說其餘語言實現的程序往隊列裏面塞數據,而後另外一種語言來進行消費處理。哈,這種方式相似mq的概念,雖然有點low,可是也很好用。
集合,用的比較多的是用在一個須要精確判斷的去重功能。像咱們天天有三千萬訂單,這三千萬訂單能夠有重複,這時候我想統計下一共有訂單,這時候直接數據庫group by是不大可能的,由於數據庫中分了十幾張表,這裏分享個實戰經驗:比方說攬收,商家發貨了,網點要把件收回來,可是收回來以前網點不知道本身有多少貨啊,這時候咱們作了一個功能,也就是訂單會發送到咱們公司來,咱們會建一個time_site的key的集合,並且集合自己有去重的功能,並且咱們能夠很方便的經過set.Count功能來統計數量,當件被攬收之後,咱們後臺把這個件從集合中Remove掉.而後這個Set中存在的就是網點尚未攬收的件,這時候經過Count就會知道這個網點今天還有多少件沒有攬收。實際使用中這個數量比較大,由於有幾萬個網點。
Redis中布隆過濾器,去重的,面試的時候問的比較多
小經驗分享:
- 數據庫中不合法的時間處理:判斷時間中的年份,是否大於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(); }
運行的結果以下圖所示:
測試就是進行get,set remove,累加等的操做。你們能夠看到在我本機上輕輕鬆鬆的到了六十萬,多線程的時候甚至到了一百多萬。爲何會達到這麼高的ops呢,下面給你們說一下。
- Bench 會分根據線程數分多組進行添刪改壓力測試。
- rand 參數,是否隨機產生key/value。
- batch 批大小,分批執行讀寫操做,藉助GetAll/SetAll進行優化。
Redis中NB的函數來提高性能
上面的操做若是你們都掌握的基本算Redis入門了,接下來進行進階。會了基本比別人更勝一籌了。
- 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
- Redis管道Pipeline
好比執行10次命令會打包成一個包集體發過去執行,這裏實現的方式是StartPipeline()開始,StopPipeline()結束中間的代碼就會以管道的形式執行。這裏推薦使用咱們的更強的武器,AutoPipeline自動管道屬性。管道操做到必定數量時,自動提交,默認0。使用了AutoPipeline,就不須要StartPipeline,StopPipeline指定管道的開始結束了!
- 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使用的經驗,問題
-
一條數據多個key怎麼設置比較合理?
若是對性能要求不是很高直接用json序列化實體就好,不必使用字典進行存儲。 -
隊列跟List有什麼區別?左進右出的話用List仍是用隊列比較好?
隊列其實就是用List實現的,也是基於List封裝的。左進右出的話直接隊列就好。Redis的List結構比較有意思,既能夠左進右出,也能右進左出。因此它既能夠實現列表結構,也能隊列,也能實現棧 -
存放多個字段的類性能同樣嗎?
大部分場景都不會有誤差,可能對於大公司數據量比較大的場景會有些誤差 -
能否介紹一下使用Redis進行數據計算、統計的場景?
略。本身看視頻吧!o(∩_∩)o 哈哈!(由於我沒聽清!) -
大數據寫入到數據庫以後 好比數據到億以上的時候 統計分析這塊 查詢這塊 能不能分享些經驗。
分表分庫,拆分到一千萬之內。 -
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組件,供咱們選擇。
-
NewLife.Redis 他是NewLife團隊開發的,已經在ZTO大數據實時計算中普遍應用,200多個Redis實例穩定工做一年多,天天處理近1億包裹數據,日均調用量80億次。
-
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等提升效率的功能!話很少少下面咱們將經過一個個實例來看下他的操做吧。
簡單使用
-
獲取Nuget包(目前版本3.0.18)!哈,沒錯,使用前要經過Nuget來安裝下引用,什麼?你不知道怎麼使用Nuget包?對不起,右上角點下「X」 關掉網頁就能夠了。
nuget Install-Package CSRedisCore
-
幾種啓動模式介紹:
-
普通模式:
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() % 節點總數量,肯定連向的節點 //也能夠自定義規則(第一個參數設置)
-
-
今天我只給你們演示怎麼來進行使用,因此採用了普通模式,代碼以下所示:
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操做的話都儘可能使用同步操做。緣由在上篇也進行了介紹!這裏就再也不次進行介紹了!。
-
執行的結果以下所示:
大#家能夠摘錄代碼而後拷貝到一個新的控制檯程序中運行便可!
高級使用
上面給你們介紹了一些通用的使用方法,接下來呢咱們進行一些高級方法的使用。包括訂閱/發佈,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驅動性能比拼,上面有做者作的測試,大夥能夠看下,我也作個截圖分享
做者交流羣
做者交流QQ羣:8578575
總結
今天給你們介紹了.NET Core玩轉Redis的又一傻瓜式神器CSRedisCore的使用,因爲篇幅有限,因此還有不少方法沒有進行演示。大夥能夠按照本文的方法自行進行測試!(基本RedisCli裏面有的命令,都有對應的方法實現!)看到.net core的生態愈來愈好!有不少優秀的工具以及框架在開源!做爲.net Corer的你開森嘛?