一. 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. okhttp浅析

    转载自:http://www.ishenping.com/ArtInfo/69561.html 1.okhttp工作的大致流程 1.1.整体流程 (1).当我们通过OkhttpClient创建一个Ca ...

  2. kfifo

    kfifo 的一些伪代码 kfifo_len() out = LOAD fifo->out smp_rmb() len = LOAD fifo->in - out kfifo_in() k ...

  3. Nginx反向代理Tomcat静态资源无法加载以及请求链接错误

     在使用Nginx实现Tomcat的负载均衡的时候,项目发布到了Tomcat,Nginx也配置好了, 当访问的时候发现了与预期不符 表现为: 静态资源加载失败 链接跳转地址错误 下面是我错误的配置文件 ...

  4. EF-入门操作

    EntityFramework Core 理解 DbContext :数据库 DbSet: 数据库表 Model : 数据行 IQueryable<Model> 查询结果集合 Lamada ...

  5. MySQL学习——查看数据库信息

    MySQL学习——查看数据库信息 摘要:本文主要学习了查看数据库信息的方法. 查询指定表的索引 语法 show index from 表名; 示例 mysql> show index from ...

  6. .NET MVC后台获得VIEW对应的html

    一..Net Core Mvc下获得 建立一个帮助类,如下: using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Render ...

  7. 按需动态加载js

    有些时间我们希望能按需动态加载js文件,而不是直接在HTML中写script标签. 以下为示例代码: var js = document.createElement('script'); js.asy ...

  8. JavaScript深入浅出第1课:箭头函数中的this究竟是什么鬼?

    <JavaScript 深入浅出>系列: JavaScript 深入浅出第 1 课:箭头函数中的 this 究竟是什么鬼? JavaScript 深入浅出第 2 课:函数是一等公民是什么意 ...

  9. MD5 加密工具类MD5Util

    我们在使用MD5 在线加密的时候,会发现下面情况,大小写的区别就不说啦,那么16位和32位有啥区别呢,其实16 位实际上是从 32 位字符串中,取中间的第 9 位到第 24 位的部分,就是str.su ...

  10. 【转载】Kafka史上最详细原理总结

    Kafka是最初由Linkedin公司开发,是一个分布式.支持分区的(partition).多副本的(replica),基于zookeeper协调的分布式消息系统,它的最大的特性就是可以实时的处理大量 ...