当前位置: 首页 > 科技观察

构建高性能ASP.NET应用程序的12个技巧

时间:2023-03-18 11:37:53 科技观察

如果您正在构建一个面向公众的网站,那么您在项目结束时想要的是良好的Web加载性能。这意味着,您要确保您的产品可以在高负载下运行(50个并发用户或每秒200个用户等),即使您认为当时不会有那么大的负载。随着时间的推移,您的网站可能会吸引越来越多的用户。这时候,如果网站的负载不堪重负,那么网站自然会开始走下坡路,这意味着客户的流失和声誉的损害。那么可以采取哪些步骤来提高ASP.NET或ASP.NETMVC应用程序的性能呢?在早期阶段对应用程序进行负载测试大多数开发人员倾向于在应用程序开发完成并通过集成和回归测试后对应用程序进行负载测试。虽然在开发之后执行负载测试总比完全不执行要好,但是一旦代码编写完成,再修复性能问题就太晚了。这个问题最常见的例子是当应用程序在负载测试期间没有正确响应时,它被认为是向外扩展(添加更多服务器)。有时这是不可能的,因为代码不适合实现扩展服务器。例如,当对象存储在Session中且不可序列化时,就不可能再添加更多的web节点或worker进程。如果你在开发初期发现你的应用可能会部署到多台服务器上,那么你应该让测试环境在配置和服务器数量上尽可能接近最终环境,这样你的代码会更容易适应。使用高性能类库我最近在诊断网站性能问题时发现代码中的一个热点:来自第三方Web服务的JSON信息必须被多次反序列化。那些Json信息被Newtonsoft.Json反序列化,反序列化的时候证明Newtonsoft.Json不是最快的类库,然后我们用更快的类库(比如ServiceStack)代替Json.Net,得到了更好的结果。如果我们在选择Json.Net作为序列化库的早期就进行了负载测试,我们会更早地发现性能问题,我们就不必对代码进行如此多的更改,也不必完成它再次重新测试。您的应用程序受CPU限制还是IO限制?在开始实施Web应用程序和设计项目时,您首先应该考虑的事情之一是您的应用程序是CPU密集型还是IO密集型?这对于了解扩展应用程序的策略很重要。例如,如果您的应用程序是CPU密集型的,那么您可能希望使用同步模式、并行处理等。但是,对于具有很多IO-bound操作(例如与外部Web服务或数据库等网络资源通信)的产品,基于任务的异步模型可能更有利于横向扩展。此外,为了有朝一日能够创建网络花园和网络农场,您可能希望拥有一个集中式缓存系统,以便负载可以分布在多个工作进程或服务中。使用基于任务的异步模型,但要小心。如果您的产品依赖于许多IO绑定操作,或者包含可能消耗等待操作完成的宝贵IIS线程的长时间运行的操作,您最好提供ASP.NETMVC项目,考虑使用基于任务的异步模式。网上有很多关于异步ASP.NETMVC动作的手册(比如这篇),所以本篇博客尽量避免介绍异步相关的知识点。但是,必须指出的是,ASP.NET(MVC)中传统的同步动作会导致IIS线程忙碌,直到操作完成或请求处理完成。这意味着如果Web应用程序正在等待外部资源(例如Web服务)响应,那么线程将很忙。.Net线程池中可用于处理请求的线程数也是有限的,因此尽快释放线程很重要。基于任务的异步动作或方法会在处理完请求后释放线程,然后从线程池中获取一个新的线程,并使用新的线程返回动作的结果。这样,多个请求可以由多个线程处理,这将为您的应用程序带来更好的响应能力。虽然TAP模式对于适当的应用程序很方便,但必须谨慎使用。当您使用TAP设计或实施项目时,有几点您必须注意(您可以在此处查看),但是,使用async和await关键字的开发人员面临的最大挑战是知道他们必须在此上下文中处理线程略有不同。比如你可以创建一个返回一个Task的方法(比如Task《Product》,博客园的markdown是不支持单尖括号的),通常你可以调用那个Task的Run()方法或者直接调用task.Result强制任务运行,直到得到结果。分发缓存和会话状态开发人员在一台开发机器上构建Web应用程序并假设该产品在一台服务器上运行是很常见的,但是对于面向大众的Web来说通常情况并非如此。它们通常部署在多台服务器上,称为负载平衡。虽然你仍然可以使用In-Proc(关于In-Proc和Out-Proc的知识点补充)模式和粘性(sticky)Session将一个web应用程序部署到多个服务器(负载均衡器将属于同一个会话的所有请求都定向到单个服务器),您可能必须保留会话数据和缓存数据的多个副本。例如,如果您将生产部署到一个由4个服务器组成的网络场,并且您将会话数据保存在进程中,那么请求到达一个已经包含缓存数据的服务器的可能性是四分之一或25%,但是如果您使用在正确位置的集中式缓存机制,您有100%的机会为每个请求找到缓存条目。这对于严重依赖缓存数据的Web应用程序至关重要。使用集中式缓存机制(如AppFabric或Redis)的另一个好处是能够为实际生产实施主动缓存系统。主动缓存机制可用于在客户端请求项目之前将最流行的项目预加载到缓存中。如果您使用实际数据源来管理缓存同步,那么这可以帮助您大大提高大数据驱动应用程序的性能。在创建WebGardens之前提到过,在一个包含很多长时间运行的操作(比如Web服务调用)的IO受限的Web应用程序中,您可能希望此时尽可能释放主线程。默认情况下,每个Web应用程序都在主线程下运行,主线程负责保持网站的活动,但不幸的是,当它太忙时,您的网站就会变得无响应。向应用程序添加更多“主线程”的一种方法是在IIS下向网站添加更多工作进程。每个工作进程将包含一个主线程,因此如果一个主线程忙,还有其他主线程来处理传入的请求。拥有多个工作进程会将您的网站变成一个Web花园,这需要您将会话和应用程序数据持久保存到一个Out-Proc(例如状态服务器或SqlServer)。巧妙地使用缓存和延迟加载不用说,如果您将频繁访问的数据位缓存在内存中,那么这将减少数据库和Web服务调用。如前所述,这对IO-bound应用程序特别有用,当网站处于低负载时可能会导致不幸。另一种提高站点响应能力的方法是使用延迟加载。延迟加载是指应用程序没有某条数据,但它知道数据在哪里。例如,如果网页上有一个下拉列表,这意味着显示一个产品列表,而无需在页面加载后从数据库中加载所有产品。您可以向您的页面添加一个jQuery函数,该函数在第一次下拉时填充下拉列表。您还可以在代码中的许多地方使用相同的技术,例如在使用Linq查询和CLR集合时。不要将C#代码放在MVC视图中ASP.NETMVC视图是在运行时而不是编译时编译的,因此如果您在视图中包含过多的C#代码,您的代码将不会被编译并放入dll文件中间。这样做不仅不利于软件的可测试性,还会使Web应用程序变慢,因为每个页面都需要更长的时间才能显示(因为它们必须被编译)。向视图添加代码的另一个缺点是它们不会异步运行,因此如果您决定基于TAP构建应用程序,您将无法充分利用视图中的异步方法和操作。例如,如果您的代码中有这样一个方法:publicasyncTaskGetName(intcode){varresult=...returnawaitresult;}此方法可以在异步ASP.NETMVC操作的上下文中异步运行,例如:publicTaskIndex(CancellationTokenctx){varname=awaitGetName(100);}但是如果在视图中调用这个方法,因为视图不是异步的,所以必须以线程阻塞的方式运行,比如:varname=GetName(100).Result;.Result会阻塞正在运行的线程,直到GetName()完成对请求的处理,这样应用程序的执行会暂时停止,但是当用await关键字调用代码时,线程不会被阻止。在适当的时候使用Fire&Forget注意:Fire&Forget,字面意思是启动,然后忘记,忽略它。网上解释为shootandignore。如果两个或多个操作不生成单个事务,那么您可能不必按顺序运行它们。例如,如果用户在您的网站上注册时创建了一个帐户,一旦他们注册,您将他们的详细信息保存到数据库并向他们发送电子邮件,您不必等待电子邮件发送完成操作。在这种情况下,最好的方法可能是启动一个新线程,让它向用户发送一封电子邮件,然后返回到主线程。这称为Fire&Forget,它可以提高应用程序的响应能力。为x64CPU的32位创建的应用程序仅限于较少的内存量,并且可以访问较少的CPU函数/指令。要克服这些限制,如果您的服务器是64位的,请确保您的应用程序在64位模式下运行(通过确保未启用在IIS中以32位模式运行网站的选项)。然后为x64CPU而不是AnyCPU编译并生成代码。x64有用的一个示例是提高数据驱动应用程序的响应能力和性能,其中必须有良好的缓存机制。进程内会消耗内存,因为所有内容都存储在网站应用程序池的内存边界内。对于x86进程,可以分配的内存量限制为4GB,因此如果加载到缓存中,这个限制很快就会被打破。如果为x64CPU显式生成同一个站点,那么内存限制将被忽略,因此可以将更多条目添加到缓存中,然后与数据库的通信更少,从而带来更好的性能。在服务器上使用监控和诊断工具可能会出现更多肉眼看不到的性能问题,因为它们不会显示在错误日志中。当应用程序已经部署到几乎没有调试机会的服务器时,识别性能问题就更加令人生畏了。要找出处理缓慢、线程阻塞、挂起、错误等,强烈建议在服务器上安装一个监控和诊断工具,让它们持续跟踪和监控你的应用程序。我个人使用NewRelic来检查我的在线网站的健康状况。在此处获取详细信息并创建一个免费帐户!分析正在运行的应用程序一旦开发了网站,将其部署到IIS,然后附加一个分析器,例如VisualStudioProfiler,然后拍摄应用程序各个部分的快照。例如,抓取购买或用户注册等操作的快照,然后检查是否有任何代码导致缓慢或阻塞。尽早发现这些热点可能会为您节省大量时间、荣誉和金钱。