一. List类型基础

1.介绍

  它是一个双向链表,支持左进、左出、右进、右出,所以它即可以充当队列使用,也可以充当栈使用。

(1). 队列:先进先出, 可以利用List左进右出,或者右进左出(ListLeftPush和ListRightPop配合 、 ListRightPush和ListLeftPop配合)

(2). 栈:先进后出,可以利用List左进左出,或者右进右出

2. 常用指令Api

3.常用Api

(1). ListLeftPush:从左侧添加,返回集合总数

(2). ListRightPush:从右侧添加,返回集合总数

(3). ListLeftPop:从左侧取1个值,并删除

(4). ListRightPop:从右侧取1个值,并删除

(5). ListInsertBefore:指定的key指定value之前(左边)插入1个值

(6). ListInsertAfter:指定的key指定value之后(右边)插入1个值

(7). ListGetByIndex:获取key的指定索引对应的value值(从左往右算)

(8). ListRange:获取key的所有value,数据类型得一致 (也可以获取指定索引之间的value值,带扩展)

(9). ListLength:获取指定key的数据的个数

(10). ListRemove:删除指定key对应的指定value值,返回删除的个数

(11). ListRightPopLeftPush:从List1右侧取一个值加到List2左侧,返回的是右侧取出来的这个值

代码分享:

             //1.从左侧添加
//单个,返回集合总数
db.ListLeftPush("group1", "你好1");
db.ListLeftPush("group1", "你好2");
db.ListLeftPush("group1", "你好3");
//多个
string[] dList1 = { "你好4", "你好5" };
RedisValue[] redisValue = dList1.Select(u => (RedisValue)u).ToArray();
var d1 = db.ListLeftPush("group1", redisValue); //2.从右侧添加
db.ListRightPush("group1", "你好6"); //3.从左侧取1个值,并删除
//var v1=db.ListLeftPop("group1"); //4.从右侧取1个值并删除
//var v2 = db.ListRightPop("group1"); //5. 在List的指定的key指定value之前(左边)插入1个值
db.ListInsertBefore("group1", "你好3", "ypf001"); //6. 在List的指定的key指定value之后(右边)插入1个值
db.ListInsertAfter("group1", "你好3", "ypf002"); //7. 获取key指定索引的值(从左往右算)
var d2 = db.ListGetByIndex("group1", );
var d3 = db.ListGetByIndex("group1", ); //8. 获取key的所有数据,数据类型得一致
var d4 = db.ListRange("group1").Select(u => (string)u).ToList();
//获取key的前4条数据(从左往右)
var d44 = db.ListRange("group1", , ).Select(u => (string)u).ToList(); //9.获取指定key的数据的个数
long d5 = db.ListLength("group1"); //10. 删除指定key对应的指定value值,返回删除的个数
db.ListLeftPush("group1", "你好");
db.ListLeftPush("group1", "你好");
long d6 = db.ListRemove("group1", "你好"); //11. 从List1右侧取一个值加到List2左侧,返回的是右侧取出来的这个值
db.ListLeftPush("group2", "你好1");
db.ListLeftPush("group2", "你好2");
db.ListLeftPush("group2", "你好3");
db.ListLeftPush("group3", "哈哈1");
db.ListLeftPush("group3", "哈哈2");
db.ListLeftPush("group3", "哈哈3");
//从队列group2右侧取出来一个值放到group3中
var d7 = db.ListRightPopLeftPush("group2", "group3");

二. 案例分析

(一). 消息队列 (生产者消费者模式)

PS:生产者消费模式:可以是多个生产者,多个消费者,但是生成的数据按顺序进入队列,但是每个数据只能被一个消费者消费。

1. 异步处理

  (1). 将同步业务:创建订单→增加积分→发送短信,改为创建订单后 存放到两个消息队列中(积分队列和短信队列),然后积分业务和短信业务分部去队列中读取,执行各自的业务

  (2). 注册成功发邮件通知:很多场景注册成功后要给用户发一封邮件提示,但这封邮件实时性要求并不是很高,而且发邮件一般是调用第三方接口进行发送,有时候可能会很慢或者故障了, 针对这种情况,借助队列采用生产者消费者模式非常适合。

2. 应用解耦

  将原先订单系统和库存系统的强依赖关系,改为中间引入消息队列,这样二者都依赖消息队列做中介.

3. 流量削锋

  秒杀服务,下单的用户加到队列中,然后开启另外一个线程从队列中读取进行下单,下单成功/失败 利用实时通讯技术通知客户端 或者 客户端主动刷新页面进行查看结果,这里要结合实际架构(单体or集群)分析秒杀情况,不能一概而论,详见后面秒杀章节。

4. 即时通讯

  考虑到同时很多人发送,前端页面的渲染会有点吃不消,这里可以采用 群id 当做队列的key,发送的消息当做value,存入队列中,然后开启一个新的线程从里面读取, 可以一下获取20条,获取的同时并删除,如果队列为空,则休息几秒中,再次获取。

