不可忽视的 .NET 应用5大性能问题

实现有效 APM 策略所面临的挑战:

  • 代码依赖
  • 过度或不必要的日志
  • 同步与锁
  • 潜在数据库问题
  • 潜在的基础架构问题

1、代码依赖

开发程序是一项具有挑战性的工作。你不仅要为了满足商业需求而建立程序逻辑,还要选择最合适的代码库和工具来帮助你。你能想象自己创建所有的日志管理代码,XML 和 JSON 解析逻辑,或所有的序列化库么?你当然可以编写代码来完成这些事,但是诸多开源开发者团队已经做好了这些事情,你又何必亲力亲为呢?此外,如果你正在与第三方系统集成,你会自己读完专有的通信协议规范,还是购买供应商提供的库帮你完成呢?

我相信你会同意:如果有人已经解决了你的问题,使用他的解决办法会比自己想办法解决效率更高。如果这是一个已经被许多公司采用的开源项目,那么很可能它已经经过完备的测试,文档充足,而且你应该找得到许多使用教程。

然而,使用依赖库是有危险的。你需要回答以下问题:

  • 这个库真的写得很好并且已经充分测试了吗?
  • 你是否用与众多公司一样的方式使用这个库?
  • 你的使用方式是否正确?

请确保在选择外部库之前进行一些调查,如果你对某个库的性能有什么疑问,那就进行一些性能测试。开源项目很好的地方在于你可以访问它们的全部源代码以及测试套件和构建流程。下载它们的源代码,执行编译过程,并查看测试结果。如果你看到很高的测试覆盖率,那么就可以比没有测试案例时信心百倍!

最后,确保正确地使用依赖库。如果正确使用,ORM 工具的确能够大大提高性能。ORM 工具的问题在于,如果你不花时间去学习如何正确地使用它,你就会轻易的砸自己脚,破坏自己的应用性能。关键就在于如果不花时间学习这些工具,本应帮助你的工具反而会伤害你。

2、过度或不必要的日志

日志记录是调试工具库里的强大武器,可以帮助你识别应用执行过程中在特定时间内可能发生的异常。当错误发生时,捕捉错误信息并收集尽可能多的上下文信息是非常重要的。然而,简洁地捕捉错误条件和过度记录之间是有差别的。

最普遍的两个问题就是:

  • 多级别异常日志
  • 错误配置生产日志级别

异常日志能帮助你了解应用程序中发生的问题,因而非常重要。但一个常见的问题是,应用程序所有层级的异常都进行记录。例如,你的某个数据访问对象捕获到一个数据库异常,并将该异常传达到服务层。服务层可能会捕捉该异常,并将其传达到网络层。如果我们在数据层、服务层和网络层上都记录该异常,那么我们对此相同的错误条件就有三条堆栈记录。这会导致写入日志文件的额外负担,还会使日志文件充满冗余信息。但这个问题非常普遍,我敢断言,如果你检查自己的日志文件,你很可能会发现多个这样的例子。

生产应用中常见的另一个大的日志问题与日志级别有关。.NET 日志记录器定义了以下日志记录级别(.NET TraceLevel 与 log4net 中的命名会有所不同,但绝对相似):

  • Off
  • Fatal
  • Error
  • Warning
  • Info
  • Verbose / Debug

在生产应用程序中,你应该只记录 error 或 fetal 级别的日志语句,在更宽松的环境中,捕捉 warning 甚至 info 级别的日志信息也完全可以,但是一旦应用投入生产环境,用户负载将迅速填满日志并使应用程序陷入瘫痪。如果你不经意地将生产环境下的应用日志级别设为 debug,应用的响应时间比正常情况下高两或三倍都不奇怪!

3、同步与锁

有时候,你想确保应用代码中每次只有一个线程执行一段代码子集。 例如,读取单线程规则执行组件之类的共享软件资源,以及文件句柄或网络连接之类的共享基础架构资源。.NET 框架提供了许多不同类型的同步策略,包括锁/监视器、进程间互斥,和读/写锁这类的专用锁。

不管你为什么要同步代码或者选择什么机制实现代码同步,都会导致一个问题:那就是有部分代码一次只能由一个线程执行。 设想去超市,只有一个收银员在工作:许多人进入商店,浏览商品,将商品放进购物车里,但某一时候,他们不得不排队以进行支付。在这个例子中,购物是多线程的,每个人都代表一个线程。然而结账是单线程的,这意味着每个人都要花费排队付款的时间。这个过程如图1所示。

 图1:线程同步

