2011-09-27 90 views
13

我們有一個.NET Web角色託管於Windows Azure只能服務於REST API只有一個幾個網絡方法如何使用.NET在Azure上實現高性能REST API?

API被其他雲託管應用程序(非瀏覽器)積極使用。每種方法都是無狀態的,可以直接擴展,並且通常與Blob或表存儲進行交互。

然後違背最經典的API,數據量上傳到API通常比從API下載數據大得多。然後,平均消息的大小通常也相當大(即高於100kB)。

到目前爲止,我們是在帶有POX消息(Plain Old Xml)的ASP.NET Forms上使用WCF。前端性能不是很好,匪徒是:

  • XML是詳細的==>帶寬限制。
  • ASP.NET + WCF + WcfRestContrib緩慢解析/序列化消息==> CPU限制。

我想知道什麼是實現最高的前端性能的最佳策略,以減少虛擬機數量需要支持的工作量。

可能的策略,我在考慮:

  • 放棄XML支持的protobuf的。
  • 添加上游 GZip壓縮(傳統的HTTP壓縮僅適用於下游)。
  • 放棄WCF完全贊成原始HttpHandler s。

是否有人對各種替代方法進行了基準測試,以實現每個Azure虛擬機的最大用途?

Ps:暗指Lokad Forecasting API,但試圖用更一般的方式來說明問題。

+0

你發現什麼是問題/解決方案? – Rory

回答

1

在您的POC中,我認爲您可以在測試某些場景時從等式中刪除Azure。

如果這是真正的帶寬,壓縮無疑是一種選擇,但如果此Web服務將向「公衆」開放而不是由您控制的應用程序簡單使用,則可能會產生問題。在異質環境中尤其如此。

只要你有一個很好的手段,可以很好地溝通由於錯誤的格式造成的失敗,就可以選擇一種不太冗長的格式。 XML使這非常簡單。由於缺乏ProtoBuf的經驗,它似乎在這方面有一定的安全性,所以如果帶寬是你的問題並且可能解決分析問題的速度,它可能是一個非常好的選擇。我會在POC之外的Azure第一次,然後把它。

我只會運行原始HttpHandler方向,如果你有證據WCF開銷是一個問題。 Azure非常難以在配置中進行調試,以至於我不相信增加額外的原始HttpHandlers問題是正確的方向。

3

您的XML是否通過反射序列化(即使用屬性等)?如果是這樣,則protobuf-net代表much, much faster。實際上,即使您的XML序列化是使用明確的獲取器和設置器Func<>來定製的,您仍然可以看到protobuf-net的一些重大收益。在我們的例子中,根據序列化對象的大小和內容,我們看到序列化時間的速度提高了5-15%。

使用protobuf-net也會對可用帶寬產生影響,儘管這在很大程度上取決於您的內容。

我們的系統聽起來與您的系統完全不同,但FWIW我們發現WCF本身與其餘流量相比具有幾乎不可感知的低開銷。像dotTrace這樣的分析器可能有助於確定您切換到protobufs後可以保存的位置。

+0

更多人需要知道XML序列化的緩慢程度,您提供的鏈接說明了一切。 – knightpfhor

+1

+1分析。此外,自託管的WCF可能比WCF-over-ASP.NET更快。看到這裏:http://msdn.microsoft.com/en-us/library/ms731758.aspx –

0

我發現blob存儲(CreateCloudBlobClient(),GetContainerReference()等)的初始化非常慢。在設計Azure服務時考慮這一點是一個好主意。

對於需要blob訪問的任何事情,我都有單獨的服務,因爲它拖動了純數據庫請求的時間。

3

由於消息中包含大量數據或因爲它們包含文件,您的服務正在接收的消息的大小如此之大?

如果是第一種情況,那麼ProtoBuf的確是一個非常好的選擇。

如果郵件大小因爲它嵌入文件而很大,那麼我一直在成功使用的一種策略是爲您的服務方法創建兩個不同的體系結構:一個用於上傳和下載文件的方法,另一個用於只發送文件的方法並接收消息。

與文件相關的方法將以簡單的形式在HTTP請求正文內傳輸文件,而不需要任何轉換或編碼。剩下的參數將使用請求URL發送。

對於文件上傳,在WCF REST服務中,在服務方法中,您將不得不聲明表示類型爲Stream的文件的參數。例如:

[OperationContract] 
[WebInvoke(Method = "POST", UriTemplate = "uploadProjectDocument?projectId={projectId}")] 
void UploadProjectDocument(Guid projectId, Stream document); 

當遇到流參數,WCF將簡單地直接從請求的主體採取它們的內容,而不在其上作任何處理。您只能在服務方法上使用Stream類型的一個參數(這很有意義,因爲每個HTTP請求只有一個主體)。

上述方法的缺點是,除了表示文件的參數外,其他所有參數都需要基本類型(如字符串,數字,GUID)。你不能傳遞任何複雜的對象。如果你需要這樣做,你將不得不爲它創建一個單獨的方法,所以你最終可能會有兩個方法(它們會在運行時在兩個調用中進行轉換),此時你只有一個方法。但是,直接在請求主體中上傳文件應該比序列化效率更高,所以即使是額外的調用,也應該改進。

對於從服務中下載文件,您需要將WCF方法聲明爲返回流並簡單地將該文件寫入返回的對象中。與Stream參數一樣,WCF將直接將Stream的內容輸出到結果體中,而不對其進行任何轉換。

3

這篇文章http://social.msdn.microsoft.com/Forums/en-US/windowsazuredata/thread/d84ba34b-b0e0-4961-a167-bbe7618beb83涵蓋了Azure的性能問題。

默認情況下,Azure角色只能在單個線程中運行,這在服務器上效率非常低。有一些非常好的設計模式,向您展示如何實現多線程Azure角色,我個人會遵循這一個http://www.31a2ba2a-b718-11dc-8314-0800200c9a66.com/2010/12/running-multiple-threads-on-windows.html。有了這個你的角色可以並行地串行化對象。

我使用JSON作爲交換格式而不是XML,它具有小得多的字節大小,並且在.NET 4中得到很好的支持。我目前使用DataContractJsonSerializer,但如果是串行化,您也可以查看JavaScriptSerializer或JSON.NET我會建議你比較這些表現後你的表現。

WCF服務默認爲單線程(來源:http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=EN-US&k=k(SYSTEM.SERVICEMODEL.SERVICEBEHAVIORATTRIBUTE.CONCURRENCYMODE);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV4.0%22);k(DevLang-CSHARP)&rd=true)。下面是一個代碼示例,這將使你的問題的REST API的多線程:

ExampleService.svc.cs

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall, 
     IncludeExceptionDetailInFaults = false, MaxItemsInObjectGraph = Int32.MaxValue)] 
    public class ExampleService : IExample 

的web.config

<system.serviceModel> 
    <protocolMapping> 
     <add scheme="http" binding="webHttpBinding" bindingConfiguration="" /> 
    </protocolMapping> 
    <behaviors> 
     <endpointBehaviors> 
     <behavior name=""> 
      <webHttp defaultOutgoingResponseFormat="Json" /> 
     </behavior> 
     </endpointBehaviors> 
     <serviceBehaviors> 
     <behavior name=""> 
      <serviceMetadata httpGetEnabled="true" /> 
      <serviceDebug includeExceptionDetailInFaults="false" /> 
     </behavior> 
     </serviceBehaviors> 
    </behaviors> 
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> 
    </system.serviceModel> 

ExampleService.svc

<%@ ServiceHost Language="C#" Debug="true" Service="WebPages.Interfaces.ExampleService" CodeBehind="ExampleService.svc.cs" %> 

