前言

这是一篇拖更很久的博客,不知不觉InitQ在nuget下载量已经过15K了,奈何胸无点墨也不晓得怎么写(懒),随便在github上挂了个md,现在好好唠唠如何在redis里使用队列



队列缓存分布式 异步调优堆配置 ------(来自某位不知名码友)

诞生背景

redis在项目中使用的越来越频繁,通常我们是用来做缓存,使用较多的就是String,Hash这两种类型,以及分布式锁,redis的List类型,就可以用于消息队列,使用起来更加简单,且速度更快,非常适合子服务内部之间的消息流转,创造灵感来自于杨老板的CAP(地址:https://www.cnblogs.com/tibos/p/11858095.html),采用注解的方式消费队列,让业务逻辑更加的清晰,方便维护

安装环境

  • .net core版本:2.1
  • redis版本:3.0以上

特点

1.通过注解的方式,订阅队列
2.可以设置消费消息的频次
3.支持消息广播
4.支持延迟队列

使用介绍

  • 1.获取initQ包

    方案A. install-package InitQ

    方案B. nuget包管理工具搜索 InitQ

  • 2.添加中间件(该中间件依赖 StackExchange.Redis)

    services.AddInitQ(m=>
    {
    m.SuspendTime = 1000;
    m.IntervalTime = 1000;
    m.ConnectionString = "127.0.0.1,connectTimeout=15000,syncTimeout=5000,password=123456";
    m.ListSubscribe = new List<Type>() { typeof(RedisSubscribeA), typeof(RedisSubscribeB) };
    m.ShowLog = false;
    });
  • 3.配置说明

    public class InitQOptions
    {
    /// <summary>
    /// redis连接字符串
    /// </summary>
    public string ConnectionString { get; set; } /// <summary>
    /// 没消息时挂起时长(毫秒)
    /// </summary>
    public int SuspendTime { get; set; } /// <summary>
    /// 每次消费消息间隔时间(毫秒)
    /// </summary>
    public int IntervalTime { get; set; } /// <summary>
    /// 是否显示日志
    /// </summary>
    public bool ShowLog { get; set; } /// <summary>
    /// 需要注入的类型
    /// </summary>
    public IList<Type> ListSubscribe { get; set; } public InitQOptions()
    {
    ConnectionString = "";
    IntervalTime = 0;
    SuspendTime = 1000;
    ShowLog = false;
    }
    }

消息发布/订阅

消息的发布/订阅是最基础的功能,这里做了几个优化

  1. 采用的是长轮询模式,可以控制消息消费的频次,以及轮询空消息的间隔,避免资源浪费
  2. 支持多个类订阅消息,可以很方便的根据业务进行分类,前提是这些类 必须注册
  3. 支持多线程消费消息(在执行耗时任务的时候,非常有用)

示例如下(Thread.Sleep):

    public class RedisSubscribeA: IRedisSubscribe
{
[Subscribe("tibos_test_1")]
private async Task SubRedisTest(string msg)
{
Console.WriteLine($"A类--->当前时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 订阅者A消费消息:{msg}");
Thread.Sleep(3000); //使用堵塞线程模式,同步延时
Console.WriteLine($"A类<---当前时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 订阅者A消费消息:{msg} 完成");
}
}

    public class RedisSubscribeA: IRedisSubscribe
{
[Subscribe("tibos_test_1")]
private async Task SubRedisTest(string msg)
{
Console.WriteLine($"A类--->当前时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 订阅者A消费消息:{msg}");
Thread.Sleep(3000); //使用堵塞线程模式,同步延时
Console.WriteLine($"A类<---当前时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 订阅者A消费消息:{msg} 完成");
}
[Subscribe("tibos_test_1")]
private async Task SubRedisTest2(string msg)
{
Console.WriteLine($"A类--->当前时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 订阅者A消费消息:{msg}");
Thread.Sleep(3000); //使用堵塞线程模式,同步延时
Console.WriteLine($"A类<---当前时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 订阅者A消费消息:{msg} 完成");
}
}

示例如下(Task.Delay):

    [Subscribe("tibos_test_1")]
private async Task SubRedisTest(string msg)
{
Console.WriteLine($"A类--->当前时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 订阅者A消费消息:{msg}");
await Task.Delay(3000); //使用非堵塞线程模式,异步延时
Console.WriteLine($"A类<---当前时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 订阅者A消费消息:{msg} 完成");
}

根据业务情况,合理的选择堵塞模式

  • 1.订阅发布者
      using (var scope = _provider.GetRequiredService<IServiceScopeFactory>().CreateScope())
    {
    //redis对象
    var _redis = scope.ServiceProvider.GetService<ICacheService>();
    //循环向 tibos_test_1 队列发送消息
    for (int i = 0; i < 1000; i++)
    {
    await _redis.ListRightPushAsync("tibos_test_1", $"我是消息{i + 1}号");
    }
    }
  • 2.定义消费者类 RedisSubscribeA
    public class RedisSubscribeA: IRedisSubscribe
    {
    [Subscribe("tibos_test_1")]
    private async Task SubRedisTest(string msg)
    {
    Console.WriteLine($"A类--->订阅者A消息消息:{msg}");
    } [Subscribe("tibos_test_1")]
    private async Task SubRedisTest1(string msg)
    {
    Console.WriteLine($"A类--->订阅者A1消息消息:{msg}");
    } [Subscribe("tibos_test_1")]
    private async Task SubRedisTest2(string msg)
    {
    Console.WriteLine($"A类--->订阅者A2消息消息:{msg}");
    } [Subscribe("tibos_test_1")]
    private async Task SubRedisTest3(string msg)
    {
    Console.WriteLine($"A类--->订阅者A3消息消息:{msg}");
    }
    }
  • 3.定义消费者类 RedisSubscribeB
    public class RedisSubscribeB : IRedisSubscribe
    {
    /// <summary>
    /// 测试
    /// </summary>
    /// <param name="msg"></param>
    /// <returns></returns>
    [Subscribe("tibos_test_1")]
    private async Task SubRedisTest(string msg)
    {
    Console.WriteLine($"B类--->订阅者B消费消息:{msg}");
    }
    }

消息广播/订阅

消息广播是StackExchange.Redis已经封装好的,我们只用起个线程监听即可,只要监听了这个key的线程,都会收到消息

  • 1.订阅消息通道,订阅者需要在程序初始化的时候启动一个线程侦听通道,这里使用HostedService来实现,并注册到容器
      public class ChannelSubscribeA : IHostedService, IDisposable
    {
    private readonly IServiceProvider _provider;
    private readonly ILogger _logger; public ChannelSubscribeA(ILogger<TestMain> logger, IServiceProvider provider)
    {
    _logger = logger;
    _provider = provider;
    }
    public void Dispose()
    {
    _logger.LogInformation("退出");
    } public Task StartAsync(CancellationToken cancellationToken)
    {
    _logger.LogInformation("程序启动");
    Task.Run(async () =>
    {
    using (var scope = _provider.GetRequiredService<IServiceScopeFactory>().CreateScope())
    {
    //redis对象
    var _redis = scope.ServiceProvider.GetService<ICacheService>();
    await _redis.SubscribeAsync("test_channel", new Action<RedisChannel, RedisValue>((channel, message) =>
    {
    Console.WriteLine("test_channel" + " 订阅服务A收到消息:" + message);
    })); }
    });
    return Task.CompletedTask;
    } public Task StopAsync(CancellationToken cancellationToken)
    {
    _logger.LogInformation("结束");
    return Task.CompletedTask;
    }
    }
      public class ChannelSubscribeB : IHostedService, IDisposable
    {
    private readonly IServiceProvider _provider;
    private readonly ILogger _logger; public ChannelSubscribeB(ILogger<TestMain> logger, IServiceProvider provider)
    {
    _logger = logger;
    _provider = provider;
    }
    public void Dispose()
    {
    _logger.LogInformation("退出");
    } public Task StartAsync(CancellationToken cancellationToken)
    {
    _logger.LogInformation("程序启动");
    Task.Run(async () =>
    {
    using (var scope = _provider.GetRequiredService<IServiceScopeFactory>().CreateScope())
    {
    //redis对象
    var _redis = scope.ServiceProvider.GetService<ICacheService>();
    await _redis.SubscribeAsync("test_channel", new Action<RedisChannel, RedisValue>((channel, message) =>
    {
    Console.WriteLine("test_channel" + " 订阅服务B收到消息:" + message);
    })); }
    });
    return Task.CompletedTask;
    } public Task StopAsync(CancellationToken cancellationToken)
    {
    _logger.LogInformation("结束");
    return Task.CompletedTask;
    }
    }
  • 2.将HostedService类注入到容器
      services.AddHostedService<ChannelSubscribeA>();
    services.AddHostedService<ChannelSubscribeB>();
  • 3.广播消息
      using (var scope = _provider.GetRequiredService<IServiceScopeFactory>().CreateScope())
    {
    //redis对象
    var _redis = scope.ServiceProvider.GetService<ICacheService>();
    for (int i = 0; i < 1000; i++)
    {
    await _redis.PublishAsync("test_channel", $"往通道发送第{i}条消息");
    }
    }

延迟消息

延迟消息非常适用处理一些定时任务的场景,如订单15分钟未付款,自动取消, xxx天后,自动续费...... 这里使用zset+redis锁来实现,这里的操作方式,跟发布/定义非常类似

写入延迟消息:SortedSetAddAsync

注解使用:SubscribeDelay

  • 1.定义发布者

      Task.Run(async () =>
    { using (var scope = _provider.GetRequiredService<IServiceScopeFactory>().CreateScope())
    {
    //redis对象
    var _redis = scope.ServiceProvider.GetService<ICacheService>(); for (int i = 0; i < 100; i++)
    {
    var dt = DateTime.Now.AddSeconds(3 * (i + 1));
    //key:redis里的key,唯一
    //msg:任务
    //time:延时执行的时间
    await _redis.SortedSetAddAsync("test_0625", $"延迟任务,第{i + 1}个元素,执行时间:{dt.ToString("yyyy-MM-dd HH:mm:ss")}", dt);
    }
    }
    });
  • 2.定义消费者

      //延迟队列
    [SubscribeDelay("test_0625")]
    private async Task SubRedisTest1(string msg)
    {
    Console.WriteLine($"A类--->当前时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 订阅者延迟队列消息开始--->{msg}");
    //模拟任务执行耗时
    await Task.Delay(TimeSpan.FromSeconds(3));
    Console.WriteLine($"A类--->{msg} 结束<---");
    }

版本

  • V1.0 更新时间:2019-12-30

版本库:

作者:提伯斯

.net core Redis消息队列中间件【InitQ】的更多相关文章

  1. Delayer 基于 Redis 的延迟消息队列中间件

    Delayer 基于 Redis 的延迟消息队列中间件,采用 Golang 开发,支持 PHP.Golang 等多种语言客户端. 参考 有赞延迟队列设计 中的部分设计,优化后实现. 项目链接:http ...

  2. java-spring基于redis单机版(redisTemplate)实现的分布式锁+redis消息队列,可用于秒杀,定时器,高并发,抢购

    此教程不涉及整合spring整合redis,可另行查阅资料教程. 代码: RedisLock package com.cashloan.analytics.utils; import org.slf4 ...

  3. 基于硬件的消息队列中间件 Solace 简介之二

    前言...... 前面简单介绍了Solace来自于哪家公司, 主要能做哪些事情. 本篇主要进一步介绍Solace作为消息传递的中间件如何工作的. 传统意义上来讲, 每当我们谈到消息中间件时, 首先想到 ...

  4. redis消息队列简单应用

    消息队列出现的原因 随着互联网的高速发展,门户网站.视频直播.电商领域等web应用中,高并发.大数据已经成为基本的标识.淘宝双11.京东618.各种抢购.秒杀活动.以及12306的春运抢票等,他们这些 ...

  5. c#开源消息队列中间件EQueue 教程

    一.简介 EQueue是一个参照RocketMQ实现的开源消息队列中间件,兼容Mono,具体可以参看作者的文章<分享一个c#写的开源分布式消息队列equeue>.项目开源地址:https: ...

  6. 常用的消息队列中间件mq对比

    原文地址:https://blog.csdn.net/qq_30764991/article/details/80239076 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量 ...

  7. logstash解耦之redis消息队列

    logstash解耦之redis消息队列 架构图如下: 说明:通过input收集日志消息放入消息队列服务中(redis,MSMQ.Resque.ActiveMQ,RabbitMQ),再通过output ...

  8. 预热一下吧《实现Redis消息队列》

    应用场景 为什么要用redis?二进制存储.java序列化传输.IO连接数高.连接频繁 一.序列化 这里编写了一个java序列化的工具,主要是将对象转化为byte数组,和根据byte数组反序列化成ja ...

  9. nodejs一个函数实现消息队列中间件

    消息队列中间件(Message Queue)相信大家不会陌生,如Kafka.RabbitMQ.RocketMQ等,已经非常成熟,在大大小小的公司和项目中也已经广泛使用. 有些项目中,如果是只使用初步的 ...

随机推荐

  1. Java_常用类API之一

    Math类 Math类中包含一些对数据进行数学运算的方法,而该类中的方法全都是静态的.像这样的类称之为工具类. 1 public static int abs(int a) 2 对一个数据求绝对值 3 ...

  2. python模块导入原理

    转自:http://blog.csdn.net/u012422440/article/details/41791433 今日在自学Python,借此机会,正好重新开始写博文,既可以巩固python的知 ...

  3. 有哪些适合中小企业使用的PaaS平台?

    对于中小企业来说,在业务上同样需要工作流.应用平台来进行支持,但是,面对诸如ERP等动辄好几十万的费用来说,完全是在增加运营成本.如何解决中小企业对于业务应用.工作流管理的需求问题呢?使用PaaS低代 ...

  4. [DB] Hadoop免密登录原理及设置

    情景: 现有两台电脑bigdata111.bigdata112,bigdata111想免密码登录bigdata112 过程: 1.bigdata111生成公钥(用于加密,给别人)和私钥(用于解密,自己 ...

  5. Docker——Registry 通过Shell管理私有仓库镜像

    使用方法: 复制代码保存为 image_registry.sh sh image_registry.sh  -h   #查看帮助 HUB=10.0.29.104:5000 改为自己的地址 #!/bin ...

  6. 关于STM32的可编程电压检测器的使用方法

    关于STM32的可编程电压检测器的使用方法 思维导图总览: 代码: 1 #include "sys.h" 2 #include "delay.h" 3 #inc ...

  7. linux 系统监控命令之 top-(转自 Howie的专栏)

    top命令经常用来监控linux的系统状况,比如cpu.内存的使用,程序员基本都知道这个命令,但比较奇怪的是能用好它的人却很少,例如top监控视图中内存数值的含义就有不少的曲解. 本文通过一个运行中的 ...

  8. 10.16-17 mailq&mail:显示邮件传输队列&发送邮件

    mailq命令 是mail queue(邮件队列)的缩写,它会显示待发送的邮件队列,显示的条目包括邮件队列ID.邮件大小.加入队列时间.邮件发送者和接受者.如果邮件进行最后一次尝试后还没有将邮件投递出 ...

  9. RSA算法在Python Django中的简单应用

    说明 RSA算法是当今使用最广泛,安全度最高的加密算法. • RSA算法的安全性理论基础 [引]根据百科介绍,对极大整数做因数分解的难度决定了RSA算法的可靠性.换言之,对一极大整数做因数分解愈困难, ...

  10. 安装JDK 常见错误解决(Day_07)

    在cmd中输入java -version或者java 或出现以下错误: 原因一:可能是你的JDK装的时间比较早,导致环境变量中的Path(此电脑->右击属性->高级系统设置->环境变 ...