使用流式处理优化内存使用情况

本主题提供有关使用流式处理模式的建议,以最大程度地减少使用 WCF 传输或接收大型消息时或加载业务流程中的消息时的消息内存占用。

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

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

在使用 WCF 传输发送或接收大型消息时,采用流式处理方式。

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

  • basicHttpBinding + BasicHttpBindingElement,transferMode = Streamed

  • netTcpBinding + NetTcpBindingElement,传输模式 = 流式传输

  • customBinding + HttpTransportElement,transferMode = Streamed

  • customBinding +ConnectionOrientedTransportElement,传输模式 = 流式

    选择 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 代码,并将部件内容作为字符串检索。

    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 对象的实例,而不会随时间推移释放它们。 要了解以这种方式调用 message.Dispose() 的详细信息,请参阅 BizTalk Server 文档中的 表示为 XLANGMessage 的消息。 本主题还提供了使用 IStreamFactory 使用基于流的方法在用户代码中构造 XLANGMessage 变量的最佳做法。

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

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

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

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

  • 此示例演示如何使用 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 应用程序