天天看點

MTOM效率測試

這一篇,我們主要用執行個體來看看到底它在性能方面有何表現

我們先做一些準備工作,編寫了一個接口和服務

1. 接口

using System.ServiceModel;

namespace Services
{
    [ServiceContract]
    public interface IHelloService
    {

        [OperationContract]
        byte[] GetData();



    }
}      
2.服務
namespace Services
{
    public class HelloWorldService:IHelloService
    {
        #region IHelloService 成員

        byte[] IHelloService.GetData()
        {
            return new byte[10000];
        }

        #endregion
    }
}      

注意,我們這裡的服務很簡單,就是直接傳回一個長度為10000的位元組數組。

接下來編寫宿主和用戶端

3.宿主

using System;
using System.ServiceModel;
using Services;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof(HelloWorldService)))
            {
                host.Open();
                Console.WriteLine("伺服器已經準備好");
                Console.Read();
            }
        }
    }
}      

宿主的配置檔案

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="Services.HelloWorldService">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8080/HelloService"/>
          </baseAddresses>
        </host>
        <endpoint address="" contract="Services.IHelloService" binding="basicHttpBinding" bindingConfiguration="mtom"></endpoint>
      </service>
    </services>

    <bindings>
      <basicHttpBinding>
        <binding name="mtom" messageEncoding="Mtom"></binding>
      </basicHttpBinding>
    </bindings>
  </system.serviceModel>
</configuration>      

4. 用戶端

using System;
using System.ServiceModel;
using Services;

namespace TestClient
{
    class Program
    {
        static void Main(string[] args)
        {
            IHelloService proxy =
                (new ChannelFactory<IHelloService>("local")).CreateChannel();

            byte[] buffer=proxy.GetData();
            Console.WriteLine(buffer.Length);

            Console.Read();
        }
    }
}      

用戶端的配置檔案

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    <system.diagnostics>
    <sources>
      <source name="System.ServiceModel.MessageLogging"
      switchValue="Verbose">
        <listeners>
          <add name="xml"
          type="System.Diagnostics.XmlWriterTraceListener"
          initializeData="c:\logs\client.svclog" />
        </listeners>
      </source>
    </sources>
    <trace autoflush="true" />
  </system.diagnostics>




  <system.serviceModel>

    <diagnostics>
      <messageLogging logEntireMessage="true"
      maxMessagesToLog="300"
      logMessagesAtServiceLevel="false"
      logMalformedMessages="true"
      logMessagesAtTransportLevel="true" />
    </diagnostics>
    <client>
      <endpoint address="http://localhost:8080/HelloService" binding="basicHttpBinding" contract="Services.IHelloService" name="local" bindingConfiguration="mtom"></endpoint>
    </client>
    <bindings>
      <basicHttpBinding>
        <binding name="mtom" messageEncoding="Mtom"></binding>
      </basicHttpBinding>
    </bindings>
  </system.serviceModel>
</configuration>      

說明,上面的配置檔案中,已經配置好了messageEncoding為Mtom. 但我們在運作程式時會分兩次運作,一次是标準的編碼,一次則是Mtom編碼

我們通過下面的日志檔案可以比較出來MTOM編碼的效率優勢

下圖是标準編碼的結果,請注意Content-length為13524

​​

MTOM效率測試

​​

下圖是Mtom編碼的結果,請注意Content-Length為10739

MTOM效率測試

最後有一點要提示的是,MTOM編碼主要針對二進制結果有優勢。原因在于,标準編碼(Text)在對二進制資料編碼的時候會采取Base64編碼,使得資料平白地多出來了1/3左右。

而針對純文字的資料,則MTOM編碼的長度 總是會大于标準編碼的長度。

例如我們有如下的一個方法,傳回一些字元

public string GetString()
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 1000; i++)
            {
                sb.Append("Hello");
            }
            return sb.ToString();
        }      

首先看标準編碼的長度,為5196

MTOM效率測試

而MTOM編碼的長度則為

MTOM效率測試

我們再來測試一下複雜類型的情況。為了示範,我們添加了一個Employee類型在用戶端和伺服器之間傳遞。

[DataContract]
    public class Employee {
        [DataMember]
        public string FirstName { get; set; }
        [DataMember]
        public string LastName { get; set; }

        public override string ToString()
        {
            return string.Format("{0},{1}", FirstName, LastName);
        }
    }      

然後在服務中實作了一個方法

Employee[] IHelloService.GetEmployees()
        {
            List<Employee> result = new List<Employee>();
            for (int i = 0; i < 500; i++)
            {
                result.Add(new Employee()
                {
                    FirstName = "陳",
                    LastName = "希章"
                });
            }

            return result.ToArray();
        }      

我們看到,發送500個員工的資料,如果按照标準編碼的話,長度大緻需要43319

MTOM效率測試

如果改成mtom呢,長度為43561,還是稍微大一些。這是因為MTOM這種編碼本身會有一些額外的開銷

MTOM效率測試

好的,這裡需要總結一下了

1. MTOM是一個編碼方式

2. MTOM是針對較大的二進制資料有優化作用,相比較預設的Text編碼,它傳輸的是接近于資料原本的格式,而Text編碼方式則采用了Base64的方式進行編碼。

3. 針對标準文本或者對象,MTOM并沒有優化作用。

最後,有沒有朋友想到了這樣一個問題:既然是二進制的資料,能不能直接就采用二進制的方式傳遞呢?幹嘛要去編碼成文本呢?

是的,這是一個很好的問題,我們當然也可以直接采用netTcpBinding,它預設就是直接使用二進制編碼

我們從下圖是看不到長度的

static void CompareMessageSize(int dataSize)
        {
            // Create and buffer a message with a binary payload
            byte[] binaryData = new byte[dataSize];
            Message message = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "action", binaryData);
            MessageBuffer buffer = message.CreateBufferedCopy(int.MaxValue);

            // Print the size of a text encoded copy
            int size = SizeOfTextMessage(buffer.CreateMessage());
            Console.WriteLine("Text encoding with a {0} byte payload: {1}", binaryData.Length, size);

            // Print the size of an MTOM encoded copy
            size = SizeOfMtomMessage(buffer.CreateMessage());
            Console.WriteLine("MTOM encoding with a {0} byte payload: {1}", binaryData.Length, size);

            Console.WriteLine();
            message.Close();
        }

        static int SizeOfTextMessage(Message message)
        {
            // Create a text encoder
            MessageEncodingBindingElement element = new TextMessageEncodingBindingElement();
            MessageEncoderFactory factory = element.CreateMessageEncoderFactory();
            MessageEncoder encoder = factory.Encoder;

            // Write the message and return its length
            MemoryStream stream = new MemoryStream();
            encoder.WriteMessage(message, stream);
            int size = (int)stream.Length;

            message.Close();
            stream.Close();
            return size;
        }

        static int SizeOfMtomMessage(Message message)
        {
            // Create an MTOM encoder
            MessageEncodingBindingElement element = new MtomMessageEncodingBindingElement();
            MessageEncoderFactory factory = element.CreateMessageEncoderFactory();
            MessageEncoder encoder = factory.Encoder;

            // Write the message and return its length
            MemoryStream stream = new MemoryStream();
            encoder.WriteMessage(message, stream);
            int size = (int)stream.Length;

            stream.Close();
            message.Close();
            return size;
        }