首页 文章 精选 留言 我的

精选列表

搜索[网络端点],共10000篇文章
优秀的个人博客,低调大师

网络架构之路(二):性能

我们在上一篇博客中设定了架构的目标,只有一个,就是可维护性。完全没有提性能,这是故意的。 似乎程序员都是急性子,或许是被windows冗长的开机时间折磨够了,有可能是因为提升性能的效果是最显而易见的……总之,我发现,绝大部分程序员对性能的关注和热情是无与伦比的! C#刚刚推出的时候,就有人摇头晃脑的说,“嗯,自动垃圾回收,性能不行吧?” DataSet横空出世,马上有很多人写代码,在DataSet里插入几百万条数据,证明DataSet的性能问题 Linq当然更要被骂了,尼玛用反射?反射是什么,同学们知道么?性能大老虎呀!更不用说那些自动生成的sql了,有我手写的高效么? …… 所以直到今天,我仍然看到很多程序员无怨无悔的用存储过程来构建他们的系统,一个存储过程可以有几千行!然后,他们很无辜的问,“业务层有什么用?究竟能干些什么呢?” 在带团队的时候,我最怕讲的就是性能有关的问题。你要是不谈性能呢,那代码有时候真心看不下去;你要是强调性能呢,不知道他会给你整出什么幺蛾子出来。其实这就是一个“度”的掌握,所以非常难以用语言予以表示清楚。所以无数次挫败之后,我只好咬牙切齿的说,“你的代码,只有一个评判标准,可维护性。性能的问题先不管!”这个答案似乎并不能服众——尤其是对有上进心的程序员而言。 所以,我先专篇讲性能,希望能帮助大家更清楚的认识这个问题。 一、性能不是不重要,而是他没有可维护性重要。要理解这一点,首先要理解可维护性的重要(请再读上一篇我花数周找bug的段子);然后要明白:解决性能问题,我们可以有很多代码以外行之有效的方法,而可维护性基本上就只能靠代码了;最后,还是要牢记:没有牺牲,就没有胜利! 二、所以,在绝大多数情况下,当性能和可维护性相冲突的时候,性能让位于可维护性。我们采用其他办法来弥补代码性能不够高的问题。 空洞的说教没有意义。我们还是举例来说明吧! 破坏可读性 前段时间我review代码的时候发现,这个程序员用Linq之后老是用First()而不是Single(),我就奇怪了,按业务逻辑,返回的值就应该是一个,难道可能会是多个,多个应报异常,不应该取First()就完事了呀?想了一会儿,问这个程序员,他的回答让我瞬间一种无力感,“First()性能更高呀!”以下为对话实录: “你怎么知道First()性能更高呢?”我问。 “First()嘛,取了第一个合格的值就返回,就不会继续查下去了;Single()的话,就会一直查,查出所有数据,然后再取其中的一个。” “你确定?你知道有一种东西叫做索引不?” “啊?……” 然后我简单的告诉他,索引是一种树状结构,可以让查询更快等等。 “但我还是觉得应该用First()”,他想了一会儿,还是很坚定。 “为什么?”,我不明白了。 “就算有索引加快了查询速度,但用First()在加快了速度上更快呀!更快总是没错的吧?” “……”,我真不知道该怎么说了,最后突然灵光一闪,“好吧,那你说说,微软为什么要搞一个Single()方法出来呢?就为了搞出来误导你们?让用First()的产生优越感,嘲笑用Single()的?” 他陷入了沉思。 我刚入行的时候,还很是收藏了几篇文章,比如《高性能编程的十大准则》之类的,里面的内容大致就是,“总是使用StringBuilder,不要使用‘+’;总是使用……,不要使用……”。这类文章下面总是有一堆人叫好,“不错!”,“谢谢分享!”但慢慢的,我就对这些文章产生了怀疑(也应该感谢园子里的老赵,csdn里面的sp1234之类的大神);直到很后来,我才明白为什么这种说法是肤浅的;而只有通过上面的对话,我才能清晰的把我的理解说出来。 所有这些牺牲性能的简单封装,都是有其目的的;而其中一个很重要的目的,就是为了提高可读性。你为了性能,故意不使用这些现成的封装,通常,丧失的就是可读性。 想当然 继续上面这个例子。最开始的时候,这个程序员关于性能的考虑其实是想当然的。这种想当然的情形很多,大致有这几种: 自己的理解完全就是错的 自己的理解不能算错,但实际上底层已经对该问题做了优化 自己的理解没错,底层也没优化 第1、2种比较好理解,第3种为什么也说他“想当然”呢?因为没有和硬件环境相契合。 最简单的例子就是“缓存”。比如面试的时候,问你一个问题,“缓存能不能提高性能?”请注意,这是一个陷阱。答案应该是:“不一定”。几乎所有的人都认为,缓存可以迅速改善性能,是因为今天计算机的CPU和磁盘运行速度,远跟不上内存的发展。但即使如此,无节制的缓存,一样可以拖垮整个系统。 类似的例子还有很多。你沾沾自喜,我节约了一次磁盘读写的时候,你同时增加了CPU的负荷;你优化了算法,减少了CPU的运算,但其实增加了内存的压力……天下没有免费的午餐。同样的代码,随着数据的增加,硬件的改变,会呈现出截然不同的性能表现。 所以,开发过程中,很多的“优化”,其实只是你的想当然。与其这样想当然的优化,不如在拿到性能测试结果之后再有的放矢的进行优化。这时候,又回到了我们之前说的,是不是代码的可读性更重要?这样你才能迅速的找到该优化的瓶颈啊!否则,一堆乱七八糟看都看不懂的代码,你怎么去优化,你连该优化的点都找不到。 难以维护 另一个搞笑的例子是关于我自己的。创业家园项目里有一个功能:显示博客正文的同时提供一个上一页下一页的链接。惯常的做法就是直接在数据库里查就是了,但我总觉得不对,这样做两次查询有必要么?能不能优化?于是我想到了一个“绝妙”的点子:为什么不直接在博客里存储上一篇和下一篇的Id呢?这样我一次性数据往返就能取到所有数据了嘛!各位同学是不是觉得我这个主意很棒? 噩梦由此开始了。 首先,我们是想在发布博客的时候,设置他的上一篇和下一篇。但是,上一篇好设置,下一篇呢?还没有啊!怎么弄,就只好在博客发布的时候,设置他的前一篇,同时设置他前一篇的后一篇。 然后,我们新添加了一个功能,除了上一篇下一篇以外,还需要在当前博客所在分类中的上一篇和下一篇。怎么办?再加字段呗。所以,博客里就有了Previous, PreviousInCategory, Next, NextInCategory。这时候,就感觉到有点不妥,但还可以接受。 接着,出现了一个问题,上一篇下一篇博客被删除了,怎么办?这个过程,就相当于从一个双向链表里移出一个节点一样麻烦。头开始有点大了。 再接着,博客除了发布删除以外,还有各种其他状态,比如被屏蔽。而且被屏蔽之后,能否显示和当前用户又有关系。当前用户是普通用户,不能阅读;当前用户是作者自己,就能够阅读。怎么办?首先,屏蔽的时候,要设置上一篇下一篇;屏蔽取消的时候,还是要设置上一篇下一篇。然后,上一篇下一篇得根据当前用户不同变化的这个问题,基本上就傻眼了…… 最后流着泪把辛辛苦苦折腾了好久的代码全改回来,就通过数据库查呗,多么清晰简洁的逻辑啊!性能问题?首先,这样做造成了性能问题么?然后,就算有问题,用一个缓存能解决不? 合理浪费堆硬件 说了这么多,不知道有没有引起同学们的反思。可能大家还是过不去心里那道坎:明明有一种性能更高的方法我们为什么不用? 因为浪费呗! 什么?你有没有搞错?我的代码,至少省了一块内存条!那是你还没从“穷学生”的角色里转换过来。你花一周的时间对代码进行了优化(就先不考虑你的优化带来的维护成本增加了),为老板省下了一块内存条的钱。你以为老板会拍着你的肩膀表扬你么?老板打不死你! 兄弟,账不是你那样算的。当你是学生的时候,你的时间成本是0;但你进入工作岗位,每一天都是要发工资的。 通过代码来调高性能,是一种无奈——对硬件性能不够的妥协(参考:80年代游戏开发者的辛苦困境。这样写性能就高,但为什么现在没有谁再这么写代码了?)。否则,绝大多数情况下,堆硬件比优化代码的效果好得多,而且便宜得多。硬件的成本按摩尔定律往下降,我们程序员的工资也能按摩尔定律减么? 明明window 10 比window 95更耗性能,为什么今天没人用window 95?为什么VS 2013要10G的空间我们都还屁颠屁颠的赶紧装上?为什么现在大家都用C#,没人用汇编?我们站在人类文明积累的今天,就应该理所当然的享受这一切成果。有打火机你不用,你要钻木取火。如果你是因为要学贝爷荒野求生装逼,可以理解;如果你说你是因为怕浪费天然气,我……我……我怎么说你呢?“给做打火机的一条活路,行不?”同样的,程序员大神同学,你就当做好事,给下面写底层做硬件的一条活路吧!你的代码都是010001000010000001010101……了,你让其他人怎么活啊? 最后,我突然想到的一个程序员为什么对性能如此敏感疯狂,对可维护性毫不在意的一个可能原因: 性能很好理解,卡得要死和跑得飞快;可维护性很不好理解,至少得跑个两三年才能体现,那时候,谁知道爷在哪里偷着乐呢 性能上不来,程序员只有羞愧的低着头,都是我的错;需求有变更,开口就骂,“哪个SB又要改……”; 大家觉得是不是这样的?所以,愿意把代码百炼成钢绕指柔的人少。想来,是一种莫名的悲哀和凄凉。 最后最后,有一些我能想到的名言警句供大家参详: 过早的优化是万恶之源 优化首先需要找到性能“瓶颈”。否则,任何人都可以随手一指,“这段代码需要优化”。 可读性更强的代码总是更好优化 硬件永远比软件便宜 作者:自由飞 来源:51CTO

