【问题标题】:How to synchronously consume raw byte messages from RabbitMQ using EasyNetQ?如何使用 EasyNetQ 同步消费来自 RabbitMQ 的原始字节消息?
【发布时间】:2015-11-17 20:46:32
【问题描述】:

有什么方法可以使用 EasyNetQ 同步消费来自 RabbitMQ 的原始字节消息?

我需要保证对来自未以 EasyNetQ 格式发布的系统的消息进行有序处理和确认。我知道消费者在单线程上运行,但IAdvancedBus 接口只提供了一种使用原始消息的方法:

IDisposable Consume(IQueue queue, Func<byte[], MessageProperties, MessageReceivedInfo, Task> onMessage);

Task 返回类型意味着消费者正在异步运行回调,因此可能会乱序处理消息。

如果没有,有什么想法可以更改代码以支持此功能?我会做接口方法:

IDisposable Consume(IQueue queue, Action<byte[], MessageProperties, MessageReceivedInfo> onMessage);

并在RabbitAdvancedBus 中实现它,但我不确定代码的确切位置。

【问题讨论】:

    标签: c# .net rabbitmq messaging easynetq


    【解决方案1】:

    这是一个有趣的问题。我自己不是 EasyNetQ 专家,也许其他人会过来给你一个更好的答案。 然而我已经熟悉EasyNetQ code base 大约一年了,在我看来,要了解连接消费者时发生的事情是很棘手的(因此当消费者正在调用)。

    我首先要指出的是,仅仅通过改变方法的签名,并不能保证消息是按顺序处理的。例如,看看你建议的接口的这个实现:

    IDisposable Consume(IQueue queue, Action<byte[], MessageProperties, MessageReceivedInfo> onMessage)
    {
        Func<byte[], MessageProperties, MessageReceivedInfo, Task> taskWrapper = (bytes, properties, info) =>
        {
            onMessage(bytes, properties, info);
            return new Task(() => { });
        };
        Consume(queue, taskWrapper);
    }
    

    它调用原始的Consume 方法,我们真的不知道之后会发生什么,对吧?

    如果我在你的情况下,我会做以下事情之一:

    1. 使用Official RabbitMq Client 并使用那里的消息表单(这不是那么棘手!)
    2. 也许看看RawRabbit,这是我一直在贡献的 RabbitMq 之上的一个薄层(使用 vNext 标准)。它只支持使用消息的异步签名,但编写Subscriber.cs 的同步实现应该不难(使用像AsyncEx 这样的同步库)。
    3. 更改业务逻辑的建模。我不确定这是否适用于您的情况,但一般来说,如果以正确的顺序处理每条消息是关键任务,您应该以某种方式对其进行建模,以便使用方法可以验证该消息是下一条排队。 (此外,我认为 EasyNetQ 不保证消息序列,因此您可能希望针对框架的每个新版本进行验证)。

    希望这会有所帮助!

    【讨论】:

    • 感谢您的考虑。当我说“更改方法签名”时,我的意思是在接口上添加一个方法,以明确说明代码是同步执行的(然后在类中实现它)。我在有效的 Google 群组中收到了回复(请参阅我的回复)
    【解决方案2】:

    我收到了适用于 EasyNetQ Google 群组的回复:

    要同步执行,您可以这样做:

    bus.Advanced.Consume(queue, (bytes, properties, info) =>
    {
        // do your synchronous work.....
        return Task.CompletedTask;
    });
    

    或添加扩展:

    using System;
    using System.Threading.Tasks;
    using EasyNetQ;
    using EasyNetQ.Consumer;
    using EasyNetQ.Loggers;
    using EasyNetQ.Topology;
    
    namespace ConsoleApplication4
    {
        public static class RabbitAdvancedBusConsumeExtension
        {
           public static IDisposable Consume(this IAdvancedBus bus, IQueue queue, Action<byte[], MessageProperties, MessageReceivedInfo> onMessage)
        {
            return bus.Consume(queue, (bytes, properties, info) => ExecuteSynchronously(() => onMessage(bytes, properties, info)));
        }
    
        public static IDisposable Consume(this IAdvancedBus bus, IQueue queue, Action<byte[], MessageProperties, MessageReceivedInfo> onMessage, Action<IConsumerConfiguration> configure)
        {
            return bus.Consume(queue, (bytes, properties, info) => ExecuteSynchronously(() => onMessage(bytes, properties, info)), configure);
        }
    
        private static Task ExecuteSynchronously(Action action)
        {
            var tcs = new TaskCompletionSource<object>();
            try
            {
                action();
                tcs.SetResult(null);
            }
            catch (Exception e)
            {
                tcs.SetException(e);
            }
            return tcs.Task;
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var bus = RabbitHutch.CreateBus("host=localhost", x => x.Register<IEasyNetQLogger>(s => new ConsoleLogger()));
    
            var queue = bus.Advanced.QueueDeclare();
            bus.Advanced.Consume(queue, (bytes, properties, info) =>
            {
                // .....
            });
        }
    }
    }
    

    更新:此功能是在 0.52.0.410 版中添加的:

    https://github.com/EasyNetQ/EasyNetQ/pull/505

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-20
      • 1970-01-01
      • 2019-06-25
      相关资源
      最近更新 更多