如何提高 WCF 数据服务性能

Posted

技术标签:

【中文标题】如何提高 WCF 数据服务性能【英文标题】:How to Improve WCF Data Services Performance 【发布时间】:2011-04-24 10:30:21 【问题描述】:

我是 WCF 数据服务的新手,所以我一直在玩。经过一些初步测试后,我对测试数据服务的性能感到失望。

我意识到,由于 WCF DS 是基于 HTTP 的,因此协议中存在固有的开销,但我的测试仍然比我预期的要慢:

环境:

一体机:四核 64 位笔记本电脑,配备 4GB RAM,运行 W7。不错的机器。 具有 16 个表的小型 SQL 数据库 (SQLExpress 2008 R2)...被测表有 243 行。 在 IIS 中使用所有默认值托管我的测试服务。

代码:

我已经为这个数据库(VS2010 的 stock codegen)创建了一个实体框架模型 (DataContext)。 我已经基于这个模型创建了一个数据服务。 我创建了一个客户端,该客户端具有此服务的直接服务引用 (ObjectContext)(VS2010 的股票代码生成) 在客户端我也可以直接调用 EF 模型,也可以使用 Native SQL (ADO.NET SqlConnection)

测试计划:

每次迭代都连接到数据库(可以选择重用连接),查询目标表中的所有行(“EVENTS”),然后对它们进行计数(从而强制执行任何延迟提取)。 为 Native SQL (SqlConnection/SqlCommand)、实体框架 (DataContext) 和 WCF 数据服务 (ObjectContext) 分别运行 25 次迭代。

结果:

25 次 Native SQL 迭代:436ms 25 次实体框架迭代:656 毫秒 25 次 WCF 数据服务迭代:12110 毫秒

哎哟。这比 EF 慢了大约 20 倍。

由于 WCF 数据服务是 HTTP,因此没有机会重用 HTTP 连接,因此每次迭代都强制客户端重新连接到 Web 服务器。但肯定不止于此。

EF 本身相当快,并且相同的 EF 代码/模型被重复用于服务和直接到 EF 的客户端测试。数据服务中的 Xml 序列化和反序列化会产生一些开销,但是那么多!?!过去我在 Xml 序列化方面取得了不错的成绩。

我将使用 JSON 和 Protocol-Buffer 编码运行一些测试,看看我是否可以获得更好的性能,但我很好奇社区是否有任何加快速度的建议。

我不擅长 IIS,所以也许可以设置一些 IIS 调整(缓存、连接池等)来改善这一点?

【问题讨论】:

有趣...一些观点、一些赞成票和一些最喜欢的添加,但没有答案。我正在为此提供赏金,以便为这个问题注入更多活力。希望有人能给出答案。 除非我打算将我的数据公开给其他应用程序,否则我不会使用 WCF 数据服务。如果一切都在同一个盒子上运行,为什么不直接使用 EF? 它不在同一个盒子上运行。但是数据源都在一个(非常大和国际化的)公司网络上。我试图在一堆不同的数据源(SQL、XML、平面文件等)前面放置一个服务层,将实际存储语义与发现和查询数据的能力隔离开来。 你是如何测试这个的。我曾经用 IE 测试过一个 web 服务。显示数据的浏览器开销是 80% 左右。 你在这个测试中使用了多少数据,可能是吞吐量。 【参考方案1】:

考虑改为部署为 Windows 服务? IIS 可能有它运行的 ASAPI 过滤器、重写规则等。即使这些都没有处于活动状态,IIS 管道也很长,可能会稍微减慢您的速度。

服务应该为您提供一个很好的基线,即在不降低 IIS 速度的情况下,请求运行、打包等需要多长时间

【讨论】:

这是个好主意。当我回到项目的这一部分时,我也会试一试。【参考方案2】:

下面的链接有视频,其中包含一些有趣的 WCF 基准测试以及 WCF 数据服务和实体框架之间的比较。

http://www.relationalis.com/articles/2011/4/10/wcf-data-services-overhead-performance.html

【讨论】:

【参考方案3】:

仅通过启用压缩,我将 WCF 数据服务 API 的性能提高了 41%。这真的很容易做到。请点击这个解释如何在您的 IIs 服务器上执行的链接:Enabling dynamic compression (gzip, deflate) for WCF Data Feeds, OData and other custom services in IIS7

更改后不要忘记 iisReset!

在客户端:

// This is your context basically, you should have this code throughout your app.
var context = new YourEntities("YourServiceURL");
context.SendingRequest2 += SendingRequest2;

// Add the following method somewhere in a static utility library
public static void SendingRequest2(object sender, SendingRequest2EventArgs e)

    var request = ((HttpWebRequestMessage)e.RequestMessage).HttpWebRequest;
    request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;

