随着.NET应用范围越来越广泛,对于大多数应用程序所有者和开发人员来说,确保.NET应用程序的优良性能是最重要的需求。
.NET应用程序运行缓慢的原因可能有很多。这些包括不正确的内存大小调整,GC暂停,代码级错误,异常的过多日志记录,同步块的高使用率,IIS服务器瓶颈等等。
在此博客中,我们将研究.NET应用程序中的一些常见性能问题,并提供解决和解决这些问题的技巧。
1.未处理的异常与记录过多异常日志
.NET异常不是一件坏事,只有错误的用法却是坏事。这就是大多数开发人员所相信的,如果对异常进行了适当的处理,即抛出,捕获和处理(并且不忽略),则会带来稳定的性能。
然而,就像太多的厨师会宠坏了汤一般,太多未处理的异常会导致代码效率低下并影响应用程序性能[1]。
尤其是隐藏的异常更糟,这种隐藏异常就像雷区,一旦未能检查这些异常,它们会影响网页加载时间。
.NET的另一个问题是过多地记录了异常。日志记录可能是您的调试工具库中的一个好工具,它可以识别在处理应用程序时记录的异常。
但是,当设置日志记录来捕获应用程序体系结构每一层的异常时,最终可能会在Web,服务和数据层上记录相同的异常。这可能会增加应用程序代码的负担,并增加响应时间。在生产环境中,只需要记录致命事件和错误就需要小心。
记录所有信息,包括参考消息,调试和警告,很容易使您的生产日志文件file臃肿,进而影响代码处理。
有用的疑难解答提示:
- 确保您的C#代码具有“尝试最终捕获”块以处理异常。
- 利用C#6和更高版本中提供的异常过滤器,该过滤器允许为每个catch块指定一个条件子句
- 检查空值,并使用TryParse避免潜在的异常。
- 请注意第二次机会异常,因为它们表明出现了第一次机会异常,并且未正确处理它。
- 使用异常处理和日志记录库(例如企业库,NLog,Serilog或log4net)将异常记录到文件或数据库中。
- 确保仅根据需要记录异常,并且最终不会使日志文件膨胀。
2.过度使用线程同步和Lock
.NET Framework提供了许多线程同步选项,例如进程间互斥,读取器/写入器锁等。
有时,.NET开发人员将以这样的方式编写代码:在给定的条件下,只有一个线程可以得到服务。
时间以及其他要处理的并行线程将不得不在队列中等待。例如,结帐应用程序根据其业务逻辑应一次处理一个请求。同步和锁定有助于序列化传入线程以执行。通过创建同步的代码块并在特定对象上施加锁定,需要传入线程等待直到同步对象上的锁可用为止。尽管此策略在某些情况下会有所帮助,但不应过度使用。
有用的疑难解答提示:
- 使用同步编码,仅在必要时使用锁。在决定使用锁之前,请先了解代码执行的需求。
- 最佳地调整锁的持续时间范围,以便延迟获取并提前释放它们,并且不会长时间等待其他线程。
- 为了减少并发问题,请考虑使用松散耦合。事件委托模型也可以用于最小化锁争用。
- 使用代码分析工具[2]监视.NET代码,以识别线程锁定是否导致应用程序处理缓慢。
3.应用程序挂起
有时特定的URL缓慢时,这是一回事。但是,当IIS网站刚刚挂起并且所有或大多数网页需要永久加载时,它不会变得更糟。通常,当应用程序过载或死锁时,可能会挂起。.NET应用程序通常会遇到两种类型的应用程序挂起方案。
硬挂(IIS问题):这通常发生在请求处理管道的开始–在请求排队的地方。由于应用程序死锁,所有可用线程都可能被阻塞,导致随后的传入请求最终在等待服务的队列中结束。当活动请求数超过IIS服务器上配置的并发限制时,也会发生这种情况。此类挂起将表现为请求超时并收到503 Service Unavailable错误。硬性影响所有URL和整个Web应用程序本身。
有用的疑难解答提示:
- 不断跟踪IIS服务器中队列中的请求数(Windows性能监视器中为Http Service Request Queues \ ArrivalRate)。这决不能超过为工作进程配置的请求处理限制。
- 还跟踪队列中的请求等待时间(Windows性能监视器中的Http Service Request Queues \ MaxQueueItemAge)。这将有助于检测应用程序是否面临潜在的挂起。
- 还可以通过监视IIS服务器事件来注意服务不可用和连接超时错误。
软挂(ASP.NET问题):这通常是由于特定段中的应用程序代码错误而造成的,仅影响几个URL而不影响整个网站。通常,由ASP.NET控制器或页面引起的挂起发生在
ExecuteRequestHandler
阶段。为了确认这一点,您可能想调试一下调试器,以确切了解请求被卡在哪里。检查模块名称,阶段名称和URL。URL将指示导致挂起的控制器/页面。
有用的疑难解答提示:
- 通过检查Windows性能监视器中的Http服务请求队列\ CurrentQueueSize计数器,验证IIS是否是问题。如果为0,则IIS队列中没有任何请求。
- 如果不是IIS问题,则必须是ASP.NET控制器/页面中的代码级问题。
- 使用任何代码分析,确定哪些URL挂起并获得详细的请求跟踪。验证请求挂起的模块名称和阶段名称,以确认这是ASP.NET问题。
- 使用事务跟踪工具[3]进行代码分析可以帮助识别存在问题的确切代码行。
4.频繁垃圾回收暂停
当托管堆上分配的对象使用的内存超过应用程序开发人员配置的可接受阈值时,.NET CLR中的垃圾回收(GC)会初始化。这是GC.Collect方法跳转到动作并回收死对象占用的内存的时候。CLR中的GC通常发生在存储短期对象的第0代堆中。当GC发生在包含长期对象的第二代堆中时,它称为Full GC。每次发生GC都会在CLR上增加大量CPU负载,并减慢应用程序的处理速度。因此,如果GC暂停时间更长且更频繁,则应用程序将趋于放缓。
有用的疑难解答提示:
- 适当调整GC堆内存的大小,并确保已根据需要设置GC限制。
- 避免在不需要它们的地方使用对象和大字符串。
- 跟踪GC的实例,GC花费的时间以及JVM花费的GC时间的百分比。
- 寻找发生完全GC的时间。这可能导致应用程序变慢。
- 根据应用程序需求明智地使用服务器GC或工作站GC。
- 端到端监视CLR层以识别内存使用情况,GC活动,CPU峰值等。
5.IIS服务器瓶颈
Microsoft IIS Server是.NET Framework的关键部分。IIS是Web服务器,它承载构建于.NET上的Web应用程序或网站,并运行W3WP进程,该进程负责响应传入的请求。IIS还集成了公共语言运行时(CLR),该运行时负责为线程处理分配资源。由于IIS具有各种活动部分,因此IIS中的瓶颈可能会对.NET应用程序性能产生直接的负面影响。
常见的IIS服务器问题:
- 由于内存,CPU等资源的过度利用而导致服务器超载
- 高并发连接数和连接数下降
- 应用程序池故障
- SSL证书已过期
- ASP.NET请求处理服务的高响应时间
- 高CLR等待时间
- 缓存不正确
- HTTP错误,包括静态和动态内容错误以及连接错误
有用的疑难解答提示:
- 调整IIS服务器的大小,以便不存在资源争用或资源过度利用的情况。
- 根据传入请求的速率与更多IIS服务器进行负载平衡。
- 跟踪SSL证书的有效性,并在证书过期之前主动发出警报。
- 监视IIS性能[4],应用程序池,网站的所有方面,[5]并识别不正确的配置和性能偏差。
6.数据库慢查询
并非总是会影响应用程序性能的.NET代码问题。运行缓慢的查询通常是常见的原因。但是通常是.NET应用程序开发人员因应用程序性能下降[6]而受到指责。
这样做的原因是,SQL性能如何影响.NET应用程序处理没有上下文可见性。ADO.NET和ODP.NET连接问题可能是查询处理缓慢的原因之一,但常见原因是查询的格式不正确。执行计划不正确,索引缺失,架构设计不当,缓冲池较小,联接缺失,缓存不正确,连接未正确进行池化等也是导致数据库查询处理受到影响的原因。
虽然DBA负责数据库性能和查询创建,但.NET应用程序所有者需要在应用程序处理期间跟踪查询级别的问题。这将有助于区分代码级问题和数据库问题,并且.NET开发人员不必花时间寻找代码中的问题。
有用的疑难解答提示:
- 在应用程序事务的上下文中监视查询处理以识别慢查询。
- 适当规划数据库的大小和配置,以确保一致的性能。
- 使用数据库监视工具[7]来识别和修复丢失的索引,通过重新索引来优化数据库布局等。
- 跟踪数据库与应用程序的连接,以隔离所有连接问题。
温馨提示:除了数据库调用速度慢之外,由于外部调用[8](例如HTTP,Web Service,WCF)也可能导致速度慢[9]。
7.基础设施故障:尽管不是.NET框架的问题,但仍是一个.NET问题!
.NET Framework不是独立的层。使用.NET Framework的应用程序将与基础架构(例如任何虚拟服务器,容器或云基础架构)有很多依赖性。然后,可能会有后端存储设备。尽管这些不是直接的.NET问题,但是这些基础结构组件中的任何一个问题都可能同样影响.NET性能。
就像我们看到IIS服务器和数据库可能遇到瓶颈一样,VM可能会耗尽资源,SAN阵列可能会遇到无法处理的高IOPS,或者如果.NET应用程序托管在Azure上,那么可能会有一个应用程序服务运行不正常。
在大多数应用程序环境中,与网络相关的投诉都位居榜首。无论是网络问题还是应用程序问题之间总是存在责备游戏。网络拥塞,丢包或设备故障可能会影响应用程序的性能和连接性。
有用的疑难解答提示:
.NET应用程序环境的总体性能保证要求应用程序与支持基础结构之间的依存关系具有相关的可见性。确保实施融合的应用程序和基础结构监视策略以捕获基础结构问题。
当您专注于捕获和解决所有这些问题时,请务必记住,编写干净而高效的代码可以解决.NET方面的许多问题。编写良好的代码,保持系统和基础架构的健康,并实施必要的工具以监控自动化。这将帮助您提供高性能的.NET应用程序和数字体验。
References
[1] 应用程序性能: https://www.eginnovations.com/blog/what-is-application-performance-monitoring/
[2] 代码分析工具: https://www.eginnovations.com/microsoft-net-monitoring
[3] 事务跟踪工具: https://www.eginnovations.com/microsoft-net-monitoring
[4] 监视IIS性能: https://www.eginnovations.com/iis-monitoring
[5] 所有方面,: https://www.eginnovations.com/iis-monitoring
[6] 应用程序性能下降: https://www.eginnovations.com/webinar/my-application-is-slow-troubleshooting-prevention/
[7] 数据库监视工具: https://www.eginnovations.com/database-monitoring
[8] 速度慢之外,由于外部调用: https://www.eginnovations.com/microsoft-net-monitoring#supported
[9] 速度慢: https://www.eginnovations.com/microsoft-net-monitoring#supported