Rabbit使用CorrelationId进行可靠性消息回调

  先放一张使用CorrelationId相关ID进行消息回调处理的流程图

  

  • 客户端启动时,它将创建一个匿名回调队列
  • 对于 RPC 请求,客户端发送一条消息,该消息具有两个属性: reply_to(设置为回调队列)和 correlation_id(设置为每个请求的唯一值)
  • 该请求被发送到 rpc_queue 队列
  • RPC 工作程序等待该队列上的请求,出现请求时,它将使用 reply_to 字段中的队列来完成工作,并将消息和结果发送回客户端
  • 客户端等待回调队列上的数据,出现消息时,它将检查 correlation_id 属性。 如果它与请求中的值匹配,则将响应返回给应用程序(TODO)

发送回调

        public void SendCallback<T>(string topic, T value, Action<byte[]> callback, byte priority = 1)
        {
            EnsureDeclare(topic);
            var replyQueue = _model.QueueDeclare().QueueName;
            var props = _model.CreateBasicProperties();
            props.Priority = priority;
            props.ReplyTo = replyQueue;
            var correlationId = Guid.NewGuid().ToString("N");
            props.CorrelationId = correlationId;

            //发布消息
            _model.BasicPublish(_exchange, topic, props, value.GetBuf());

            //创建消费者用于消息回调
            var callbackConsumer = new EventingBasicConsumer(_model);
            _model.BasicConsume(queue: replyQueue, autoAck: true, consumer: callbackConsumer);

            callbackConsumer.Received += (model, ea) =>
            {
                if (ea.BasicProperties.CorrelationId == correlationId)
                    callback(ea.Body);
            };
        }

 

消息接收,在接受消息后,通过ReplyTo来进行消息回执

        public void SetReceiveCallback(string topic, Action<byte[], IBasicProperties> received)
        {
            EnsureDeclare(topic);
            var consumer = new EventingBasicConsumer(_model);
            consumer.Received += (model, ea) =>
            {
                var properties = ea.BasicProperties;
                var replyProerties = _model.CreateBasicProperties();
                replyProerties.CorrelationId = properties.CorrelationId;
                replyProerties.ReplyTo = properties.ReplyTo;
                received(ea.Body, replyProerties);
                _model.BasicAck(ea.DeliveryTag, false);
            };
            _model.BasicConsume(topic, false, consumer);
        }

 

单元测试代码

    [Order(1)]
    public class RabbitCallbackTest
    {
        [Fact, Order(1)]
        public void ServerCallback()
        {
            _rabbitSvr = new Aster.Itms.Core.Data.Provider.Rabbit(new Connection()
            {
                Ip = "localhost",
                Port = 5672,
                User = "guest",
                Password = "guest"
            }, "");

            _rabbitSvr.SetReceiveCallback("topic", ServerReceiveCommand);
        }


        private static volatile Aster.Itms.Core.Data.Provider.Rabbit _rabbitSvr;
        [Fact, Order(2)]
        public void ClientCallback()
        {
            var _rabbitSvr = new Aster.Itms.Core.Data.Provider.Rabbit(new Connection()
            {
                Ip = "localhost",
                Port = 5672,
                User = "guest",
                Password = "guest"
            }, "");

            string val = $"发送消息{Guid.NewGuid().ToString("N")}";

            _rabbitSvr.SendCallback<string>("topic", val, ClientReceiveCommand, 1);
        }

        private static void ClientReceiveCommand(byte[] body)
        {
            var result = body.To<string>();

            //拿到结果再发送回去

            Console.WriteLine($"接收服务端返回消息:{result}");

            Assert.NotEmpty(result);
        }

        private static void ServerReceiveCommand(byte[] body, IBasicProperties replyProps)
        {
            var result = body.To<string>();

            Assert.NotEmpty(result);

            Console.WriteLine($"服务端接收:{result},CorrelationId:{replyProps.CorrelationId}");

            int.TryParse(result, out int newResult);

            if (!string.IsNullOrEmpty(replyProps.ReplyTo))
                _rabbitSvr.Send(replyProps.ReplyTo, $"{Convert.ToInt32(newResult) + 1000}", replyProps);
        }
    }

 

posted @ 2020-04-01 15:08  leeolevis  阅读(1121)  评论(0编辑  收藏  举报