我们有七个线程,都需要访问一段同步代码块,所以它们依次获得权限访问该代码块,执行其功能,然后继续。

在图2中总结了线程同步的过程。 

图2 线程同步过程

首先,为特定的对象(System.Object 派生)创建锁,意味着当一个线程试图进入同步代码块时必须获取该同步对象的锁。如果该锁可用,则该线程被授予执行同步代码的权限。在图2中的例子中,当第二个线程到达时,第一个线程已经占有了该锁,所以第二个线程被强制等待,直到第一个线程执行完毕。当第一个线程执行结束时,会释放该锁,然后第二个线程被授予访问权限。

正如你可能猜测到的,线程同步将给 .NET 应用带来一个极大的挑战。我们设计应用程序时,希望其能支持数十个甚至数百个同步请求,但线程同步会把所有处理这些请求的线程串行化,导致性能瓶颈!

解决的办法有两种:

  • 仔细检查同步的代码,以确定是否存在其他可行办法
  • 限制同步代码块的范围

有时候,你要访问必须同步的共享资源,但很多时候,你可以用完全避免同步的方法重新解决该问题。例如,我们之前使用的规则过程引擎有单线程的要求,因此拖慢了程序中所有请求的执行速度。这显然是一个设计上的缺陷,我们可以用一个可以并行工作的库取代之。你需要问自己是否有更好的选择:如果你在往一个本地文件系统写入信息,你是否可以把信息发送给某项服务,再由该服务将信息存储到数据库中?你是否可以将对象设为不可变,从而无论是否有多线程访问它都没关系?等等,等等…

对于那些必须要同步的代码段,请合理地选择锁。你的目标是将同步代码块隔离以满足最低限度的同步要求。通常最好是定义一个特定的对象进行同步,而不是对包含同步代码的对象进行同步,因为你可能会在不经意间拖慢该对象的其他交互。最后,考虑使用读/写锁,而不是标准的锁,这样,你可以在资源只进行同步变化时,允许读操作。

4.潜在的数据库问题

几乎所有的内容应用最终都会涉及到向/从数据库或文档存储储存/检索数据。因此,数据库、数据库查询,以及存储过程调优对应用程序的性能来说是最重要的。

程序架构师/开发人员和数据库架构师/开发人员之间有一个哲学性的划分。应用程序架构师倾向于认为所有的业务逻辑都应该驻留在应用程序中,数据库应该只提供访问数据的通道。另一方面,数据库架构师更倾向于认为将业务逻辑推到数据库中更有益提高性能。这个划分的答案很可能就是介于两者之间。

作为一个应用程序架构师,我倾向于将更多的业务逻辑应用在程序当中,但我完全承认数据库架构师能更好的理解数据和与数据交互的最佳方式。我认为,这两个群体之间的协同合作才能产生最佳的解决方案。但是,不管你倾向于哪一方,请确保你的数据库架构师检查你的数据模型,所有的查询语句和存储过程,他们都有丰富的知识帮助你以最佳的方式来调整和配置数据库,他们有大量的工具可以为你调整查询语句。例如,有一些工具可用于 SQL 调优,遵循以下这些步骤:

  • 分析 SQL 语句
  • 确定查询的执行计划
  • 利用人工智能生成备选的 SQL 语句
  • 确定所有备选方案的执行计划
  • 提出最佳的查询方式来完成目标

当我在写数据库查询代码时,我使用了这类工具,并在高负载情况下量化了结果,一些细微的调整和优化,都能导致极大的性能提升。

5、潜在的基础架构问题

之前提过,.NET 应用运行在分层的环境中,其层级结构如图3所示:

 图3.NET分层执行模型

你的应用程序运行在 ASP.NET 或是 Windows Forms 容器中,使用 ADO 库与运行在 CLR 内部的数据库交互,而 CLR 运行在操作系统中,操作系统又运行在硬件里。而该硬件又与其他包含不同技术堆栈的硬件通过网络相连。在你的应用与外部环境之间,以及在应用的组件之间,通常有多个负载平衡器。我们还有 API 管理服务以及多级缓存。所有这一切,都是为了说明,基础构造数量庞杂,都可能影响应用程序的性能!