【讨论】:

【参考方案4】:

尝试在配置的绑定部分将安全设置为“无”。这应该会有很大的改进。

【讨论】:

好主意...我会试一试。当然,我的最终应用程序仍然需要安全性。但我很好奇开销是多少。【参考方案5】:

为了消除大部分连接开销,您可以尝试将所有操作批处理到 WCF DS 以查看是否有显着差异。

NorthwindEntities context = new NorthwindEntities(svcUri);
var batchRequests = 
     new DataServiceRequest[]someCustomerQuery, someProductsQuery;

var batchResponse = context.ExecuteBatch(batchRequests);

欲了解更多信息,请参阅here。

【讨论】:

只有在您提前知道要执行哪些操作时,才能进行批处理。就我而言,我需要对数据做出反应,所以不幸的是它是一个查询->处理->显示->查询->处理->显示的循环。【参考方案6】:

WCF DataServices 用于为您的不同客户端提供 OpenData 协议;这样您就不必为每个更改请求编写/重构多个 Web 服务方法。如果整个系统都是基于微软技术栈的,我从不建议使用它。它适用于远程客户端。

【讨论】:

【参考方案7】:

如何通过 WCF 的这 25 次迭代?

var WCFobj = new ...Service();
foreach(var calling in CallList)
   WCFobj.Call(...)

如果你这样调用,意味着你调用了 WCF 25 次,消耗太多资源。

对我来说,我曾经将所有内容都构建为DataTable,并将用户表名构建到我正在调用的存储过程中; DataRow 是参数。调用时,只需使用

以加密形式传递 DataTable
var table = new DataTable("PROC_CALLING")...
...
StringBuilder sb = new StringBuilder();
var xml = System.Xml.XmlWriter.Create(sb);
table.WriteXml(xml);
var bytes = System.Text.Encoding.UTF8.GetBytes(sb.ToString());
[optional]use GZip to bytes
WCFobj.Call(bytes);

问题是您一次通过所有 25 个调用,这可以显着节省性能。如果返回对象是相同的结构,只需将其作为DataTable以字节形式传递,然后将其转换回DataTable

我曾经用 GZip 实现这个方法来导入/导出数据模块。传递大量字节会使 WCF 不高兴。这取决于你想吃什么;计算资源或网络资源。

【讨论】:

这是专门针对 WCF 数据服务的...对于标准的 WCF 服务,这个问题是可以解决的,因为它可以更好地控制绑定和消息编码。【参考方案8】:

要尝试的事情:

1) 结果编码:尽可能使用 WCF 通道的二进制编码,请参阅http://msdn.microsoft.com/en-us/magazine/ee294456.aspx -- 交替使用压缩:http://programmerpayback.com/2009/02/18/speed-up-your-app-by-compressing-wcf-service-responses/

2) 更改您的服务实例行为,请参阅 http://msdn.microsoft.com/en-us/magazine/cc163590.aspx#S6 -- 尝试 InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode=ConcurrencyMode.Multiple - 如果您可以验证您的服务是否以线程安全的方式构建。

关于你的基准,我认为你应该模拟更真实的负载(包括并发用户)并忽略异常值,对 IIS 的第一个请求会非常慢(它必须加载所有 DLL)

【讨论】:

听起来您提供的 WCF 指南比特定的 WCF 数据服务指南更一般。不可能(据我所知)更改 WCF 数据服务的编码方法......它们必须是带有文本数据的 HTTP 端点...序列化格式可以是 Xml 或 Json,但必须是文本。所以压缩和二进制编码是不可能的(我希望是这样)。 我会看看实例化和并发性。 WCF 的最佳实践表明应该使用 PerCall 实例化,但具有多个并发的单个实例也可以工作。我试试看。 对于基准测试,真实性不是问题,因为不同提供商之间的操作保持一致。我已经丢弃了异常值......初始化运行大约需要 4-5 秒。如果只有一次,我可以忍受。 压缩适用于文本回复,显然二进制编码已失效。您正在谈论的所有 xml 数据都将占用大量时间。 PerCall 意味着您必须每次都实例化您的数据库连接, Single 意味着它们被缓存(同样,线程安全将是一个问题)。

以上是关于如何提高 WCF 数据服务性能的主要内容,如果未能解决你的问题,请参考以下文章

如何使用性能计数器监控 WCF 服务正常运行时间?

n 层应用程序中的 WCF 服务层:性能注意事项

如何提高 Oracle SQL 数据库或 Web 服务性能?

如何优化数据库,如何提高数据库的性能?

WCF 性能问题:请求停止处理

如何提高 ASP.Net Web 服务性能