.NET Core 使用 Channel 消息队列

背景

最近做一个项目,连接了很多设备,需要保存设备的心跳数据,刚开始的做法是直接接收到设备的数据之后进行心跳数据的保存,但是随着设备多了起来,然后设备的使用时长不断的加大,对数据库的压力也比较大,所以想着优化一下。

方案调研

1、使用第三方中间件

常见的使用redis,或者mq,只需要不断的向中间件发送数据即可,redis使用队列,如果是mq直接发送消息即可,使用起来简单方便,但是要引入这些中间件,目前的架构里面没有,需要自己去起服务,维护。

2、使用channel

System.Threading.Channels 是.NET Core 3.0 后推出的新的集合类型, 具有异步API,高性能,线程安全等特点,它可以用来做消息队列,进行数据的生产和消费, 公开的 Writer 和 Reader api对应消息的生产者和消费者,也让Channel更加的简洁和易用,与Rabbit MQ 等其他队列不同的是,Channel 是进程内的队列

目前就介绍来看非常完美,不需要添加第三方中间件,直接添加现有的模块即可。

代码实现

选择了使用channel来做优化。拿到设备数据之后直接把消息丢入到channel,然后后台使用定时任务或者自己实现hostservice去不断的消费数据。

生产者代码

public async Task ProduceHeartBeat(string message)
{
    await channel.Writer.WriteAsync(message);
}

不断的向里面写入数据即可.

消费者代码

/// <summary>
/// timespan时间内消费多少数据
/// </summary>
/// <param name="count"></param>
/// <param name="timeSpan"></param>
/// <returns></returns>
public async Task<List<string>> ConsumeHeartBeatAsync(int count,TimeSpan timeSpan)
{
     var result = new List<string>(count);
     CancellationTokenSource cts = new CancellationTokenSource();
     var cancellationToken = cts.Token;
     cts.CancelAfter(timeSpan);
     int rcount = 0;
     while ( !cancellationToken.IsCancellationRequested && rcount<count)
     {
         //await Task.Delay(2000);
         if (channel.Reader.TryRead(out var number))
         {
             Console.WriteLine(number);
             result.Add(number);
             rcount++;
         }
         else
         {
             break;
         } 
     }  
    return result;
}

里面加入了一个cancellationToken,进行消费的时长限制。在此时长内消费多少条数据,超时直接结束。

这就是基本的代码

后台定时消费数据

public class HeartBeatService : BackgroundService
{
     private readonly HeartBeatsChannel heartBeatsChannel;

     public HeartBeatService(HeartBeatsChannel heartBeatsChannel)
     {
         this.heartBeatsChannel = heartBeatsChannel;
     }

     protected override async Task ExecuteAsync(CancellationToken stoppingToken)
     {
         try
         {

             Task.Factory.StartNew(() =>
             {
                 while (!stoppingToken.IsCancellationRequested)
                 {
                     //阻塞的队列使得一直在同一个线程运行
                     Process(15,heartBeatsChannel).Wait();
                 }

             }, TaskCreationOptions.LongRunning);

             Console.WriteLine("主线程 现在运行的线程id为:" + Thread.CurrentThread.ManagedThreadId);

             }
         catch (Exception ex)
         {
             Console.WriteLine(ex.ToString());
         }
     }
     /// <summary>
     /// 消费数据
     /// </summary>
     /// <param name="count">一次消费数量</param>
     /// <param name="heartBeatsChannel"></param>
     /// <returns></returns>
     private async Task Process(int count ,HeartBeatsChannel heartBeatsChannel)
     {
         Console.WriteLine("子线程_现在运行的线程id为:" + Thread.CurrentThread.ManagedThreadId);
         //每次消费三十个
         if (heartBeatsChannel.IsHasContent)
         {
             //int count = 15;
             //进行消费
             await heartBeatsChannel.ConsumeHeartBeatAsync(count, TimeSpan.FromSeconds(3));
         }           
         await Task.Delay(3000);
     }
}

使用的是BackgroundServic,直接实现要处理的业务逻辑就好了。