针对群聊的代码分享

  以群聊为例,利用ListLeftPush方法,以“群id”当做key,以发送人id、发送内容、时间组合当做value,从左侧存储到队列;然后利用Core中的BackService类开启后台线程利用ListRightPop 进行读取,同时要在ConfigureService中进行注册。

代码分享:

        /// <summary>
/// 测试群聊页面
/// (PS:不断刷新即可)
/// </summary>
/// <returns></returns>
public IActionResult Index()
{
string userId = Guid.NewGuid().ToString("N");
string msg = "哈哈" + new Random().Next(, );
SendMessage(userId, msg);
return View();
} /// <summary>
///群聊发送消息接口
/// </summary>
/// <param name="userId">用户id</param>
/// <param name="msg">发送的内容</param>
/// <returns></returns>
public string SendMessage(string userId, string msg)
{
try
{
string groupName = "classParty"; //群名
string sendContent = $"{userId}_{msg}_{DateTime.Now}"; //内容
//存入队列
_redis.ListLeftPush(groupName, sendContent);
return "ok";
}
catch (Exception ex)
{
return "error";
}
}

后台服务及注册:

  public class SendService : BackgroundService
{
private readonly IDatabase _redis;
public SendService(RedisHelp redisHelp)
{
_redis = redisHelp.GetDatabase();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
//实际情况,这里有几个群,开几个线程执行
List<string> msgList = new List<string>();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
//要么没有更多待发消息立即发给客户端,要么累积满1秒钟待发消息后发送给客户端
while (true)
{
string msg = _redis.ListRightPop("classParty");
if (!string.IsNullOrEmpty(msg))
{
msgList.Add(msg);
}
else
{
await Task.Delay();
}
//满一段时间的消息向客户端发送
if (stopwatch.Elapsed>TimeSpan.FromSeconds())
{
stopwatch.Stop();
if (msgList.Any())
{
//将这一批消息发送给客户端
//需要重新滞空msgList
}
}
}
}
catch (Exception)
{
throw;
}
}
}
}
  public void ConfigureServices(IServiceCollection services)
{
//注册后台服务
services.AddHostedService<SendService>();
}

(二). 解决查询缓慢问题

  比如发帖网站,会有非常多的帖子,而且数量每日俱增,首页显示的是最新发布的10条帖子,显示的是:发帖人名称 和 发帖标题,如果从数据库中查询可能会非常慢,这时候可以把发帖人名称和

发帖标题(包括帖子id),存到Redis队列中,这个时候利用 栈 的特性,ListGetByIndex:获取key指定索引的值(从左往右算), 获取前10条数据,用于显示,查看详情的时候,再根据帖子的id到数据库中查。

三. 发布订阅模式

 1. 说明

  发布者发布一条消息,所有的订阅者都能收到。

2. 案例背景

  以微博为例(或者微信的订阅号),博主A,博主B都关注了博主C、博主D,在博主A(或B)的版面,应该显示的是博主C和博主D发布的最新博文(最新发布的在最上面),换句话说博主C或者博主D每发一篇博文,都要推送给关注他们的博主A和博主B。

3. 技术分析

(1). 数据结构的设计:一个博主对应一个List链表,用来存储该博主应该显示的博文消息。 以博主的用户id作为key,博文消息的id作为value。

(2). 博主C每发一条博文,就需要向关注他的粉丝(A和B)对应的链表中分别存储一下 该博文消息的id。

(3). 博主D每发一条博文,就需要向关注他的粉丝(A和B)对应的链表中分别存储一下 该博文消息的id。

(4).  博主A就可以到自己对应的链表中利用的特性,获取最新的n条博文消息id。

PS: 拿到博文消息id了,剩下的就容易了,根据id去关系型数据中查内容就很快了,或者也可以将标题或者内容的前100字也存储到Redis中,便于页面显示(这样value的格式就是:博文id-博文标题-博文内容前100字)。

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 