优秀的个人博客,低调大师

网络架构之路(一):目标

标准来源于目标 评价架构好坏是一个很主观的东西。既然大家写出来的程序都能跑,凭什么就说你架构好,我的架构就差?拿出来大家评评理,张三说好,李四说不行,王五说将就……究竟谁说了算?现在已经不是一个迷信权威的时代了,所以不管你多少光环加持,你都得说出子丑寅卯来,都得服众才行。 我觉得,这种现象的产生,抛开“同行相轻”和“流派之争”之类无厘头的东西,一个很重要的原因就是没有明确判断标准。所以在网上,常常就出现这样一种很热闹很奇葩很无奈的现象:我和你说性能,你跟我说安全;我跟你说安全,你跟我说扩展;我跟你说扩展,你跟我说维护;我跟你说维护,你跟我说成本…… 这是一个很简单的道理,没有标准,就无法进行评判!所以,如果不能统一一个评判架构优劣的标准,我们永远无法达成一致。你说有标准啊,性能安全可扩展……但这样还是不行,标准过多,一样等同于没有标准。假设以100分为满分,性能占多少分?安全占多少分?可扩展又占多少分?因为要想提高性能,就可能就要牺牲可扩展性;要想安全,就会牺牲性能;要想……就会……;哪一方面更重要,哪一些可以牺牲?如果不是胸有成竹的话,最后还是会左支右绌手忙脚乱,乱成一锅粥。 所以我再提出一个观点:以是否实现架构师的设计目标为标准。如果说一个系统的架构,最终实现了架构师的设计目标,我们就可以说这是一个好架构;如果说没有能实现架构师的目标,这就是一个不那么好的架构。 “等一下等一下”,你要是反应够敏锐的话,肯定会跳起来,“这是不是太主观(儿戏)了?我随便一堆烂代码,然后告诉你,‘是啊,我设计的目的就是让他烂,越难越好’,按你的逻辑,这样也行?”(⊙_⊙),嗯,你要是这样玩儿我还真没办法。但是话又说回来,要让一堆代码能跑又还够“烂”,也还不是一件容易的事,你觉得呢? 总之,我希望大家能明白我的意思:架构师开始一个新项目,应当设立一个适当的设计目标;然后通过架构,努力实现其预定目标。如果最终系统的运行,符合其设计预期,我们就可以说:这个架构不错还行!反正,架构就出了问题。 我们的目标 软件行业有各种各样的系统,每一种系统的开发都可能会有不同的目标。比如导弹发射的系统,我们可以想象,目标(甚至是基本要求)肯定是:1、稳定(绝对不能走火);2、迅速反应(不允许按下发射按钮后一分钟导弹才开始发射)。你可能觉得这种要求很好啊!任何系统不都是应该满足这样要求的吗?比如我在淘宝买T恤,结果给我发一条丁字裤,这怎么行?一个网页半天打不开还有理了?我还真得答一句,它就是有理了。“存在即合理”,这里的合理,合理在成本。我们目前日常使用到的绝大部分软件,都是有bug的,而且是一堆的bug,但我们仍然在使用它们。如果你想使用像“导弹发射”一样稳定精确迅捷的软件,可能最后的结果只有一个:你用不起。(请自行脑补) 所以,其实我们是做了一个妥协,“便宜点,将就用吧”。我们为了达到我们的基本目的,牺牲掉一些“无关紧要”的东西。对于很多追求卓越的程序员来说,这种牺牲妥协是难以接受的。“白玉微瑕,你让我怎么能够接受?”——但很多时候,你必须接受。这个问题这个观点,我们会在整个系列中不断的提及。请试着接受;如果你暂时还不能接受,请牢记:没有牺牲,就没有胜利! 那么,我们的策略是:特色突出、整体均衡。说得更直白一点:有亮点,没硬伤。这就够了!而我们的亮点就是:可维护性。(注意:不是可扩展,可维护性包含可扩展,但不仅仅是可扩展) 为什么是可维护性 幸或者不幸,我进入软件行业之后,绝大部分的工作是几乎所有程序员都不齿厌恶的维护。我曾经维护过一个有十年历史的、糅合了C、VB、java、C#各种语言在内的一个物流系统的部件。我在那家公司工作了一年多,说实话,直到我离职,对整个系统,我连边都没摸到——这个系统太大了,而且连我们公司都只是其主营公司众多外包公司中的一个。 在我花了两周的时间找到一个bug的位置之后,我以为我终于明白了为什么会说:“维护和开发的花费比是80:20”。但这只是我以为——现实更加残忍:差不多一个月后,我又花了一个星期的时间,找到了另外一个bug的根源,正是我fix前一个bug所产生的。我泪流满面,有没有?脑子里一下就蹦出个词:“按下了葫芦浮起了瓢”!总之,如果fix前一个bug就会导致后一个bug;如果fix后一个bug,就会导致前面的bug。我忘了最后是怎么处理这个问题的,依稀记得是让项目经理去和稀泥去了。因为这不是一个很关键很常用的功能,所以最后大概是不了了之吧。 后来我了解到,很多的开发项目,是这样一个流程:一群人根据文档开始开发,几个月后通过验收上线;然后开发团队解散,留下一两个项目组里最菜的菜鸟做“维护”。Game Over!皆大欢喜。这种现象,在各种外包团队(尤其是以项目计价的廉价外包团队)中更加的突出(这或许也是大家普遍歧视外包公司的一个原因?) 既然是这样一种开发模式,很多开发人员根本体会不到维护的痛苦。在他们看来,“维护嘛,修修补补,加一两个if...else而已,让我们开发人员做更高大上的工作吧!”但他们也不是总这么幸运,有时候,他们会被抓去“填坑”。据说最通常的做法,就是在“老坑”周围再挖一堆“新坑”,填平之前的老坑即可。周而复始,直到有一天,“受不了啦!我们重写吧!”——等等,为什么不重构?呵呵,好问题,你觉得呢? 需求变更 很多程序员把这种困境归咎于“需求变更”。如果不是那些傻逼客户一天到晚的改需求,我一定会做出一个完美的作品! 或许是因为我是半路出家的原因,和很多程序员相反,我觉得:不是需求变更驱动着软件的不断更改,而是“软件可以随意更改”的这种特性刺激了不断的需求变更。你装修好的房子,是不是住一段时间之后就会觉得这里那里不合适?这里少了一个插座,阳台上该加一个龙头,橱柜用着不顺手……“要是能改改就更好了!”,只是这样的改动太费力,所以大多数时间我们都还是算了。但软件可以!理论上怎么改都可以。想想软件真的是一种很特殊的商品——它是可以交付“半成品”的。你先用着,如果有问题我再改改,有新需求我再改改,一直可以改到面目全非。没有在其他传统行业里待过的程序员无法理解,“可以随意更改”是一种多么出色的特质。这意味着产品可以自我进化,应对各种变化,可以永生!想象这样一台“汽车”,开始可以在马路上跑,过段时间改一下就可以在水里游,再拆装一下可以当摩托拉风,堵车的时候展开翅膀……这是什么样一种屌爆天的体验啊? 所以,“拥抱变化”绝不是一句口号,这是一种胸怀。 作为示例的这两个系统,我是希望能用他们一辈子的。但我甚至无法想象一年之后他们会是什么样子——他们需要接受市场的检验,应对技术的升级换代,会有各种想象不到的变化。所以,可维护性无疑是必须放到首位的。 为了可维护 明确了架构的首要目标,我们就可以做一些基础的选择了。比如开发语言,可是是面向对象的C#,不需要“性能卓越”的C。 说道“面向对象”,可能有些同学就会比较high,脑子里就会冒出“抽象”、“封装”、“设计模式”等各种高大上的东西出来。但我不得不提醒你们:首先,这些都是微观层面考虑的东西,而架构是宏观的;然后,这些都不是架构,而是润滑黏合支持架构的东西;最后,在其他条件不变的情况下,系统中这些东西用得越少,说明架构越好。 我们以“设计模式”为例。大家在学习设计模式的过程中有没有这样一种困惑,“这样继承封装多态乱七八糟的绕来绕去的干嘛?”我花了很长一段时间才明白,要理解设计模式,必须要明白三个字:“不得已”。是迫不得已,才用设计模式来解决一些特定的问题,而不是说正常的代码就应该这样写!这种迫不得已,有很多种原因。个人觉得最容易理解的就是“适配器模式”,因为出现了接口的冲突,所以我们不得不进行适配。但一个很自然的问题就是:为什么不直接改接口让他们自然融洽呢?这不是一种更自然更直观的解决方案吗?答案很有可能就是因为架构——大的架构已经确立,局部必须服从整体。那么,如果一个完全理想化的架构,是不是根本就不应该出现这种问题接口冲突的问题,因而根本就不需要这种设计模式? 所以,我说设计模式之类的东西是润滑剂是黏合剂,他们的作用是弥补架构的局部缺陷,更好的支撑架构。更极端的一种说法可以送给痴迷于设计模式的同学:设计模式是药,没病就不要吃药! 那么,为了可维护性,架构中究竟应该注意些什么?这是一个很大的话题,开篇我们只说一点。 模块划分 模块有大有小,大可以是一个分层一个项目,小可以是一个方法一个类。我们通常的做法是由大到小,逐步细分。 模块的划分是相当的考验架构能力的。良好的模块划分,能够让我们方便的安排人手、合理的组织项目进度、迅速的定位代码……各种好处说都说不完。所以还是说说不好的模块划分有什么问题更容易一些,嗯,这个好像根本就不要说,想想你在一堆乱七八糟的代码里不断的F11的情形吧! 我个人认为,模块划分的难度在于“整齐”和“灵活”之间取舍。通常来说,大的模块我们都是“一刀切”,着重强调的是“整齐”,比如口熟能详的UI层、BLL层和DAL层,但这种“一刀切”的做法,更多的是一种无奈。我们的人类的思维局限决定了我们在考虑复杂问题时无法深入到每一个细节,所以只能先“大而化之”的把一个复杂问题先进行简单化。这样带来的一个严重的副作用就是,限制了代码的灵活性;而灵活性,正是应对复杂变化的有效武器。所以,在更小一些的模块(比如说:类)里,我们引入了丰富多彩的抽象继承设计模式等一系列充满各种灵活性的机制,以弥合“一刀切”造成的问题。这一松一紧一张一弛中“度”的掌握,就只能说是一种艺术了。 模块划分,笼统的说教用处不大,我们将在后面的文章中结合具体情况逐一说明。但我希望大家能够明白:模块划分是必须的——这种必须,是一种无可奈何的选择。所以,喜欢从页面直接写sql到数据库的同学,老大让你把你的代码拆成几段放到不同地方的时候,不要嫌麻烦;喜欢把一个简单项目切成七层的同学,先仔细想不想这样做是不是真的有必要。 代码之外 为了代码能够长期有效的维护,我们还需要做很多工作,比如良好的文档、完善的项目管理流程。但我想说的,还是不是这个,而是代码之外的因素对项目架构的影响。比如开发团队的背景能力偏好,一群C#程序员,你一定要整个node.js,这纯粹是给自己找不痛快。除了这些稍稍用脑袋想一想就能明白的东西,有一件事,很多程序员并没有意识到。 架构的一个天然目的就是:让代码更智能让程序员更傻瓜。换一张说法就是,架构要“创造便利,让程序员更关注业务”。 这可能是一个让程序员感到悲哀的事实。正如机械师不停的发明,让机器变得越来越聪明,取代流水线上的工人,最终取代了他们自己。从某种意义上说,我们都是自掘坟墓的人。一个良好的架构,就应该是让每一个普通开发人员,都是一个个尽量廉价随时可以替换的螺丝钉,这样才能保证系统永远健康正常的运行下去。告诉你这个事实可能让你一整天都不开心,但接受这个事实之后能帮助你在工作中变得更加的“心平气和”。螺丝钉就要有螺丝钉的觉悟;更何况,当好一颗螺丝钉也不是一件很容易的事。 作者:自由飞 来源:51CTO

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册