我们经常抱怨首次调用WebService比较慢,通常的做法是在程序启动的时候,后台逐一调用一遍所有的WebService,还有人利用多线程来解决这个问题。其实,大家只是看到了问题的现象以及“工程”的解决办法,而没有接触到问题的本质。经过本人反编译.Net类库,逐步查找,应该说找到了解决这个问题的根本办法。我们都知道,WebService是通过Soap协议来传递消息的,所有的消息都是XML,而在客户端和服务器端,都是使用的对象,这其中必然有一个XML和对象之间的转换,这个转换就是慢的罪魁祸首。下面,我就逐步分析,希望对你能有帮助,请耐心看完。
1. 添加Web引用的时候,WebService在客户端有一个代理,如下:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="WebService1Soap",Namespace="http://tempuri.org/")
public partial class WebService1 : System.Web.Services.Protocols.SoapHttpClientProtocol
3. 客户端调用WebService的过程如下
string
str = (new Service2.WebService1()).HelloWorld ();
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/HelloWorld0766",RequestNamespace="http://tempuri.org/",ResponseNamespace="http://tempuri.org/",Use=System.Web.Services.Description.SoapBindingUse.Literal,ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public string HelloWorld() {
object[] results = this.Invoke("HelloWorld",new object[0]);
return ((string)(results[0]));
}
c) SoapHttpClientProtocal进行Soap序列化Soap头和方法,都是这个类自己做的,但是输入参数和返回值,是利用的XmlSerializer,输入参数要序列化,返回值要反序列化。
protected object[] Invoke(string methodName,object[] parameters)
{
…
try
{
message1.SetStream(stream1);
this.Serialize(message1);//
注1
}
…
response1 = this.GetWebResponse(request1);
Stream stream2 = null;
try
{
stream2 = response1.GetResponseStream();
objArray1 = this.ReadResponse(message1,response1,stream2,false);//
注2
}
}
注1:this.Serialize中有一句参数序列化的代码如下
method1.parameterSerializer.Serialize(writer1,message.GetParameterValues(),null,flag1 ? text2 : null);
注2:this.ReadResponse中有一句返回值的反序列化的代码如下
message.SetParameterValues((object[]) method1.returnSerializer.Deserialize(reader1,flag1 ? text1 : null));
获取缓存中的程序集:this.tempAssembly = XmlSerializer.cache[defaultNamespace,type];
缓存中没有就去加载:
Assembly
assembly1 = TempAssembly.LoadGeneratedAssembly(type,defaultNamespace,out implementation1);
this
.tempAssembly = new TempAssembly(new XmlMapping[] { this.mapping },assembly1,implementation1);
e) TempAssemlby这个类负责加载以及产生临时程序集
internal static string GetTempAssemblyName(AssemblyName parent,string ns)
{
return (parent.Name + ".XmlSerializers" + (((ns == null) || (ns.Length == 0)) ? "" : ("." + ns.GetHashCode())));
}
同时,如果加载失败会触发
AppDomain
.CurrentDomain.AssemblyResolve
事件
4. 结论
2)
WebService
慢,是因为产生序列化类慢,所谓的临时文件都是
XmlSerializer
的中间代码。可以在config文件中加入如下的配置,临时序列化的文件就不会被删除了,WinForm程序是*.exe.config,asp.net是web.config。
<configuration>
<system.diagnostics>
<switches>
<add name="XmlSerialization.Compilation" value="4"/>
</switches>
</system.diagnostics>
</configuration>
<system.diagnostics>
<switches>
<add name="XmlSerialization.Compilation" value="4"/>
</switches>
</system.diagnostics>
</configuration>
4)
在代理类上可以加
[System.Xml.Serialization.XmlSerializerAssemblyAttribute(AssemblyName = "TestPerformance.XmlSerializers")]
指定Xml序列化的类,这个序列化的类可以通过一个工具产生,
但是根据研究TempAssemlby的LoadGeneratedAssemlby代码发现,这个Attribute可以不加的,只要你有一个GetTempAssemblyName返回值一样的名称的序列化类即可。
5)
根据加载失败会触发
AppDomain
.CurrentDomain.AssemblyResolve
事件,可以在加载失败后动态产生序列化类,如下。
private void Form1_Load(object sender,EventArgs e)
{
AppDomain.CurrentDomain.AssemblyResolve +=
new ResolveEventHandler(MyResolveEventHandler);
}
static Assembly MyResolveEventHandler(object sender,ResolveEventArgs args)
{
Assembly a = null;
string[] arr = args.Name.Split(new string[] { "." },StringSplitOptions.None);
if (args.Name.IndexOf("XmlSerializers") >= 0)
{
if (!System.IO.File.Exists(args.Name + ".dll"))
PreGenNS.Pregen.Generate(new string[] { arr[0] });
string sSerializersDLL = args.Name + ".dll";
string smartDeploymentHostLocation = "";
a = Assembly.LoadFrom(smartDeploymentHostLocation + sSerializersDLL);
}
return a;
}
6)VS2005利用Release编译,会产生AssemblyName+"XmlSerializer.dll"的序列化文件,可以随着客户端一起部署,跟5这种方式不太一样,可以根据实际情况来选择。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。