另外,ASP.NET默認只允許兩個併發的HTTP連接(源請參閱http://social.msdn.microsoft.com/Forums/en-US/windowsazuredata/thread/d84ba34b-b0e0-4961-a167-bbe7618beb83)。這些設置將允許多達48個併發的HTTP連接:

的web.config

<system.net> 
    <connectionManagement> 
     <!-- See http://social.msdn.microsoft.com/Forums/en-US/windowsazuredata/thread/d84ba34b-b0e0-4961-a167-bbe7618beb83 --> 
     <add address="*" maxconnection="48" /> 
    </connectionManagement> 
    </system.net> 

如果你的HTTP POST體消息通常是你應該把Nagling的提高性能的小於1460個字節(源http://social.msdn.microsoft.com/Forums/en-US/windowsazuredata/thread/d84ba34b-b0e0-4961-a167-bbe7618beb83)。下面是一些設置,這是否:

的web.config

<system.net> 
    <settings> 
     <!-- See http://social.msdn.microsoft.com/Forums/en-US/windowsazuredata/thread/d84ba34b-b0e0-4961-a167-bbe7618beb83 --> 
     <servicePointManager expect100Continue="false" /> 
    </settings> 
    </system.net> 

定義你的JSON API的是這樣的:

using System.ServiceModel; 
using System.ServiceModel.Web; 
using Interchange; 

namespace WebPages.Interfaces 
{ 
    [ServiceContract] 
    public interface IExample 
    { 
     [OperationContract] 
     [WebInvoke(Method = "POST", 
      BodyStyle = WebMessageBodyStyle.Bare, 
      RequestFormat = WebMessageFormat.Json, 
      ResponseFormat = WebMessageFormat.Json)] 
     string GetUpdates(RequestUpdates name); 

     [OperationContract] 
     [WebInvoke(Method = "POST", 
      BodyStyle = WebMessageBodyStyle.Bare, 
      RequestFormat = WebMessageFormat.Json, 
      ResponseFormat = WebMessageFormat.Json)] 
     string PostMessage(PostMessage message); 

    } 
} 

可序列化JSON英寸NET 4這樣的:

string SerializeData(object data) 
{ 
    var serializer = new DataContractJsonSerializer(data.GetType()); 
    var memoryStream = new MemoryStream(); 
    serializer.WriteObject(memoryStream, data); 
    return Encoding.Default.GetString(memoryStream.ToArray());    
} 

一個典型的交換實體可以定義爲正常:

using System.Collections.Generic; 
using System.Runtime.Serialization; 

namespace Interchange 
{ 
    [DataContract] 
    public class PostMessage 
    { 
     [DataMember] 
     public string Text { get; set; } 

     [DataMember] 
     public List<string> Tags { get; set; } 

     [DataMember] 
     public string AspNetSessionId { get; set; } 
    } 
} 

你可以寫你自己的上游gzip壓縮的HttpModule,但我會嘗試以上第一的東西。

最後,請確保您的表存儲與使用它們的服務位於相同的位置。

+0

我不認爲這是真的,Web角色只能運行在一個單一的線程,是嗎?您鏈接到的那篇文章僅涉及工作人員角色。 – Rory

+0

@Rory ASP.NET默認情況下只允許兩個併發的HTTP連接(參見http://social.msdn.microsoft.com/Forums/en-US/windowsazuredata/thread/d84ba34b-b0e0-4961-a167-bbe7618beb83) – 2013-05-22 14:15:21

3

我已經與ServiceStack一個非常愉快的經歷:

http://www.servicestack.net

這基本上是你最後的選擇;在HttpHandler之上的一個非常薄的層,具有快速的XML和JSON序列化,這暴露了一個REST API。它也提供的JSV序列化大約是我相信Protobuf.NET速度的一半,並且計劃支持ProtoBuf。

我不確定它是否運行在Azure上,但我想不出爲什麼它不能簡單地集成到任何ASP.NET應用程序中。

1

這裏是Benchmarks for different .NET serialization options

輸出都JSON序列化我爲基準我ServiceStack的JSON序列化的表現最好各地3倍比JSON.NET更快。這裏有幾個外部基準的表示該:

  1. http://daniel.wertheim.se/2011/02/07/json-net-vs-servicestack/
  2. http://theburningmonk.com/2011/08/performance-test-json-serializers/

ServiceStack(一個開放源碼的替代品WCF)來預先配置使用.NET最快的JSVJSON文本串行器OOB 。

我看到有人包含冗長的配置,關於如何讓WCF將其配置爲使用.NET附帶的較慢JSON序列化程序。在Service Stack中,每個Web服務都可以自動通過JSON,XML,SOAP(包括JSV,CSV,HTML)自動獲得,而無需任何配置,因此您無需任何額外的工作即可選擇最合適的端點。

相同數量的代碼和配置在服務棧的WCF例子就是:

public class PostMessage 
{ 
    public string Text { get; set; } 
    public List<string> Tags { get; set; } 
    public string AspNetSessionId { get; set; } 
} 

public class GetUpdatesService : IService<GetUpdates> 
{ 
    public object Execute(GetUpdates request){ ... } 
} 

public class PostMessageService : IService<PostMessage> 
{ 
    public object Execute(PostMessage request){ ... } 
} 

注:使用[DataContract]裝飾你的DTO是可選的。

ServiceStack Hello World示例顯示了創建Web服務後可自動使用的所有鏈接不同格式,元數據頁XSD模式和SOAP WSDL。