在这里使用的是TaskCreationOptions.LongRunning,新开一个线程去处理心跳数据。

总结

以上就是主要的实现全过程,完整的代码在github

https://github.com/lackguozi/LearnChannelWebApi

实际上完全可以不用后台去定时消费数据,channel有很多api可以去处理,比如WaitToReadAsync(),但是这里没有使用,主要是不想持续的占数据库资源?

总结的话学习了channel的用法,底层似乎使用了deque?只稍微看了下源码,但是看到了许多的lock,这个是必不可少的。还是巨硬轮子造的好

转自:果小天

链接:cnblogs.com/guoxiaotian/p/17506536.html

版权声明:本文来源于网友收集或网友供稿,仅供学习交流之用,如果有侵权,请转告小编或者留言,本公众号立即删除。

- EOF -

技术群:添加小编微信dotnet999

公众号:Dotnet讲堂 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是使用 RabbitMQ 消息队列.NET Core Demo: 首先,需要创建一个 .NET Core Console 应用程序。在控制台中,使用以下命令: ``` dotnet new console -o RabbitMQDemo cd RabbitMQDemo ``` 接着,需要安装 RabbitMQ 客户端库:RabbitMQ.Client。使用以下命令: ``` dotnet add package RabbitMQ.Client ``` 在代码中,我们可以使用以下代码来连接到 RabbitMQ,并向队列发送消息: ```csharp using RabbitMQ.Client; using System; using System.Text; class Program { static void Main(string[] args) { var factory = new ConnectionFactory() { HostName = "localhost" }; using (var connection = factory.CreateConnection()) using (var channel = connection.CreateModel()) { channel.QueueDeclare(queue: "hello", durable: false, exclusive: false, autoDelete: false, arguments: null); string message = "Hello World!"; var body = Encoding.UTF8.GetBytes(message); channel.BasicPublish(exchange: "", routingKey: "hello", basicProperties: null, body: body); Console.WriteLine(" [x] Sent {0}", message); } Console.WriteLine(" Press [enter] to exit."); Console.ReadLine(); } } ``` 在上面的代码中,我们使用了 ConnectionFactory 类来创建一个连接到 RabbitMQ 的连接。然后,我们使用连接创建一个 channel,并使用 channel.QueueDeclare() 方法来声明一个名为 "hello" 的队列。 接着,我们将要发送的消息编码成字节数组,并使用 channel.BasicPublish() 方法将消息发布到队列中。 最后,我们使用 Console.ReadLine() 方法来等待用户按下回车键,以便程序可以安全地退出。 在另一个控制台中,我们可以使用以下代码来接收队列中的消息: ```csharp using RabbitMQ.Client; using RabbitMQ.Client.Events; using System; using System.Text; class Program { static void Main(string[] args) { var factory = new ConnectionFactory() { HostName = "localhost" }; using (var connection = factory.CreateConnection()) using (var channel = connection.CreateModel()) { channel.QueueDeclare(queue: "hello", durable: false, exclusive: false, autoDelete: false, arguments: null); var consumer = new EventingBasicConsumer(channel); consumer.Received += (model, ea) => { var body = ea.Body.ToArray(); var message = Encoding.UTF8.GetString(body); Console.WriteLine(" [x] Received {0}", message); }; channel.BasicConsume(queue: "hello", autoAck: true, consumer: consumer); Console.WriteLine(" Press [enter] to exit."); Console.ReadLine(); } } } ``` 在上面的代码中,我们同样使用 ConnectionFactory 创建连接和 channel。然后,我们使用 channel.QueueDeclare() 方法声明名为 "hello" 的队列。 接着,我们使用 EventingBasicConsumer 类创建一个消费者,并在 Received 事件处理程序中输出收到的消息。 最后,我们使用 channel.BasicConsume() 方法开始消费队列中的消息,并使用 Console.ReadLine() 方法等待用户按下回车键以退出程序。 这就是使用 RabbitMQ 客户端库的 .NET Core 消息队列 Demo。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值