2011-09-27 46 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。