第三节: List类型的介绍、生产者消费者模式、发布订阅模式的更多相关文章

  1. 阶段5 3.微服务项目【学成在线】_day05 消息中间件RabbitMQ_8.RabbitMQ研究-工作模式-发布订阅模式-生产者

    Publish/subscribe:发布订阅模式 发布订阅模式: 1.每个消费者监听自己的队列. 2.生产者将消息发给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将 ...

  2. 阶段5 3.微服务项目【学成在线】_day05 消息中间件RabbitMQ_9.RabbitMQ研究-工作模式-发布订阅模式-消费者

    消费者需要写两个消费者 定义邮件的类 复制以前的代码到邮件类里面进行修改 最上面 声明队列的名称和交换机的名称 监听修改为email 的队列的名称 手机短信接收端 复制一份email的接收端的代码 改 ...

  3. python使用rabbitMQ介绍三(发布订阅模式)

    一.模式介绍 在前面的例子中,消息直接发送到queue中. 现在介绍的模式,消息发送到exchange中,消费者把队列绑定到exchange上. 发布-订阅模式是把消息广播到每个消费者,每个消费者接收 ...

  4. 生产者-消费者模型在Hudi中的应用

    介绍 生产者-消费者模型用于解耦生产者与消费者,平衡两者之间的能力不平衡,该模型广泛应用于各个系统中,Hudi也使用了该模型控制对记录的处理,即记录会被生产者生产至队列中,然后由消费者从队列中消费,更 ...

  5. JMS消息传递类型特点介绍

    对于消息的传递有两种类型: 一种是点对点的,即一个生产者和一个消费者一一对应: 另一种是发布/ 订阅模式,即一个生产者产生消息并进行发送后,可以由多个消费者进 行接收. 特点介绍: 点到点模型点对点传 ...

  6. labview学习——生产者/消费者(数据)(事件)

    其主要的模型: 主要从以下几个方面理解: 1.可重入性 正常的labview是多线程设计语言,而我们在执行VI时的规则是通过VI的命名来分别调用实现的. 打开VI的Highlight调试工具,可以看出 ...

  7. 生产者消费者模型及Golang简单实现

    简介:介绍生产者消费者模型,及go简单实现的demo. 一.生产者消费者模型 生产者消费者模型:某个模块(函数等〉负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类.函数.协程 ...

  8. Java实现生产者消费者问题与读者写者问题

    摘要: Java实现生产者消费者问题与读者写者问题 1.生产者消费者问题 生产者消费者问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从 ...

  9. 4.生产者 消费者模式的RabbitMQ

    1.生产者: using RabbitMQ.Client; using System; using System.Text; namespace Publisher1 { class Program ...

随机推荐

  1. mysql Hash索引和BTree索引区别

    Hash仅支持=.>.>=.<.<=.between.BTree可以支持like模糊查询 索引是帮助mysql获取数据的数据结构.最常见的索引是Btree索引和Hash索引. ...

  2. 【编译系统01】编译器 - 词法分析器(lexial)的设计思路

    时间:2019/11/29 首先,词法分析器由一个扫描器与状态机组成. 一. 词法分析器整体设计流程 二.设计细节 1. code.txt: 我们假设读取下面文本 2.符号类型的设计 我们使用 enu ...

  3. Vue介绍以及模板语法-插值

    1.Vue的介绍 Vue是一套用于构建用户界面的渐进式框架. 注意:Vue是一个框架,相对于jq库来说,是由本质的区别的:https://cn.vuejs.org/ Vue不支持IE8及一下版本,因为 ...

  4. VUE基础实用技巧

    Vue以前听说过,有了解过一点.当时还在热衷于原生JavaScript去写一些方法的封装,不是为啥,就感觉这样很帅,后面多多少少接触了一些JQuery的用法,到现在为止,JavaScript原生封装的 ...

  5. Javase之多线程(2)

    多线程(2) 线程的生命周期 新建:创建线程对象 就绪:有执行资格,没有执行权 运行:有资格运行,有执行权 ​ 阻塞:由一些操作让线程处于改状态.没有执行资格,没有执行权,而通过另一些操作激活它,激活 ...

  6. arcgis api for javascript 学习(六) 地图打印

    1.本文应用arcgis api for javascript对发布的动态地图进行打印,打印的为PDF格式,打印出来如图: 2.需要特别注意的是:我们在运行代码前,需要打开PrintingTools, ...

  7. Linux下用户管理:创建用户指定密码

    首先我们来了解下Linux下用户管理的概念: 如上图所示,左边的一列表示用户名,中间的一列表示用户组,最右边的一列表示的是家目录.用户名我们这里处于简单就,添加了root,xm,xh三个用户.用户组和 ...

  8. Ajax错误

    如果ajax访问不到后台对应的controller的方法,直接报错,首先查看url访问路径,如果路径没错误,再利用谷歌开发者工具来运行一下,是否存在基本语法错误,比如字符写错了,多一个少一个逗号, 如 ...

  9. PWA入门:手把手教你制作一个PWA应用

    摘要: PWA图文教程 原文:PWA入门:手把手教你制作一个PWA应用 作者:MudOnTire Fundebug经授权转载,版权归原作者所有. 简介 Web前端的同学是否想过学习app开发,以弥补自 ...

  10. python如何拼接时间戳到写入的文件名中

    问题描述: 记录一下拼接文件名时间戳的过程,回顾下可能的问题所在,希望能帮到同样碰到这类问题的兄dei. 进行单元测试时,最后使用HTMLTestRunner生成的HTML分析报告,需要添加一个时间戳 ...