【推荐】【给中高级开发者】构建高性能ASP.NET应用的几点建议

简介: 本篇目录 早期阶段就要对应用进行负载测试 使用高性能类库 你的应用是CPU密集还是IO密集的 使用基于Task的异步模型,但要慎重 分发缓存和会话(session)状态 创建Web Gardens 巧妙地使用缓存和懒加载 不要在MVC视图中放C#代码 适当时使用Fire & Forget 为x64 CPU创建 使用服务器上的监视和诊断工具 分析运行中的应用 如果你在构建一个面向公众的web站点,那么在项目结尾时你想要实现的就是web负载性能良好。

本篇目录

  • 早期阶段就要对应用进行负载测试
  • 使用高性能类库
  • 你的应用是CPU密集还是IO密集的
  • 使用基于Task的异步模型,但要慎重
  • 分发缓存和会话(session)状态
  • 创建Web Gardens
  • 巧妙地使用缓存和懒加载
  • 不要在MVC视图中放C#代码
  • 适当时使用Fire & Forget
  • 为x64 CPU创建
  • 使用服务器上的监视和诊断工具
  • 分析运行中的应用

如果你在构建一个面向公众的web站点,那么在项目结尾时你想要实现的就是web负载性能良好。这意味着,你要确保你的产品在高负载下(50个并发用户或者每秒200个用户等等)能够运行,即使你认为此时不会有那么大的负载。久而久之,你的web站点可能吸引越来越多的用户,此时如果web的负载难以让人忍受时,那么自然而然网站开始走下坡路,意味着客户流失以及名誉受损。

那么可以采取哪些措施可以使得一个ASP.NET或者ASP.NET MVC应用更加高性能呢?

早期阶段就要对应用进行负载测试

大多数开发者趋向在应用开发完成后,集成测试和回归测试通过后才进行负载测试。尽管在开发完成后执行一次负载测试好过不做,但是一旦完成了代码的编写,修复性能问题就为时已晚了。这个问题最常见的例子就是当应用程序在负载测试时不能正确响应时,就会考虑向外扩展(增加更多的服务器)。有时这是不可能的,因为代码不适合实现扩展服务器。比如当对象是存储在Session中并不可序列化时,这样添加更多的web节点或者工作者进程就是不可能的。如果你在开发的早期阶段就发现你的应用可能要部署到多台服务器上,那么你应该在配置和服务器的数量等方面都要让测试环境和最终环境尽可能地接近,这样你的代码会更容易适应。

使用高性能类库

近来我在诊断web站点的性能问题时发现了代码中的一个热点问题:来自第三方web服务的JSON信息必须要被反序列化多次。那些Json信息是由Newtonsoft.Json反序列化的,并且证明了Newtonsoft.Json在反序列化时不是最快的类库,然后我们使用了一个更快的类库(如ServiceStack)替代了Json.Net,并获得了更好的结果。

如果我们在挑选了Json.Net作为序列化类库的早期阶段就完成了负载测试,那么我们就会更早地发现性能问题,我们就不必再代码中做这么多的修改了,也不必再次完整地重新测试了。

你的应用是CPU密集还是IO密集的?

在开始实现web应用以及设计项目时,你要首先考虑的一件事就是你的应用是CPU密集的还是IO密集的?这对于了解扩展应用的策略是很重要的。

比如,如果你的应用是CPU密集的,那么你可能想使用同步模式,并行处理等等。然而,对于一个有很多IO受限的操作(例如和外部web服务或者网络资源如数据库通讯)的产品,基于Task的异步模式可能对于向外扩展更有帮助。此外,为了在未来的某天能够创建Web Gardens和Web Farms,你可能想有一个集中式的缓存系统,这样就实现了跨越多个工作者进程或服务的负载。

使用基于Task的异步模型,但要慎重

如果你的产品依赖许多IO受限的操作,或者包括了可能需要消耗宝贵的IIS线程等待一个操作完成的长时间运行的操作,你最好为ASP.NET MVC项目考虑使用基于Task的异步模式。

互联网上有很多关于异步的ASP.NET MVC actions的手册(比如这个),所以这篇博客尽量避免介绍关于异步的知识点。然而,必须指出ASP.NET (MVC)中传统的同步action会造成IIS线程繁忙直到操作完成或请求处理完成。这意味着如果如果web应用在等待一个外部的资源(如一个web服务)响应,那么该线程将是繁忙的。.Net线程池中可以用于处理请求的线程数量也是有限的,因此,尽可能快地释放线程是很重要的。基于Task的异步action或方法会在请求处理后释放该线程,然后从线程池中获取新的线程,并使用该新的线程返回该action的结果。这样,多个请求可以由多个线程处理,这会为你的应用带来更好的响应。

