几种队列
c#中自带的队列
使用C#自带的队列,一般会把进行队列监听的代码放于Global.asax之类的文件或寄宿windows服务之中,与应用服务器在同一台,会抢占服务器资源。后面会介绍使用其他分布式队列。
来一个简单的示例:
队列帮助类
namespace queue { public sealed class QueueHelper { private static QueueHelper queue; Queue<People> queueobj = new Queue<People>(); public static QueueHelper instance { get { if (queue==null) { queue = new QueueHelper(); } return queue; } } /// <summary> /// 向队列中添加数据 /// </summary> /// <param name="Id"></param> /// <param name="title"></param> /// <param name="content"></param> public void AddQueue(int id,int age, string name) { queueobj.Enqueue(new People() { ID=id, Age = age, Name=name }); } /// <summary> /// 当前队列数据量 /// </summary> /// <returns></returns> public int Count() { return queueobj.Count(); } //启动一个线程去监听队列 public void StartQueue() { Task t = Task.Run(() => { ScanQueue(); }); } private void ScanQueue() { while (true) { if (queueobj.Count > 0) { try { //从队列中取出 People queueinfo = queueobj.Dequeue(); Console.WriteLine($"取得队列:ID={queueinfo.ID},name={queueinfo.Name},age={queueinfo.Age}"); } catch (Exception ex) { throw; } } else { Console.WriteLine("没有数据,先休眠5秒在扫描"); Thread.Sleep(5000); } } } } }
Main方法
static void Main(string[] args) { Console.WriteLine("当前队列数:"+ QueueHelper.instance.Count()); Console.WriteLine("开启线程扫描"); QueueHelper.instance.StartQueue(); Thread.Sleep(10000); QueueHelper.instance.AddQueue(1,25,"小王"); QueueHelper.instance.AddQueue(2, 28, "小明"); QueueHelper.instance.AddQueue(3, 99, "大爷"); Console.ReadLine(); }
运行结果
redis队列
对可靠性和稳定性要求不高的应用场景,可以使用redis简单方便的实现
关于redis队列的实现方式有两种:
1、生产者消费者模式(List)。
2、发布者订阅者模式(pub/sub)。
驱动:StackExchange.Redis
以下为第一种方式示例
RedisHelper帮助类
public class RedisHelper { // redis实例 private static RedisHelper instance = null; private IDatabase db; private ConnectionMultiplexer redis; private IServer redisServer; private readonly string _enqueueName = "PersonObj"; /// <summary> /// 静态单例方法 /// </summary> /// <returns></returns> public static RedisHelper Get() { if (instance == null) { instance = new RedisHelper(); } return instance; } /// <summary> /// 无参数构造函数 /// </summary> private RedisHelper() { var redisConnection = "127.0.0.1:6379"; redis = ConnectionMultiplexer.Connect(redisConnection); redisServer = redis.GetServer(redisConnection); db = redis.GetDatabase(); } /// <summary> /// 入队 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="enqueueName">队列名称</param> /// <param name="value"></param> public void EnqueueItem<T>( T value) { //序列化 var valueString = JsonConvert.SerializeObject(value); db.ListLeftPushAsync(_enqueueName, valueString); } /// <summary> /// 出队 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="enqueueName">队列名称</param> /// <returns></returns> public T DequeueItem<T>() { var valueString = db.ListRightPopAsync(_enqueueName).Result;
//反序列化 T obj = JsonConvert.DeserializeObject<T>(valueString); return obj; } /// <summary> /// 当前队列数据量 /// </summary> /// <param name="enqueueName"></param> /// <returns></returns> public long Count() { return db.ListLengthAsync(_enqueueName).Result; } //启动一个线程去监听队列 public void StartQueue() { Task t = Task.Run(() => { ScanQueue(); }); } private void ScanQueue() { while (true) { if (this.Count() > 0) { try { //从队列中取出 Person queueinfo = DequeueItem<Person>(); Console.WriteLine($"取得队列:name={queueinfo.Name},age={queueinfo.Age}"); } catch (Exception ex) { throw; } } else { Console.WriteLine("没有数据,先休眠5秒在扫描"); Thread.Sleep(5000); } } } }
Main方法:
static void Main(string[] args) { Console.WriteLine($"当前队列数{RedisHelper.Get().Count()}"); Console.WriteLine("开启线程扫描"); RedisHelper.Get().StartQueue(); Thread.Sleep(10000); RedisHelper.Get().EnqueueItem<Person>(new Person { Name="小王",Age=20}); RedisHelper.Get().EnqueueItem<Person>(new Person { Name = "小明", Age = 20 }); Console.WriteLine($"当前队列数{RedisHelper.Get().Count()}"); Console.ReadLine(); }
运行结果
RabbitMQ
RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java等,且支持AJAX。用于在分布式系统中存储转发消息,具体特点包括易用性、扩展性、高可用性、消息集群等
关于RabbitMQ的教程可以参考:https://www.cnblogs.com/julyluo/p/6262553.html
安装教程可以参考:https://www.cnblogs.com/ericli-ericli/p/5902270.html
RabbitMq默认的监听端口是15672
public class RabbitmqHelper { private static RabbitmqHelper instance; private readonly ConnectionFactory rabbitMqFactory; //交换器名称 const string ExchangeName = "myExchange"; //当前消息队列名称 const string QueueName = "myFirstQueue"; public RabbitmqHelper() { //没有设置账号密码端口或服务端没有先开启权限会报:None of the specified endpoints were reachable rabbitMqFactory = new ConnectionFactory { HostName = "192.168.0.112" }; rabbitMqFactory.Port = 5672; rabbitMqFactory.UserName = "123456"; rabbitMqFactory.Password = "123456"; } /// <summary> /// 静态单例方法 /// </summary> /// <returns></returns> public static RabbitmqHelper Get() { if (instance == null) { instance = new RabbitmqHelper(); } return instance; } /// <summary> /// 生产者-发布消息 /// </summary> /// <param name="msg">消息</param> public void Enqueue(string msg) { using (IConnection conn = rabbitMqFactory.CreateConnection()) { using (IModel channel = conn.CreateModel()) { //定义交换器 channel.ExchangeDeclare(ExchangeName, "direct", durable: true, autoDelete: false, arguments: null); //持久化一个队列,如果名称相同不会重复创建 channel.QueueDeclare(QueueName, true, false, false, null); //定义exchange到queue的binding channel.QueueBind(QueueName, ExchangeName, routingKey: QueueName); byte[] bytes = Encoding.UTF8.GetBytes(msg); //设置消息持久化 IBasicProperties properties = channel.CreateBasicProperties(); properties.Persistent = true; channel.BasicPublish("", QueueName, properties, bytes); } } } /// <summary> /// 消费者-接收消息-基于订阅模式 /// </summary> /// <param name="msg">消息</param> public void Dequeue() { IConnection conn = rabbitMqFactory.CreateConnection(); IModel channel = conn.CreateModel(); //持久化一个队列,如果名称相同不会重复创建 channel.QueueDeclare(QueueName, durable: true, autoDelete: false, exclusive: false, arguments: null); //告诉broker同一时间只处理一个消息 channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false); //因处理性能问题,已被官方弃用 //var consumer2 = new QueueingBasicConsumer(channel) //var msgResponse = consumer.Queue.Dequeue(); var consumer = new EventingBasicConsumer(channel); consumer.Received += (model, ea) => { try { var msgBody = Encoding.UTF8.GetString(ea.Body); Console.WriteLine($"收到消息:{msgBody}"); //处理完成,服务端可以删除消息了同时分配新的消息 channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false); } catch (Exception e) { Console.WriteLine($"出现错误:{e.Message}"); } }; //noAck设置false,发送消息之后,消息不要主动删除,先等消费者处理完 channel.BasicConsume(QueueName, false, consumer); } /// <summary> /// 消费者-接收消息-主动拉取 /// </summary> /// <param name="msg">消息</param> public void Dequeue2() { using (IConnection conn = rabbitMqFactory.CreateConnection()) { using (IModel channel = conn.CreateModel()) { while (true) { BasicGetResult res = channel.BasicGet(QueueName, false/*noAck*/); if (res != null) { var msg = System.Text.UTF8Encoding.UTF8.GetString(res.Body); Console.WriteLine($"获取到消息:{msg}"); channel.BasicAck(res.DeliveryTag, false); } } } } } }