使用RabbitMQ缩放Windows服务

本文介绍如何通过RabbitMQ和竞争消费者模式解决Windows服务处理能力的问题,实现服务的负载均衡、可扩展性和冗余。文中详细展示了基于C#的RabbitMQ基本模式实现。

http://reverentgeek.com/scale-windows-services-with-rabbitmq/

CrossBased to FreshBrewedCode.com

当我加入我目前的公司时,我们收集了一系列Windows服务和计划任务,处理来自各种来源的图像和视频。例如,我们有一个服务,用于邮件邮箱的邮件,另一个邮寄的Facebook帐户,一个发送视频关闭编码,等等。虽然有共享库,但仍然有很多重复的代码。这些服务中的大多数都是单线程的,而不是设计为超越单个实例。由于我们的平台必须能够在发生突发新闻或天气事件时响应巨大的活动峰值,所以我们需要找出更好的鼠标陷阱。

在本年初早些时候参加过几次关于分布式架构的纳什维尔.NET用户组讲座[1],我从开始就开始了一个良好的开端。基于消息队列的系统似乎是明显的答案。

竞争消费者消息模式

“竞争消费者”模式来自  企业集成模式,并描述了一种基于消息的系统,其中多个消费者收听单个消息队列,只允许一个消费者处理任何给定的消息。使用消息队列的优点是,如果这些消费者是同一台机器上的应用程序(或线程)的多个实例,或者分布在多个物理机器上,则不会有任何差异。利用此模式与消息队列平台,创建提供负载平衡,可扩展性和冗余的系统几乎是微不足道的。您仍然专注于实施处理单个消息所需的业务逻辑。

RabbitMQ和C#的基本模式实现

如果您还没有安装RabbitMQ Server,请转到  RabbitMQ下载  页面,然后单击适用于您的平台的相应安装指南。如果您正在运行Windows,则需要首先下载并安装Erlang  。我还建议您安装  管理插件,这将为您提供一个基于Web的UI来监控和管理RabbitMQ Server。您可以使用NuGet轻松地将最新的RabbitMQ.Client .NET库添加到Visual Studio项目中  或者,请参阅下面的“进一步阅读和资源”下载编译的库和源代码。

让我们来看看一些演示发布和消费消息的代码。以下是服务器/发行商的代码  

// Set up the RabbitMQ connection and channel
var connectionFactory = new ConnectionFactory  
{
    HostName = "localhost",
    Port = 5672,
    UserName = "guest",
    Password = "guest",
    Protocol = Protocols.AMQP_0_9_1,
    RequestedFrameMax = UInt32.MaxValue,
    RequestedHeartbeat = UInt16.MaxValue
};

using (var connection = connectionFactory.CreateConnection())  
using (var channel = connection.CreateModel())  
{
    // Create a new, durable exchange
    channel.ExchangeDeclare("sample-ex", ExchangeType.Direct, true, false, null);
    // Create a new, durable queue
    channel.QueueDeclare("sample-queue", true, false, false, null);
    // Bind the queue to the exchange
    channel.QueueBind("sample-queue", "sample-ex", "optional-routing-key");

    // Set up message properties
    var properties = channel.CreateBasicProperties();
    properties.DeliveryMode = 2; // Messages are persistent and will survive a server restart

    // Ready to start publishing
    // The message to publish can be anything that can be serialized to a byte array,
    // such as a serializable object, an ID for an entity, or simply a string
    var encoding = new UTF8Encoding();
    for (var i = 0; i < 10; i++)
    {
        var msg = string.Format("This is message #{0}?", i+1);
        var msgBytes = encoding.GetBytes(msg);
        channel.BasicPublish("sample-ex", "optional-routing-key", false, false, properties, msgBytes);
    }
    channel.Close();
}
Console.WriteLine("Messages published");  
Console.ReadKey(true);  

这里是客户端/消费者的代码  

// Set up the RabbitMQ connection and channel
var connectionFactory = new ConnectionFactory  
{
    HostName = "localhost",
    Port = 5672,
    UserName = "guest",
    Password = "guest",
    Protocol = Protocols.AMQP_0_9_1,
    RequestedFrameMax = UInt32.MaxValue,
    RequestedHeartbeat = UInt16.MaxValue
};

using (var connection = connectionFactory.CreateConnection())  
using (var channel = connection.CreateModel())  
{
    // This instructs the channel not to prefetch more than one message
    channel.BasicQos(0, 1, false);

    // Create a new, durable exchange
    channel.ExchangeDeclare("sample-ex", ExchangeType.Direct, true, false, null);
    // Create a new, durable queue
    channel.QueueDeclare("sample-queue", true, false, false, null);
    // Bind the queue to the exchange
    channel.QueueBind("sample-queue", "sample-ex", "optional-routing-key");

    using (var subscription = new Subscription(channel, "sample-queue", false))
    {
        Console.WriteLine("Waiting for messages...");
        var encoding = new UTF8Encoding();
        while (channel.IsOpen)
        {
            BasicDeliverEventArgs eventArgs;
            var success = subscription.Next(2000, out eventArgs);
            if (success == false) continue;
            var msgBytes = eventArgs.Body;
            var message = encoding.GetString(msgBytes);
            Console.WriteLine(message);
            channel.BasicAck(eventArgs.DeliveryTag, false);
        }
    }
}

实现模式的第一个关键步骤是声明一个  直接  交换,它将基于路由信息将消息发布到单个消息队列。通常使用的  扇出  交换机将消息广播到绑定到交换机的每个消息队列。

第二个关键步骤是配置消费者频道的  BasicQos  设置,以便它一次只能从队列中提取一个消息。如果没有设置,那么一个消费者基本上可以暂停在队列中等待的所有消息,以便其他消费者都不能访问它们。这些消息将保持在“消费”但未确认的状态,直到一个消费者处理,完全失败了实现该模式的目的!在配置BasicQos上没有很多文档可用,所以我必须弄清楚这个要求。

最后一步是确认消息已被处理,允许RabbitMQ服务器从队列中删除消息,消费者可以接收下一个可用消息。

运行示例代码
  1. 下载示例项目
  2. 加载和构建解决方案。
  3. 启动两个或多个命令提示以用作消费者。
  4. 将每个命令提示符的当前目录更改为[your-project-root]\CompetingConsumers.Consumer\bin\Debug并启动CompetingConsumers.Consumer.exe
  5. 在Visual Studio中,按F5启动CompetingConsumers.Publisher。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值