通过流式处理优化内存使用

本主题提供有关使用流式处理模式在通过 WCF 传输发送或接收大型消息时或在业务流程中加载消息时最小化消息内存占用量的建议。

在业务流程中使用代码读取消息内容时,请避免使用 XmlDocument 变量。 将消息加载到 XmlDocument 变量会产生大量开销,尤其是大型消息。 此开销与内存使用情况和生成内存中结构的处理有关。 使用 XmlDocument 实例强制将整个消息内容加载到内存中,以便为文档对象模块生成对象图 (DOM) 。 此类的实例使用的内存总量可能大约是实际消息大小的 10 倍。 有关将消息加载到 XmlDocument 变量时所需的内存占用的详细信息,请参阅 第 9 章 – 提高 XML 性能

本主题的其余部分提供了用于读取不需要将消息加载到 XmlDocument 变量的消息内容的替代方法。

使用 WCF 传输发送或接收大型消息时使用流式处理

使用 WCF 传输发送或接收大型消息时,请使用 WCF-Custom 或 WCF-CustomIsolated 适配器,并使用支持 transferMode = Streamed 选项的 绑定类型进行配置,例如以下绑定:

  • basicHttpBinding + BasicHttpBindingElement, transferMode = Streamed

  • netTcpBinding + NetTcpBindingElement, transferMode = Streamed

  • customBinding + HttpTransportElement,transferMode = Streamed

  • customBinding +ConnectionOrientedTransportElement, transferMode = Streamed

    选择 WCF-Custom 或 WCF-CustomIsolated 适配器以及支持 transferMode = Streamed 选项的 绑定将根据需要将大型消息流式传输到文件系统,并缓解潜在的内存不足问题。

使用流式处理来最大程度地减少在业务流程中加载消息时所需的内存占用

以下技术介绍如何在将消息加载到业务流程中时最大程度地减少消息的内存占用。

使用 XLANGMessage 变量处理消息或消息部件的内容

将消息从业务流程传递到 .NET 类库时,请不要将其作为 XmlDocument 变量传递,因为本主题前面所述的原因;请改用 XLANGMessage 变量。 以下技术演示了使用 XLANGMessage 变量读取消息或消息部分的方法。

  • 使用 XMLReader 处理消息 - 若要使用 XmlReader 实例处理消息,请将消息作为 XLANGMessage 传递给 .NET 代码,并使用 XmlReader 检索部件内容。

    public void ProcessMessage(XLANGMessage message)
    {
        try
        {
            using (XmlReader reader = message[0].RetrieveAs(typeof(XmlReader)) as XmlReader)
            if (reader != null)
            {
                ...
            }
        }
        finally
        {
            message.Dispose();
        }
    }
    
  • 使用 StreamReader 将消息的内容检索到字符串 中 - XmlDocument 在业务流程中的一个常见用途是使用 XmlDocument.OuterXml () 将消息检索为 XML 字符串。 下面的代码示例演示了一种替代方法,该方法使用 XLANGMessage 变量以字符串的形式检索消息。

    public static string MessageToString(XLANGMessage message)
    {
        string strResults;
        try
        {
            using (Stream stream = message[0].RetrieveAs(typeof(Stream)) as Stream)
            {
                using (StreamReader reader = new StreamReader(stream))
                {
                    strResults = reader.ReadToEnd();
                }
            }
        }
        finally
        {
            message.Dispose();
        }
        return strResults;
    }
    
  • 将简单 .NET 消息的内容检索到字符串 中 - 如果消息的类型是简单的 .NET 类型,则可以将消息检索为该类型。 例如,若要以字符串的形式获取消息,请将消息作为 XLANGMessage 传递给 .NET 代码,并将 Part 内容作为字符串检索。

    public void ProcessMessage(XLANGMessage message)
    {
        try
        {
            string content = message[0].RetrieveAs(typeof(string)) as string;
            if (!string.IsNullOrEmpty(content))
            {
                ...
            }
        }
        finally
        {
            message.Dispose();
        }
    }
    
  • 将消息的内容检索到流 中 - 若要将消息作为流获取,请将消息作为 XLANGMessage 传递到 .NET 代码,并将部件内容作为流检索。

    public Stream ProcessRequestReturnStream(XLANGMessage message, int bufferSize, int thresholdSize)
    {
       ...
       try
       {
          using (VirtualStream virtualStream = new VirtualStream(bufferSize, thresholdSize))
          {
             using (Stream partStream = (Stream)message[0].RetrieveAs(typeof(Stream)))
             //Note that when calling this code, if the XmlDocument is quite large, keeping it in a memory with a MemoryStream may have an adverse effect on performance.
             //In this case, it may be worthwhile to consider an approach that uses a VirtualStream + ReadonlySeekableStream to buffer it to the file system, if its size is bigger than the thresholdSize parameter.
             //Keep in mind that:
             // - If the message size is smaller than the threshold size, the VirtualStream class buffers the stream to a MemoryStream.
             // - If the message size is bigger than the threshold size, the VirtualStream class buffers the stream to a temporary file.
                using (ReadOnlySeekableStream readOnlySeekableStream = new ReadOnlySeekableStream(partStream, virtualStream, bufferSize))
                {
                   using (XmlReader reader = XmlReader.Create(readOnlySeekableStream))
                   {
    
                   }
                }
             }
          }
       }
       catch (Exception ex)
       {
    
       }
       finally
       {
          message.Dispose();
       }
       return stream;
    }
    
  • 将消息的内容检索到 .NET 对象 中 - 若要将消息作为 .NET 对象获取,请将消息作为 XLANGMessage 传递给 .NET 代码,并将部件内容作为 .NET 类的实例检索。 使用 Visual Studio 2010 提供的 XML 架构定义工具 (Xsd.exe) 工具从消息的 Xml 架构创建后者。

    注意

    仅当消息较小时,此方法才有效。 否则,此方法可能会产生将实际消息反序列化为 .NET 对象的巨大开销。

    public void ProcessMessage(XLANGMessage message)
    {
        try
          {
          Request request = message[0].RetrieveAs(typeof(Request)) as Request;
          if (request != null)
          {
             ...
          }
       }
       finally
       {
          message.Dispose();
       }
    
    }
    

