【问题标题】:Passing object messages in Azure Queue Storage在 Azure 队列存储中传递对象消息
【发布时间】:2021-05-20 16:28:02
【问题描述】:

我正在尝试找到一种将对象传递到 Azure 队列的方法。我找不到这样做的方法。

正如我所见,我可以传递字符串或字节数组,这对于传递对象不太方便。

是否可以将自定义对象传递给队列?

谢谢!

【问题讨论】:

    标签: c# azure azure-storage-queues


    【解决方案1】:

    您可以使用以下类作为示例:

     [Serializable]
        public abstract class BaseMessage
        {
            public byte[] ToBinary()
            {
                BinaryFormatter bf = new BinaryFormatter();
                byte[] output = null;
                using (MemoryStream ms = new MemoryStream())
                {
                    ms.Position = 0;
                    bf.Serialize(ms, this);
                    output = ms.GetBuffer();
                }
                return output;
            }
    
            public static T FromMessage<T>(CloudQueueMessage m)
            {
                byte[] buffer = m.AsBytes;
                T returnValue = default(T);
                using (MemoryStream ms = new MemoryStream(buffer))
                {
                    ms.Position = 0;
                    BinaryFormatter bf = new BinaryFormatter();
                    returnValue = (T)bf.Deserialize(ms);
                }
                return returnValue;
            }
        }
    

    然后是一个 StdQueue(一个强类型的队列):

       public class StdQueue<T> where T : BaseMessage, new()
        {
            protected CloudQueue queue;
    
            public StdQueue(CloudQueue queue)
            {
                this.queue = queue;
            }
    
            public void AddMessage(T message)
            {
                CloudQueueMessage msg =
                new CloudQueueMessage(message.ToBinary());
                queue.AddMessage(msg);
            }
    
            public void DeleteMessage(CloudQueueMessage msg)
            {
                queue.DeleteMessage(msg);
            }
    
            public CloudQueueMessage GetMessage()
            {
                return queue.GetMessage(TimeSpan.FromSeconds(120));
            }
        }
    

    那么,你所要做的就是继承BaseMessage:

    [Serializable]
    public class ParseTaskMessage : BaseMessage
    {
        public Guid TaskId { get; set; }
    
        public string BlobReferenceString { get; set; }
    
        public DateTime TimeRequested { get; set; }
    }
    

    并创建一个处理该消息的队列:

    CloudStorageAccount acc;
                if (!CloudStorageAccount.TryParse(connectionString, out acc))
                {
                    throw new ArgumentOutOfRangeException("connectionString", "Invalid connection string was introduced!");
                }
                CloudQueueClient clnt = acc.CreateCloudQueueClient();
                CloudQueue queue = clnt.GetQueueReference(processQueue);
                queue.CreateIfNotExist();
                this._queue = new StdQueue<ParseTaskMessage>(queue);
    

    希望这会有所帮助!

    【讨论】:

    • 似乎这是最适合的解决方案 :) 谢谢!
    • 我知道 :) 我在生产中使用它 ;)
    • 非常简洁的解决方案。但我想说这违反了单一责任原则:可序列化 POCO 对象现在依赖于 Azure 库。我不会让消息从BaseMessage 继承,而是让ToBinary()FromMessage&lt;T&gt;()StdQueue&lt;T&gt; 类中是私有的。对象不应该真正负责自己的序列化/反序列化。
    • 嘿 astaykov,你将如何用这个存储泛型类型?如果你有 ParseTaskMessage,如果你必须反序列化它,你会从哪里得到类型?
    • Microsoft.WindowsAzure.Storage.Queue 的新版本确实将 ctor new CloudQueueMessage(byte[] content) 更改为静态方法 CloudQueueMessage.CreateCloudQueueMessageFromByteArray(byte[] content)
    【解决方案2】:

    使用 Newtonsoft.Json 和异步的扩展方法

        public static async Task AddMessageAsJsonAsync<T>(this CloudQueue cloudQueue, T objectToAdd)
        {
            var messageAsJson = JsonConvert.SerializeObject(objectToAdd);
            var cloudQueueMessage = new CloudQueueMessage(messageAsJson);
            await cloudQueue.AddMessageAsync(cloudQueueMessage);
        }
    

    【讨论】:

      【解决方案3】:

      我喜欢这种泛化方法,但我不喜欢将 Serialize 属性放在我可能想要放入消息中并从基类派生的所有类上(我可能也已经有一个基类)所以我使用...

      using System;
      using System.Text;
      using Microsoft.WindowsAzure.Storage.Queue;
      using Newtonsoft.Json;
      
      namespace Example.Queue
      {
          public static class CloudQueueMessageExtensions
          {
              public static CloudQueueMessage Serialize(Object o)
              {
                  var stringBuilder = new StringBuilder();
                  stringBuilder.Append(o.GetType().FullName);
                  stringBuilder.Append(':');
                  stringBuilder.Append(JsonConvert.SerializeObject(o));
                  return new CloudQueueMessage(stringBuilder.ToString());
              }
      
              public static T Deserialize<T>(this CloudQueueMessage m)
              {
                  int indexOf = m.AsString.IndexOf(':');
      
                  if (indexOf <= 0)
                      throw new Exception(string.Format("Cannot deserialize into object of type {0}", 
                          typeof (T).FullName));
      
                  string typeName = m.AsString.Substring(0, indexOf);
                  string json = m.AsString.Substring(indexOf + 1);
      
                  if (typeName != typeof (T).FullName)
                  {
                      throw new Exception(string.Format("Cannot deserialize object of type {0} into one of type {1}", 
                          typeName,
                          typeof (T).FullName));
                  }
      
                  return JsonConvert.DeserializeObject<T>(json);
              }
          }
      }
      

      例如

      var myobject = new MyObject();
      _queue.AddMessage( CloudQueueMessageExtensions.Serialize(myobject));
      
      var myobject = _queue.GetMessage().Deserialize<MyObject>();
      

      【讨论】:

      • 我喜欢这种方法,它很紧凑 :) 如果使用上面的代码,提醒您可能仍然需要引用原始 CloudQueueMessage 以便在阅读后将其从队列中删除。
      • 您实际上只需要原始消息的 MessageID 和 PopReciept 属性。
      【解决方案4】:

      如果存储队列与 WebJob 或 Azure 函数一起使用(非常常见的场景),那么当前的 Azure SDK 允许直接使用 POCO 对象。在此处查看示例:

      注意:SDK 会在后台自动使用 Newtonsoft.Json 进行序列化/反序列化。

      【讨论】:

        【解决方案5】:

        我喜欢@Akodo_Shado 使用Newtonsoft.Json 进行序列化的方法。我为Azure.Storage.Queues 更新了它,还添加了一个“检索和删除”方法,用于反序列化队列中的对象。

        public static class CloudQueueExtensions
        {
            public static async Task AddMessageAsJsonAsync<T>(this QueueClient queueClient, T objectToAdd) where T : class
            {
                string messageAsJson = JsonConvert.SerializeObject(objectToAdd);
                BinaryData cloudQueueMessage = new BinaryData(messageAsJson);
                await queueClient.SendMessageAsync(cloudQueueMessage);
            }
        
            public static async Task<T> RetreiveAndDeleteMessageAsObjectAsync<T>(this QueueClient queueClient) where T : class
            {
        
                QueueMessage[] retrievedMessage = await queueClient.ReceiveMessagesAsync(1);
                if (retrievedMessage.Length == 0) return null;
                string theMessage = retrievedMessage[0].MessageText;
                T instanceOfT = JsonConvert.DeserializeObject<T>(theMessage);
                await queueClient.DeleteMessageAsync(retrievedMessage[0].MessageId, retrievedMessage[0].PopReceipt);
        
                return instanceOfT;
            }
        }
        

        RetreiveAndDeleteMessageAsObjectAsync 旨在一次处理 1 条消息,但您显然可以重写以反序列化完整的消息数组并返回 ICollection&lt;T&gt; 或类似的。

        【讨论】:

          【解决方案6】:

          这不是正确的做法。队列不用于存储对象。您需要将对象放在 blob 或表中(序列化)。 我相信 queue messgae body 有 64kb 大小限制,sdk1.5 和 8kb 较低版本。 Messgae body 可以为只接收它的工作人员传输重要数据。

          【讨论】:

          • 当队列消息可以传输时,为什么我要把足够小的对象放在 Blob 或 Table 中?你想让我用 50% 的存储事务开销(从 2 个存储事务 - 1 个读取消息,1 个删除它;到 3 个 - 一个读取消息,1 个删除消息 + 1 个额外读取表实体或 blob)? ?恕我直言,blob 用于存储文件,而不是序列化对象。另外,您还具有 Table 实体的 64k 限制,其中 byte[] 属性可能最多为 64k - msdn.microsoft.com/en-us/library/windowsazure/dd179338.aspx!
          • 实际上存储事务将至少增加两次(增加 100%),因为我必须先将我想要的内容写入 blob/table,然后发送消息,然后读取消息,然后读取从 blob/table 中,然后删除该消息。如果我能避免这种情况 - 我会这样做。
          • 抱歉,回复晚了——但是,我想说,blob 不是用于存储文件,而是用于存储“长期 - 不可搜索的数据”。在 blob 中存储序列化(大)对象是 Azure 上的常见做法。元数据(和更多)进入表格。队列具有某种双重性质,我同意这一点,但肯定不意味着作为存储。如果您知道您的对象不会超过特定的大小限制并且这些对象通常不需要在任何时候都可以访问 - 确保将它们放入队列中。但需要注意的是,队列消息可以是可见的,也可以是不可见的、永远出队的等等。
          • 无法准确确定队列消息何时可用于获取您需要的数据。这就是为什么我说在队列中存储数据应该只是绑定到该消息操作的数据 only 而没有别的。但是,正如您所指出的,在某些情况下,将数据存储在队列消息本身中可能更安全且更好(事务方面)。
          • 队列是为传递数据和指令而设计的,这里展示的是一种很好的方式。我没有看到他们正在考虑将其用于长期存储的任何地方。我使用了一个使用序列化为 JSON 的小指令对象的版本,让处理系统知道有新的数据文件准备好进行处理。通过在两端使用对象,我维护了可能因损坏数据而失败的系统的类型控制。它还允许我在系统有一些可用容量时立即处理下一组文件。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-01-28
          • 1970-01-01
          • 1970-01-01
          • 2017-09-29
          • 2019-04-18
          • 1970-01-01
          • 2013-08-24
          相关资源
          最近更新 更多