几种队列

Posted qiuguochao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了几种队列相关的知识,希望对你有一定的参考价值。

 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);
                        }
                    }
                }
            }


        }
    }
View Code

 

以上是关于几种队列的主要内容,如果未能解决你的问题,请参考以下文章

Laravel:如何在控制器的几种方法中重用代码片段

几种队列

一张图,理顺 Spring Boot应用在启动阶段执行代码的几种方式

一张图,理顺 Spring Boot应用在启动阶段执行代码的几种方式

一张图帮你记忆,Spring Boot 应用在启动阶段执行代码的几种方式

HTML代码片段