注意

在循环方案和长时间运行的业务流程中,在从 .NET 代码返回之前使用由 XLANGMessage 参数公开的 Dispose () 方法尤其重要,这些业务流程可以累积 XLANGMessage 对象的实例,而不会随时间推移释放它们。 有关调用消息的详细信息。以这种方式释放 () ,请参阅 BizTalk Server 文档中表示为 XLANGMessage 的消息。 本主题还提供了使用 IStreamFactory 使用基于流的方法在用户代码中构造 XLANGMessage 变量的最佳做法。

有关在业务流程调用的帮助程序组件中处理 XLANGMessage 的不同方法的详细信息,请参阅以下主题:

使用 XPathReader 和 XPathCollection 从业务流程调用的方法中提取 XLANGMessage 对象的值

避免使用 XMLDocument 类从自定义代码读取 XML 消息的内容,例如自定义管道组件或业务流程调用的帮助程序类。 使用 XMLDocument 实例加载 XML 消息时,整个消息将加载到内存中,这效率低下,可能需要内存,内存最多是消息实际大小的 10 倍。 读取 XML 消息内容的更高效方法是使用流式处理技术将原始流包装为 Microsoft.BizTalk.Streaming.dll 程序集提供的流类之一。 在加载大型消息时,此方法特别有用。

如果需要从 XML 文档拉取特定值,请使用由 Microsoft.BizTalk.XPathReader.dll 程序集提供的 XPathReader 类的实例,而不是使用 XmlDocument 类公开的 SelectNodes 和 SelectSingleNode 方法,如以下代码示例所示。

  • 此示例演示如何使用 XPathReader 和 XPathCollection 从业务流程调用的方法内的 XLANGMessage 对象中提取给定值。

    public static string SelectSingleNode(XLANGMessage message, string xPath)
    {
        try
        {
            if (message == null || string.IsNullOrEmpty(xPath))
            {
                return string.Empty;
            }
            using (XmlReader reader = (XmlReader)message[0].RetrieveAs(typeof(XmlReader)))
            {
                XPathCollection xPathCollection = new XPathCollection();
                XPathReader xPathReader = new XPathReader(reader, xPathCollection);
                xPathCollection.Add(xPath);
                while (xPathReader.ReadUntilMatch())
                {
                    if (xPathReader.Match(0))
                    {
                        return xPathReader.ReadString();
                    }
                }
            }
        }
        catch (Exception ex)
        {
            ...
        }
        finally
        {
            message.Dispose();
        }
        return string.Empty;
    }
    

另请参阅

优化 BizTalk Server 应用程序