因此,你必须细致地调整基础架构。检查你的应用组件和数据库所运行的操作系统和硬件设备,以确保它们的最佳表现。测量服务器之间的网络延迟,并确保你有足够的带宽来满足应用程序之间的交互。检查缓存,确保较高的缓存命中率。分析负载平衡器的行为以确保请求很快地分发到所有可用的服务器。总之,你需要全面检查应用程序的性能,既包括应用业务交易也包括支持它们的基础架构。

(0)

相关推荐

  • ASP.NET性能优化之构建自定义文件缓存

    现在,借助于.NET4.0中的OutputCacheProvider,我们可以有多种选择创建自己的缓存.如,我们可以把HTML输出缓存存储到memcached分布式集群服务器,或者MongoDB中(一种常用的面向文档数据库,不妨阅读本篇http://msdn.microsoft.com/zh-cn/magazine/gg650661.aspx).当然,我们也可以把缓存作为文件存储到硬盘上,考虑到可扩展性,这是一种最廉价的做法,本文就是介绍如果构建自定义文件缓存. 1:OutputCachePro

  • .NET获取枚举DescriptionAttribute描述信息性能改进的多种方法

    一. DescriptionAttribute的普通使用方式 1.1 使用示例 DescriptionAttribute特性可以用到很多地方,比较常见的就是枚举,通过获取枚举上定义的描述信息在UI上显示,一个简单的枚举定义: public enum EnumGender { None, [System.ComponentModel.Description("男")] Male, [System.ComponentModel.Description("女")] Fem

  • Asp.net 网站性能优化二则分享

    随着后端优化空间越来越小,现在越来越多的网站更注重前端性能的优化,就是浏览器,http层面的优化,这里写两点最简单最有效的 asp.net网站优化技巧. 了解常见的网站性能优化技巧 首先我们要学一些优化网站性能和体验的理论和基础知识,雅虎已经帮我们总结好了,详见参考链接中的几篇文章,有好心人已经给翻译成中文了.这里面提到的一些技巧可操作性都很强,建议每一个网站开发人员都仔细学习并进行实践,可以说不了解这里面说的优化技巧,提升网站前端性能就无从谈起. 诊断网站性能 要想优化网站的前端性能,首先要查

  • 分享提高ASP.NET Web应用性能的技巧

    在这篇文章中,将介绍一些提高 ASP.NET Web 应用性能的方法和技巧.众所周知,解决性能问题是一项繁琐的工作,当出现性能问题,每个人都会归咎于编写代码的开发人员. 那性能问题到底该如何解决?以下是应用系统发布前,作为 .NET 开发人员需要检查的点. 1.debug=「false」 当创建 ASP.NET Web应用程序,默认设置为「true」.开发过程中,设置为「true」是非常有用多,但在应用程序发布部署时,需将其设置为「false」. <compilation defaultLang

  • 不可忽视的 .NET 应用5大性能问题

    实现有效 APM 策略所面临的挑战: 代码依赖 过度或不必要的日志 同步与锁 潜在数据库问题 潜在的基础架构问题 1.代码依赖 开发程序是一项具有挑战性的工作.你不仅要为了满足商业需求而建立程序逻辑,还要选择最合适的代码库和工具来帮助你.你能想象自己创建所有的日志管理代码,XML 和 JSON 解析逻辑,或所有的序列化库么?你当然可以编写代码来完成这些事,但是诸多开源开发者团队已经做好了这些事情,你又何必亲力亲为呢?此外,如果你正在与第三方系统集成,你会自己读完专有的通信协议规范,还是购买供应商

  • web性能优化之javascript性能调优

    JavaScript 是一个比较完善的前端开发语言,在现今的 web 开发中应用非常广泛,尤其是对 Web 2.0 的应用.随着 Web 2.0 越来越流行的今天,我们会发现:在我们的 web 应用项目中,会有大量的 JavaScript 代码,并且以后会越来越多.JavaScript 作为一个解释执行的语言,以及它的单线程机制,决定了性能问题是 JavaScript 的软肋,也是 web 软件工程师们在写 JavaScript 需要高度重视的一个问题,尤其是针对 Web 2.0 的应用.绝大多

  • Python性能优化技巧

    Python是一门非常酷的语言,因为很少的Python代码可以在短时间内做很多事情,并且,Python很容易就能支持多任务和多重处理. py 1.关键代码可以依赖于扩展包 Python使许多编程任务变得简单,但是对于很关键的任务并不总是提供最好的性能.使用C.C++或者机器语言扩展包来执行关键任务能极大改善性能.这些包是依赖于平台的,也就是说,你必须使用特定的.与你使用的平台相关的包.简而言之,该解决方案提供了一些应用程序的可移植性,以换取性能,您可以获得只有通过直接向底层主机编程.下面这些扩展

  • 前端性能优化及技巧

     前言        为什么要优化性能对于前端工程师如此重要 在行业内有句话不知道大家有没有听说过,'懂得性能优化并且研究过jquery源代码的人和不懂得性能优化写出来的代码对于性能的消耗会相差上百倍甚至上千倍',现在的javascript属于从ECMAscript3到ECMAscript5以及ECMAscript6的一个过渡的过程.在javascript的编写不健全的时候编写代码方法不得当,引起的问题也是不容忽视的. 性能优化 下面将自己对于性能优化的一些见解与大家分享: 1.精灵图     

  • JavaScript知识点总结之如何提高性能

    JavaScript的性能问题不容小觑,这就需要我们开发人员在编写JavaScript程序时多注意一些细节,本文非常详细的介绍了一下JavaScript性能优化方面的知识点,绝对是干货. 先给大家巩固下javascript基本语法: javascript基本语法 定义变量统一用var关键字 语法:var 变量名称=变量值 标示符:①.由字母数字下划线构成 不能以数字开头 不能是关键字 严格区分大小写 数据类型: 数值型:number 字符串:string 布尔型:boolean 特殊数据类型:u

  • JavaScript提高性能知识点汇总

    一.针对js文件的加载位置 在HTML文件中,<script>标签是可以加在<head>区域和<body>区域的.这里鉴于JavaScript执行和UI渲染的单线程原因,如果js文件载入会阻塞后面对于页面的解析过程,页面会等到js文件完全加载并运行后才继续执行该做的操作.那么问题就来了,这样可能会出现页面空白or卡顿现象.作为一名前端开发,重要的不仅仅止于实现了需求,应该还有优质的用户体验.那么我们就需要消除用户枯燥的等待,针对这个问题,这里有本兽想到的两种解决方案:

  • MySQL性能优化技巧分享

    MySQL性能优化 在互联网公司MySQL的使用非常广泛,大家经常会有MySQL性能优化方面的需求.整理了一些在MySQL优化方面的实用技巧. Schema与数据类型优化 整数通常是标识列最好的选择,因为它们很快并且可以使用AUTO_INCREMENT 完全"随机"的字符串(如:MD5().SHA1()或者UUID()等产生的字符串)会任意分布在很大的空间内,会导致INSERT以及一些SELECT语句变的很慢 如果希望查询执行得快速且并发性好,单个查询最好不要做太多的关联查询(互联网公

  • 菜鸟购卡慎防 显卡选购常见五大误区

    1 前言 每年春节过后,适逢各大高校开学,不少学子重返校园,市场亦迎来了传统的销售旺季. 对于涉足电脑硬件不久的朋友来说,面对市场上众多的显卡,他们都很难一一区分开来的,而在选购显卡时,也难免会陷入一些误区:"频率高的显卡就快,显存容量越大性能就越好,买显卡一定要购买支持DX10的....."不少消费者都对显卡的运行频率和显存容量的参数都过分执着,而往往在选购显卡时就会走进了误区. 为了帮助广大用户在选购显卡时避免走进误区,笔者今天进总结出购买显卡时需要特别注意的五点,希望对广大消费者

  • PHP生成器简单实例

    一般你在迭代一组数据的时候,需要创建一个数据,假设数组很大,则会消耗很大性能,甚至造成内存不足. 复制代码 代码如下: //Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes) in E:\php\test\index.php on line 5 range(1, 100000000); PHP5.5实现了生成器,每当产生一个数组元素则用yield关键词返回,并且执行函

  • 通用PHP动态生成静态HTML网页的代码

    我们知道,PHP读取MYSQL动态显示,在访问量大的情况下,会有很多性能问题,如果租用别人的虚拟主机,则会因为CPU消耗过多而被限制CPU,导致网页无法访问.我这里给出一个PHP动态生成HTML的方法,可以极大降低服务器CPU负荷. 首先设置.htaccess文件,将动态调用的参数转换为静态的HTML的URL地址,例如将在post目录下的文件,转发到根目录的wp-post.php文件中,加入的语句类似: RewriteRule ^post/([a-z0-9\-]+\.html)$ wp-post

随机推荐