这一篇,我们主要用实例来看看到底它在性能方面有何表现
我们先做一些准备工作,编写了一个接口和服务
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;
}