原文: 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的你开森嘛?