虽然TAP模式对于适当的应用来说很方便,但必须要慎重使用。当你使用TAP设计或者实现一个项目时,必须要注意几个点(你可以点击这里查看),然而,开发者使用async和await关键字面临的最大挑战是知道在这个上下文中他们必须略有不同地处理线程。比如,可以创建一个返回Task(如Task《Product》,博客园的markdown不支持单尖括号)的方法,通常你可以对那个Task调用Run()方法或者只调用task.Result来迫使运行该task,直到拿到结果。

分发缓存和会话(session)状态

开发者在单个开发机器上构建一个web应用是非常常见的,并且也会假设该产品运行在单个服务器上,然而对于面向大众的web通常不是这样的。它们往往被部署到一个叫做负载均衡之后的多个服务器上。尽管你仍然可以使用In-Proc(关于In-Proc和Out-Proc的知识点补充)的模式和粘性(sticky)Session将一个web应用部署到多个服务器上(负载均衡器会将属于相同session的所有请求定向到单个服务器),你可能必须保留session数据和缓存数据的多个副本。比如,如果你将产品部署到由4台服务器组成的web farm上,并且你保持session数据为In-Proc,那时一个请求到达一个已经包含缓存数据的机会是四分之一或25%,然而你如果在正确的地方使用了集中式缓存机制,那么为每个请求找到缓存条目的机会就是100%。这对于严重依赖缓存数据的web应用是至关重要的。

使用集中式缓存机制(像App Fabric或Redis)的另一个好处是对实际的产品实现主动缓存系统的能力。主动缓存机制可以用于在客户端请求一个条目之前就可以将最受欢迎的条目预加载到缓存中。如果你使用实际的数据源来管理缓存同步时,那么这会帮助你大幅地改善大数据驱动应用的性能。

创建Web Gardens

之前提到,在一个IO受限的web应用中包含了很多长时间运行的操作(如web服务的调用),此时你可能想尽可能地释放主线程。默认情况下,每个web应用运行在一个主线程下,该主线程为保持web站点存活(alive)负责,但不幸的是,当它太忙的时候,你的站点就变得不能响应了。给应用添加更多的“主线程”是一种办法,这是通过将更多的工作者进程添加到IIS下的Web站点来实现的。每个工作者进程都会包括一个单独的主线程,因此,如果一个主线程繁忙,还有另外的主线程来处理到来的请求。

拥有多个工作者进程会将你的web站点变成一个Web Garden,这要求你将Session和应用数据持久化到Out-Proc中(例如一个state server或者Sql Server)。

图片

巧妙地使用缓存和懒加载

无需强调,如果你将经常访问的一点数据缓存到内存中,那么这会减少数据库和web服务的调用。正如之前所说,这对于IO受限的应用特别有帮助,当网站处于低负载时可能会造成不幸。

提高站点响应的另一种方式是使用懒加载。懒加载意味着应用里不会有确定的数据片,但是它知道数据在哪里。比如,如果web页面上有一个下拉列表,这意味着要显示一个产品列表,一旦页面加载完毕,不必从数据库中加载所有的产品。你可以在页面中添加一个jQuery函数,该函数可以在第一次下拉时填充下拉列表。你也可以在代码中的许多地方使用相同的技术,比如当使用Linq查询和CLR集合时。

不要在MVC视图中放C#代码

ASP.NET MVC视图会在运行时编译而不是在编译时,因此,如果在视图中包含了太多的C#代码,那么你的代码不会编译后放到dll文件中。这样做不仅有害于软件的可测试性,还会使web应用更慢,因为每个页面都有花更长的时间获得显示(因为它们必须被编译)。将代码添加到视图中的其他缺陷在于它们不能异步运行,这样,如果你决定基于TAP来构建应用时,就不能充分利用视图中的异步方法和action了。

比如,如果你的代码中有这么一个方法:

public async Task<string> GetName(int code)
{

    var result = …

    return await result;

}

该方法可以在一个异步的ASP.NET MVC action的上下文中异步运行,比如:

public Task<ActionResult> Index( CancellationToken ctx )
{
    var name = await GetName( 100 );
}

但是如果在一个视图中调用这方法,因为该视图不是异步的,所以必须以一种线程阻塞(thread-blocking)的方式运行,如:

var name = GetName(100).Result;

.Result会阻塞运行的线程,直到GetName()处理完成了请求,这样该应用的执行会停止一会儿,然而当使用await关键字调用该代码时,该线程不会被阻塞。

适当时使用Fire & Forget

注:Fire & Forget,字面意思是发射,然后忘记,不去理会了。网络释义为射后不理。
如果两个或多个操作没有生成单个事务,那么你很可能不必按顺序运行它们。比如,如果一个用户在你的站点注册时创建了一个账户,一旦他们注册了,你就把他们的详细信息保存到数据库,然后给他们发送一封邮件,你不必等待邮件发送出去才结束这个操作。

