欢迎来到【一个大西瓜】的博客

不曾为梦想奋斗,拿什么去燃烧青春。有梦之人亦终将老去,但少年心气如昨。
太阳每一个时刻都同时是夕阳和朝阳,每天她沉入西边,意味着她同时从另一面土地升起。
扩大
缩小

RabbitMQ消息队列(十一)-如何实现高可用

在前面讲到了RabbitMQ高可用集群的搭建,但是我们知道只是集群的高可用并不能保证应用在使用消息队列时完全没有问题,例如如果应用连接的RabbitMQ集群突然宕机了,虽然这个集群时可以使用的,但是应用订阅的连接就断开了,如果有个机房外网出口带宽被挖掘机弄断了,那集群依然是不可用的。所以我们后面会介绍应用APP如何与连接集群来保证两者配合默契,以及如何实现跨机房的集群复制。

应用连接集群高可用

前面讲到应用服务器通过一个负载均衡服务将连接的流量分发到指定服务器,如果连接的节点宕机怎么办呢。应用服务器连接集群主要做两件事,订阅和发布,所以如果是发布消息每次都会重新初始化连接所以连接节点的切换对整个系统的可用性影响不大。如果是订阅消息就没有真么简单了。首先我们要做到如果连接出现问题应该是抛出异常而不是终止脚本,并且这时应该重新连接连接。 
好了不废话了,代码如下:

ColonyProduct

using System;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace ColonyProduct
{
    class Program
    {
        static void Main(string[] args)
        {
            String exchangeName = "wytExchange";
            String queueName = "wytQueue";

            ConnectionFactory factory = new ConnectionFactory();
            factory.HostName = "192.168.63.130";
            factory.Port = 5672;
            factory.VirtualHost = "/wyt";
            factory.UserName = "wyt";
            factory.Password = "wyt";

            using (IConnection connection=factory.CreateConnection())
            {
                using (IModel channel=connection.CreateModel())
                {
                    channel.ExchangeDeclare(exchange: exchangeName, type: ExchangeType.Direct, durable: true, autoDelete: false, arguments: null);

                    channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);

                    channel.QueueBind(queue: queueName, exchange: exchangeName, routingKey: String.Empty, arguments: null);

                    IBasicProperties properties = channel.CreateBasicProperties();
                    properties.Persistent = true;

                    for (int i = 0; i < 10; i++)
                    {
                        Byte[] body = Encoding.UTF8.GetBytes("Hello World -- "+i);

                        channel.BasicPublish(exchange: exchangeName, routingKey: String.Empty, basicProperties: properties, body: body);
                    }
                }
            }

            Console.WriteLine("发送完成");
            Console.ReadKey();
        }
    }
}
View Code

ColonyConsumer

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

namespace ColonyConsumer
{
    class Program
    {
        static void Main(string[] args)
        {
            String exchangeName = "wytExchange";
            String queueName = "wytQueue";

            ConnectionFactory factory = new ConnectionFactory();
            factory.HostName = "192.168.63.133";
            factory.Port = 5672;
            factory.VirtualHost = "/wyt";
            factory.UserName = "wyt";
            factory.Password = "wyt";

            using (IConnection connection=factory.CreateConnection())
            {
                using (IModel channel=connection.CreateModel())
                {
                    channel.ExchangeDeclare(exchange: exchangeName, type: ExchangeType.Direct, durable: true, autoDelete: false, arguments: null);

                    channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);

                    channel.QueueBind(queue: queueName, exchange: exchangeName, routingKey: String.Empty, arguments: null);

                    EventingBasicConsumer consumer = new EventingBasicConsumer(channel);
                    consumer.Received += (model, ea) =>
                    {
                        var body = ea.Body;
                        var message = Encoding.UTF8.GetString(body);
                        var routingKey = ea.RoutingKey;
                        Console.WriteLine(" [x] Received '{0}':'{1}'", routingKey, message);

                        channel.BasicAck(ea.DeliveryTag, multiple: false);
                    };

                    channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer);

                    Console.WriteLine("等待接收消息");
                    Console.ReadKey();
                }
            }
        }
    }
}
View Code

所以通过以上的代码就可以保证服务器某节点宕机后订阅的连接自动重连切换。

RabbitMQ集群异地复制

基于warren的共享存储模式

这种方式其实并不是跨地区的远程复制,并且需要共享存储,如果感兴趣的同学可以百度下。

基于Shovel的远程复制

如果直接基于WAN来组建异地的集群的话,集群间大量的数据通讯会产生高昂的费用,另外Erlang也不允许这么高延迟的通讯。 
Shovel是RabbitMQ自带插件(2.7.0后),自带插件的好处就是可以在RabbitMQ服务启动时自动启动Shovel和自定义复制关系。 
Shovel运行的原理其实非常简单。通过定义RabbitMQ上一个队列和另外一个RabbitMQ上的交换机之间的复制关系来实现远程复制。也就是说它会在主服务上建立一个队列来监听交换机,所以这是到交换机所以的消息会投递到该队列,并且在从服务中订阅这个队列,使队列中的消息复制到从服务的交换机中。RabbitMQ是一个比较全面的消息队列解决方案,我们公司并没有用到该功能,只是在这提下,感兴趣的同学可以搜下。 

 

posted on 2018-05-24 18:05  一个大西瓜咚咚咚  阅读(4031)  评论(1编辑  收藏  举报

导航