為什麼第一次調用WebService慢呢?

來源:互聯網
上載者:User

我們經常抱怨首次調用WebService比較慢,通常的做法是在程式啟動的時候,後台逐一調用一遍所有的WebService,還有人利用多線程來解決這個問題。其實,大家只是看到了問題的現象以及“工程”的解決辦法,而沒有接觸到問題的本質。經過本人反編譯.Net類庫,逐步尋找,應該說找到瞭解決這個問題的根本辦法。我們都知道,WebService是通過Soap協議來傳遞訊息的,所有的訊息都是XML,而在用戶端和伺服器端,都是使用的對象,這其中必然有一個XML和對象之間的轉換,這個轉換就是慢的罪魁禍首。下面,我就逐步分析,希望對你能有協助,請耐心看完。

1.       添加Web引用的時候,WebService在用戶端有一個代理,如下:

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "2.0.50727.42")]    [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 用戶端調用WebServivce就是通過這個代理類來調用的。 2.       調用WebService方法,用戶端和伺服器端通訊是Xml,所以代理類跟Xml之間就有序列化和還原序列化的過程3.       用戶端調用WebService的過程如下a)         用戶端調用代理類Hello world方法string str = (new Service2.WebService1()).HelloWorld ();b)         代理類調用基類SoapHttpClientProtocal的Invoke方法        [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));d)         XmlSerializer會緩衝暫存程序集,這個程式集作用是序列化和還原序列化,如果緩衝中沒有會調用TempAssembly產生一個 Static的緩衝(就是我們每次調用慢的罪魁禍首):private static TempAssemblyCache cache;擷取緩衝中的程式集: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這個類負責載入以及產生暫存程序集在LoadGeneratedAssemlby方法中,有一段邏輯,就是預設去載入序列化類別,這個類的命名是規則如下        internal static string GetTempAssemblyName(AssemblyName parent, string ns)        {            return (parent.Name + ".XmlSerializers" + (((ns == null) || (ns.Length == 0)) ? "" : ("." + ns.GetHashCode())));        }       同時,如果載入失敗會觸發AppDomain.CurrentDomain.AssemblyResolve事件         4.       結論1)   WebService的序列化是調用XmlSerializer 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> 臨時檔案在C:/Documents and Settings/抹布/Local Settings/Temp下,注意,因為名稱是隨機的,序列化的dll檔案,並不能重用,重開進程會重建。 3)   如果自訂序列化類別,可以跳過產生臨時序列化的步驟,大大提高第一次載入的速度,也就是說,只要有一個程式集名稱+“.XmlSerializers”的序列化類別存在,就不會動態產生序列化程式集了。 4)   在代理類上可以加[System.Xml.Serialization.XmlSerializerAssemblyAttribute(AssemblyName = "TestPerformance.XmlSerializers")]指定Xml序列化的類,這個序列化的類可以通過一個工具產生,但是根據研究TempAssemlby的LoadGeneratedAssemlby代碼發現,這個Attribute可以不加的,只要你有一個GetTempAssemblyName傳回值一樣的名稱的序列化類別即可。 5)   根據載入失敗會觸發AppDomain.CurrentDomain.AssemblyResolve事件,可以在載入失敗後動態產生序列化類別,如下。        http://support.microsoft.com/kb/872800/zh-cn,請參考這個kb      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這種方式不太一樣,可以根據實際情況來選擇。利用5這種方式,是第一次調用WebService時,動態產生序列化類別;而6是在軟體發布時,產生這個類,並部署到用戶端。      

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.