在这种情况下,最好的方法很可能就是开启一个新的线程,让它给用户发送邮件,然后返回到主线程。这就叫做Fire& Forget,它可以提高应用的响应能力。

为x64 CPU创建

32-bit的应用局限于较低的内存量和可以访问更少的CPU功能/指令。要克服这些限制,如果你的服务器是64-bit的,那么要确保你的应用运行在64-bit模式下(通过确保在IIS的32-bit模式下运行网站的选项是不是开启)。然后为x64 CPU编译并生成代码而不是Any CPU。

x64有用的一个例子是提高数据驱动应用的响应能力和性能,有一个好的缓存机制是必须的。In-proc是消耗内存的,因为一切都存储在网站的应用程序池的内存边界。对于x86进程来说,可以分配的内存量限制到4GB,这样,如果加载到缓存中,这个限制很快就被打破。如果相同的站点是为x64 CPU显式生成的,那么内存限制就不需考虑了,这样更多的条目可以加到缓存中,然后和数据库的通讯就少了,这会带来更好的性能。

使用服务器上的监视和诊断工具

可能存在你肉眼看不到的更多的性能问题,因为它们不会出现在错误日志中。当应用程序已经部署到基本没有机会调试的服务器上时,识别性能问题是更吓人的事情。

要找出缓慢的处理,线程阻塞,悬挂,错误等等,强烈建议在服务器上安装一个监视和诊断工具,让它们持续地跟踪和监视你的应用程序。我个人使用的是NewRelic检查我的在线网站的健康。获取详细信息并创建免费账号看这里哦!

分析运行中的应用

一旦完成了网站开发,部署到IIS,然后附加一个分析器(如Visual Studio Profiler),然后抓取应用的多个部分的快照。比如抓取购买或者用户注册等操作的快照,然后检查病查看是否存在任何造成缓慢或阻塞的代码。在早期阶段找到那些热点可能会节省你大量的时间,荣誉和金钱。





本文转自tkbSimplest博客园博客,原文链接:http://www.cnblogs.com/farb/p/AspNetAppWithHighPerformance.html,如需转载请自行联系原作者

目录
相关文章
|
2月前
|
存储 Shell Linux
快速上手基于 BaGet 的脚本自动化构建 .net 应用打包
本文介绍了如何使用脚本自动化构建 `.net` 应用的 `nuget` 包并推送到指定服务仓库。首先概述了 `BaGet`——一个开源、轻量级且高性能的 `NuGet` 服务器,支持多种存储后端及配置选项。接着详细描述了 `BaGet` 的安装、配置及使用方法,并提供了 `PowerShell` 和 `Bash` 脚本实例,用于自动化推送 `.nupkg` 文件。最后总结了 `BaGet` 的优势及其在实际部署中的便捷性。
116 10
|
13天前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
|
2月前
|
数据采集 JSON API
.NET 3.5 中 HttpWebRequest 的核心用法及应用
【9月更文挑战第7天】在.NET 3.5环境下,HttpWebRequest 类是处理HTTP请求的一个核心组件,它封装了HTTP协议的细节,使得开发者可以方便地发送HTTP请求并接收响应。本文将详细介绍HttpWebRequest的核心用法及其实战应用。
118 6
|
3月前
|
数据库 C# 开发者
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
52 0
|
3月前
|
C# Windows 开发者
超越选择焦虑:深入解析WinForms、WPF与UWP——谁才是打造顶级.NET桌面应用的终极利器?从开发效率到视觉享受,全面解读三大框架优劣,助你精准匹配项目需求,构建完美桌面应用生态系统
【8月更文挑战第31天】.NET框架为开发者提供了多种桌面应用开发选项,包括WinForms、WPF和UWP。WinForms简单易用,适合快速开发基本应用;WPF提供强大的UI设计工具和丰富的视觉体验,支持XAML,易于实现复杂布局;UWP专为Windows 10设计,支持多设备,充分利用现代硬件特性。本文通过示例代码详细介绍这三种框架的特点,帮助读者根据项目需求做出明智选择。以下是各框架的简单示例代码,便于理解其基本用法。
133 0
|
3月前
|
开发者 API Windows
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
66 0
|
3月前
|
Java Spring 自然语言处理
Spring 框架里竟藏着神秘魔法?国际化与本地化的奇妙之旅等你来揭开谜底!
【8月更文挑战第31天】在软件开发中,国际化(I18N)与本地化(L10N)对于满足不同地区用户需求至关重要。Spring框架提供了强大支持,利用资源文件和`MessageSource`实现多语言文本管理。通过配置日期格式和货币符号,进一步完善本地化功能。合理应用这些特性,可显著提升应用的多地区适应性和用户体验。
40 0
|
2月前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
38 7
|
2月前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
54 0
|
3月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
46 0
下一篇
无影云桌面