• home > theory > engineering > SOA >

    系统架构设计—性能和代码可维护性的取舍之道

    Author:zhoulujun Date:

    不是需求变更驱动着软件的不断更改,而是“软件可以随意更改”的这种特性刺激了不断的需求变更。但是,说这句话的前提就是,有足够的时间给开发人员。不能是瞎扯!

    软件行业有各种各样的系统,每一种系统的开发都可能会有不同的目标。比如导弹发射的系统,我们可以想象,目标(甚至是基本要求)肯定是:1、稳定(绝对不能走火);2、迅速反应(不允许按下发射按钮后一分钟导弹才开始发射)。你可能觉得这种要求很好啊!任何系统不都是应该满足这样要求的吗?比如我在淘宝买T恤,结果给我发一条丁字裤,这怎么行?一个网页半天打不开还有理了?我还真得答一句,它就是有理了。“存在即合理”,这里的合理,合理在成本。我们目前日常使用到的绝大部分软件,都是有bug的,而且是一堆的bug,但我们仍然在使用它们。如果你想使用像“导弹发射”一样稳定精确迅捷的软件,可能最后的结果只有一个:你用不起。(请自行脑补)

    一、性能不是不重要,而是他没有可维护性重要。

    要理解这一点,首先要理解可维护性的重要

    很多的开发项目,是这样一个流程:一群人根据文档开始开发,几个月后通过验收上线;然后开发团队解散,留下一两个项目组里最菜的菜鸟做“维护”。Game Over!皆大欢喜。这种现象,在各种外包团队(尤其是以项目计价的廉价外包团队)中更加的突出(这或许也是大家普遍歧视外包公司的一个原因?)

    既然是这样一种开发模式,很多开发人员根本体会不到维护的痛苦。在他们看来,“维护嘛,修修补补,加一两个if...else而已,让我们开发人员做更高大上的工作吧!”但他们也不是总这么幸运,有时候,他们会被抓去“填坑”。据说最通常的做法,就是在“老坑”周围再挖一堆“新坑”,填平之前的老坑即可。周而复始,直到有一天,“受不了啦!我们重写吧!”——等等,为什么不重构?呵呵,好问题,你觉得呢?

    变态的需求变更——脑残的产品经理

    很多程序员把这种困境归咎于“需求变更”。如果不是那些傻逼客户一天到晚的改需求,我一定会做出一个完美的作品!

    不是需求变更驱动着软件的不断更改,而是“软件可以随意更改”的这种特性刺激了不断的需求变更。

    你装修好的房子,是不是住一段时间之后就会觉得这里那里不合适?这里少了一个插座,阳台上该加一个龙头,橱柜用着不顺手……“要是能改改就更好了!”,只是这样的改动太费力,所以大多数时间我们都还是算了。但软件可以!理论上怎么改都可以。想想软件真的是一种很特殊的商品——它是可以交付“半成品”的。你先用着,如果有问题我再改改,有新需求我再改改,一直可以改到面目全非。没有在其他传统行业里待过的程序员无法理解,“可以随意更改”是一种多么出色的特质。这意味着产品可以自我进化,应对各种变化,可以永生!想象这样一台“汽车”,开始可以在马路上跑,过段时间改一下就可以在水里游,再拆装一下可以当摩托拉风,堵车的时候展开翅膀……这是什么样一种屌爆天的体验啊?

    所以,“拥抱变化”绝不是一句口号,这是一种胸怀。

    作为示例的这两个系统,我是希望能用他们一辈子的。但我甚至无法想象一年之后他们会是什么样子——他们需要接受市场的检验,应对技术的升级换代,会有各种想象不到的变化。所以,可维护性无疑是必须放到首位的

    为了可维护

    明确了架构的首要目标,我们就可以做一些基础的选择了。比如开发语言,可是是面向对象的C#,不需要“性能卓越”的C。

    说道“面向对象”,可能有些同学就会比较high,脑子里就会冒出“抽象”、“封装”、“设计模式”等各种高大上的东西出来。但我不得不提醒你们:首先,这些都是微观层面考虑的东西,而架构是宏观的;然后,这些都不是架构,而是润滑黏合支持架构的东西;最后,在其他条件不变的情况下,系统中这些东西用得越少,说明架构越好。

    我们以“设计模式”为例。大家在学习设计模式的过程中有没有这样一种困惑,“这样继承封装多态乱七八糟的绕来绕去的干嘛?”我花了很长一段时间才明白,要理解设计模式,必须要明白三个字:“不得已”。是迫不得已,才用设计模式来解决一些特定的问题,而不是说正常的代码就应该这样写!这种迫不得已,有很多种原因。个人觉得最容易理解的就是“适配器模式”,因为出现了接口的冲突,所以我们不得不进行适配。但一个很自然的问题就是:为什么不直接改接口让他们自然融洽呢?这不是一种更自然更直观的解决方案吗?答案很有可能就是因为架构——大的架构已经确立,局部必须服从整体。那么,如果一个完全理想化的架构,是不是根本就不应该出现这种问题接口冲突的问题,因而根本就不需要这种设计模式?

    所以,我说设计模式之类的东西是润滑剂是黏合剂,他们的作用是弥补架构的局部缺陷,更好的支撑架构。更极端的一种说法可以送给痴迷于设计模式的同学:设计模式是药,没病就不要吃药!

    那么,为了可维护性,架构中究竟应该注意些什么?这是一个很大的话题,开篇我们只说一点。

    模块划分

    模块有大有小,大可以是一个分层一个项目,小可以是一个方法一个类。我们通常的做法是由大到小,逐步细分。

    模块的划分是相当的考验架构能力的。良好的模块划分,能够让我们方便的安排人手、合理的组织项目进度、迅速的定位代码……各种好处说都说不完。所以还是说说不好的模块划分有什么问题更容易一些,嗯,这个好像根本就不要说,想想你在一堆乱七八糟的代码里不断的F11的情形吧!

    我个人认为,模块划分的难度在于“整齐”和“灵活”之间取舍。通常来说,大的模块我们都是“一刀切”,着重强调的是“整齐”,比如口熟能详的UI层、BLL层和DAL层,但这种“一刀切”的做法,更多的是一种无奈。我们的人类的思维局限决定了我们在考虑复杂问题时无法深入到每一个细节,所以只能先“大而化之”的把一个复杂问题先进行简单化。这样带来的一个严重的副作用就是,限制了代码的灵活性;而灵活性,正是应对复杂变化的有效武器。所以,在更小一些的模块(比如说:类)里,我们引入了丰富多彩的抽象继承设计模式等一系列充满各种灵活性的机制,以弥合“一刀切”造成的问题。这一松一紧一张一弛中“度”的掌握,就只能说是一种艺术了。

    模块划分,笼统的说教用处不大,我们将在后面的文章中结合具体情况逐一说明。但我希望大家能够明白:模块划分是必须的——这种必须,是一种无可奈何的选择。所以,喜欢从页面直接写sql到数据库的同学,老大让你把你的代码拆成几段放到不同地方的时候,不要嫌麻烦;喜欢把一个简单项目切成七层的同学,先仔细想不想这样做是不是真的有必要。

    代码之外

    为了代码能够长期有效的维护,我们还需要做很多工作,比如良好的文档、完善的项目管理流程。但我想说的,还是不是这个,而是代码之外的因素对项目架构的影响。比如开发团队的背景能力偏好,一群C#程序员,你一定要整个node.js,这纯粹是给自己找不痛快。除了这些稍稍用脑袋想一想就能明白的东西,有一件事,很多程序员并没有意识到。

    架构的一个天然目的就是:让代码更智能让程序员更傻瓜。换一张说法就是,架构要“创造便利,让程序员更关注业务”。

    这可能是一个让程序员感到悲哀的事实。正如机械师不停的发明,让机器变得越来越聪明,取代流水线上的工人,最终取代了他们自己。从某种意义上说,我们都是自掘坟墓的人。一个良好的架构,就应该是让每一个普通开发人员,都是一个个尽量廉价随时可以替换的螺丝钉,这样才能保证系统永远健康正常的运行下去。告诉你这个事实可能让你一整天都不开心,但接受这个事实之后能帮助你在工作中变得更加的“心平气和”。螺丝钉就要有螺丝钉的觉悟;更何况,当好一颗螺丝钉也不是一件很容易的事。

    解决性能问题,我们可以有很多代码以外行之有效的方法,而可维护性基本上就只能靠代码了;最后,还是要牢记:没有牺牲,就没有胜利!

    二、在绝大多数情况下,当性能和可维护性相冲突的时候,性能让位于可维护性。

    我们采用其他办法来弥补代码性能不够高的问题。

    优化首先需要找到性能“瓶颈”。否则,任何人都可以随手一指,“这段代码需要优化”。

    可读性更强的代码总是更好优化

    硬件永远比软件便宜(即硬件比技术人员便宜,看系统规模而定)

    合理浪费堆硬件

    说了这么多,不知道有没有引起同学们的反思。可能大家还是过不去心里那道坎:明明有一种性能更高的方法我们为什么不用?

    因为浪费呗!

    什么?你有没有搞错?我的代码,至少省了一块内存条!那是你还没从“穷学生”的角色里转换过来。你花一周的时间对代码进行了优化(就先不考虑你的优化带来的维护成本增加了),为老板省下了一块内存条的钱。你以为老板会拍着你的肩膀表扬你么?老板打不死你!

    兄弟,账不是你那样算的。当你是学生的时候,你的时间成本是0;但你进入工作岗位,每一天都是要发工资的。

    通过代码来调高性能,是一种无奈——对硬件性能不够的妥协(参考:80年代游戏开发者的辛苦困境。这样写性能就高,但为什么现在没有谁再这么写代码了?)。否则,绝大多数情况下,堆硬件比优化代码的效果好得多,而且便宜得多。硬件的成本按摩尔定律往下降,我们程序员的工资也能按摩尔定律减么?(注:硬件越来越便宜,而人员工资越来越增加,就拿工厂的普通工人用工成本也在增加)

    明明window 10 比window 95更耗性能,为什么今天没人用window 95?为什么VS 2013要10G的空间我们都还屁颠屁颠的赶紧装上?为什么现在大家都用C#,没人用汇编?(注:汇编比高级语言性能强,接近机器)

    我们站在人类文明积累的今天,就应该理所当然的享受这一切成 果。有打火机你不用,你要钻木取火。如果你是因为要学贝爷荒野求生装逼,可以理解;如果你说你是因为怕浪费天然气,我……我……我怎么说你呢?“给做打火 机的一条活路,行不?”同样的,程序员大神同学,你就当做好事,给下面写底层做硬件的一条活路吧!你的代码都是 010001000010000001010101……了,你让其他人怎么活啊?

    最后,我突然想到的一个程序员为什么对性能如此敏感疯狂,对可维护性毫不在意的一个可能原因:

    性能很好理解,卡得要死和跑得飞快;可维护性很不好理解,至少得跑个两三年才能体现,那时候,谁知道爷在哪里偷着乐呢

    性能上不来,程序员只有羞愧的低着头,都是我的错;需求有变更,开口就骂,“哪个SB又要改……”;

    大家觉得是不是这样的?所以,愿意把代码百炼成钢绕指柔的人少。想来,是一种莫名的悲哀和凄凉。

     某网友说:

     感觉性能问题属于眼前问题,可维护性问题属于以后的问题。很多人就是感觉先把眼前自己的事儿处理完就行,程序只要能运行,老板满意就万事大吉,至于以后维护爱谁谁吧,那个时候老子已经另谋高就了,烂摊子留给后面的倒霉蛋吧。

    这段话的确说出了目前的现状,被逼的,上面只看功能完成与否,功能完成的质量不管。那还关系代码的可维护性干嘛。于是把代码复制,拷贝函数,只要能跑起来就好了。

    性能、可维护性从来都是要折中,过分从代码中追求性能会增大开发成本、降低可维护性。

    现实中老板、客户真不在意那点硬件成本。只要项目快点上线。二期三期四期优化都好说。

    张口闭口 谈性能的 经理 我之前在北京 也见过 几个。

    最后 用几个 实际的项目,堵住了他的嘴

    —— 别跟我谈性能,即使我用滥反射,性能也比你的快。

     同意,除了核心功能和高并发的页面,

    一直以来写代码的风格,首先就是可读性,可维护, 然后再是性能.


     就比如php,很多功能函数废弃掉了

    今天拿之前的代码,完全跑不通。各种报错。比如

    $mysql_conn = mysql_connect ( "$dbserver", "$dbusername", "$dbpassword" ) or die ( "Mysql connect is error." );

    在php5.5.12版本运行会提示

    Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in D:\soft\develop\php\wamp\2.5\wamp\www\generate_mysql.php on line 16

    今天是:

    Uncaught Error: Call to undefined function mysql_connect() in 

    看来会废弃了,不建议使用了,程序无法运行的。使用mysqli or PDO 来替代。到高版本,根本无法使用这个函数了。

    我想知道哪个php版本开始就会开始不建议使用这个函数了,所以去官网www.php.net搜索这个函数。有这样的介绍:

    本扩展自 PHP 5.5.0 起已废弃,并在将来会被移除。应使用 MySQLi 或 PDO_MySQL 扩展来替换之。参见 MySQL:选择 API 指南以及相关 FAQ 以获取更多信息。用以替代本函数的有:

    给我们做接口服务的启发

    到高版本,没有兼容旧太旧版本的函数,为什么这样子?从php官方组织维护源码角度来说,这个函数肯定是没啥优势了,去进行优化这个函数,还不如对它进行废弃掉

    我们在网站,需要提供给接口给公司内部其他子系统调用。也会存在接口升级,原来的接口设计有缺陷,不想去修复了。干脆建议使用新版本的接口了。

    思考,我何不也弄一个类似的提升呢。提示调用方升级到新接口去使用。

    突然发现,从沟通成本角度考虑,把提示信息放在接口返回给调用方,会推进改进的速度。

    为什么官方要把这个函数禁用掉?而不是去修复,优化呢?

    性能考虑,安全考虑。去官网找答案

    Recommended API

    It is recommended to use either the mysqli or PDO_MySQL extensions. It is not recommended to use the old mysql extension for new development, as it has been deprecated as of PHP 5.5.0 and will be removed in the future. A detailed feature comparison matrix is provided below. The overall performance of all three extensions is considered to be about the same(三个扩展的性能不相上下). Although the performance of the extension contributes only a fraction of the total run time of a PHP web request(性能考虑在php的web请求中是一小部分考虑,即不仅仅是性能考虑). Often, the impact is as low as 0.1%(影响是很小的,0.1%)

    php接口支持

    看官网介绍,不是性能考虑才弃用那个扩展。反正就是弃用,不想去维护那个了。我也没搞清楚。不过深有同感,旧的扩展代码量也不少,去优化,接口结构始终无法达到质的提升,还不如新开一个?

     我的思考:

    性能问题:关键是找到性能的瓶颈点。而不是纠结于细微的。也就是主要矛盾。把主要矛盾解决掉后,就好多了

    就拿php来说,是解释性语言,解释性语言是比不上编译型语言快。但是在web应用中,不涉及到复杂的cpu计算(简单的增删查改数据库,不是分词这么复杂的算法,这种属于cpu密集型),瓶颈在磁盘读写(磁盘i/0)和数据库上。

    所以看不出php的劣势出来。当达到facebook这样的应用,他们就会感到一点点php优化,对整个成本的减低,省去很多服务器。

    所以有规模,就是规模成本。经济学中有个规模效益。通俗例子理解,生产100个要这么多成本,生产1000也是差不多的成本,但是可以得到更多利润。所以只能把规模扩大,才能赚到钱。

    你达不到那个规模的时候,去谈优化性能,带来的是得不偿失的。

    而且,就算是解决性能问题,关键是找到瓶颈在哪里,解决了瓶颈,就会带来质的变化。而不是纠结于细节优化。主次矛盾要分清楚。

    单纯说性能,那直接用汇编写代码,发明高级语言干嘛,汇编语言更加接近机器二进制,所以性能更强。但是汇编语言不容易维护,因为难懂,难上手。不接近人类的思维习惯。

    记得国外有本书中提到一个关掉:代码是写给人(对象是程序员)看的,不是写给机器看的,如果是写给机器看的,那么干嘛不直接使用二进制01011这样的方式去写代码呢,机器识别就是二进制。而且,性能更强,不用经过中间转换,是不是。

    参考文章:

    http://www.cnblogs.com/wangtao_20/p/4809799.html

    http://www.cnblogs.com/wangtao_20/p/4823088.html

    http://www.cnblogs.com/freeflying/p/4788494.html

    http://www.cnblogs.com/freeflying/p/4752415.html


    转载本站文章《系统架构设计—性能和代码可维护性的取舍之道》,
    请注明出处:https://www.zhoulujun.cn/html/theory/engineering/SOA/2017_0418_7990.html