這一篇,我們主要用執行個體來看看到底它在性能方面有何表現
我們先做一些準備工作,編寫了一個接口和服務
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編碼的結果,請注意Content-Length為10739
最後有一點要提示的是,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編碼的長度則為
我們再來測試一下複雜類型的情況。為了示範,我們添加了一個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呢,長度為43561,還是稍微大一些。這是因為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;
}