add by zhj:虽然是几年前的文章,但仍是颇有参考价值的html
原文:http://blog.zhaojie.me/2010/05/programmer-magazine-2010-5-architect.html程序员
上个月《程序员》杂志向我约稿,但愿我能够参加5月份的“架构师接龙”栏目,我略为犹豫了一下便答应了。“架构师接龙”是一个问答形式的栏目,每期由一我的提问,并由另外一我的回答。回答的一方即是下期的提问者。此次提问的架构师是新浪微博的技术经理杨卫华。他提出的问题包括语言选择与架构设计、NoSQL存储方案的取舍、微博类系统的架构等多个方面。杨卫华是国内技术社区一等一的高手,这使得我在回答问题时更有当心翼翼地班门弄斧之感。若是您对某些问题感兴趣,也不妨来一块儿讨论一下。数据库
提问:不少架构师表示编程语言不重要,架构设计思想才重要,可是大部分团队都是很是依赖某种语言的,甚至不少项目负责人也存在对某种语言存在偏好而对另一种语言反感的现象。你怎么看待编程语言选型问题?同时业界也存在另一种现象,不少前沿技术研究者对一些新兴语言如Erlang, Go等表示出狂热,你对团队或项目中是否引入这些新的语言持什么观点?编程
回答:在我看来,编程语言的选择也是相当重要的。诚然,架构的设计思想可谓直接决定了系统自己的质量。与此相反,从理论上说,只要是图灵完备的语言,就不存在“能力”上的根本差别,任何工做都是能够实现的。可是,有一点也没法被忽视,那即是咱们使用的语言,每每也会影响,甚至决定了咱们的思惟方式。后端
举个有些极端的例子,若是人们还在使用汇编语言进行开发,那么估计程序员的思惟永远没法跳出“子过程”这个抽象级别,什么面向对象设计,函数式编程几乎无从谈起。人们在生产和学习过程种会引起一些需求,于是须要产生一些工具来辅助学习和生产,而“语言”即是此类工具之一。只有利用高级语言,人们才能有效地把真实世界抽象成计算机这些机器盒子能认识的东西。数组
现在,可用于构建项目可选的主流语言每每都会有多种,有时候的确会发现,好像不一样的语言──例如Ruby和Python──从各方面来讲并无太大区别。这其实也是比较正常的,由于某些(甚至是大部分的)语言特性,并无对咱们的“思惟”产生影响。缓存
举例来讲,某些喜欢Ruby的朋友认为Ruby语言的编程体验很是良好,比如它的数组操做能够直接作加法或减法:服务器
array = ['aaa', 'bbb', 'ccc'] - ['ccc', 'ddd']
或者说,在Python里交换两个变量的值也只须要一行代码(大部分语言中可能须要借助中间变量):架构
a, b = b, a
可是,就我我的观点来讲,这些语言特性,虽然它们的确可让编程工做变的相对轻松一些,例如可让咱们少些一点代码,但终究没有改变,或是表现出另外一种编程思惟。这样的语法特性,通常来讲均可以经过构建一些简单的辅助函数来作到相似程度的“简化开发”(如上面Ruby的例子),对于那些非“即写即抛”的程序来讲,这些特性的优点并不明显。并发
而与此相比,Ruby的Mixin机制和Python的Decorator就不只仅是“语法糖”,而是比较重要的语言特性了,由于它们能够带来或大大简化某些十分有用的编程模式。
不过对于语言的选择有时候还须要往更高处看。现今一些新出现,或者新流行起来语言,对于系统开发方面的影响则更为深远。举个例子,目前提及并发/并行程序设计,没法忽略的即是Erlang语言。这门语言提供了一种构建轻量级计算单元(在Erlang中被称为“进程”),使用发送消息(Message Passing)的方式进行相互间的通讯。这种作法避免了共享状态(Shared State)方式下容易出现的各类问题,而且在其独特的虚拟机实现下能够获得很强大的并发能力。可是,Erlang的任务调度机制有个特色,那即是它会为每一个“进程”分配相同的计算能力。这样,若是系统中有1000个进程,那么每一个进程获得的计算能力便会是100个进程时的十分之一。这种调度方式对于某些类型的应用来讲可能并不合适,由于它可能在并发压力增大的状况下,形成吞吐量的下降甚至彻底中止服务(由于每一个任务都超时了)。Erlang的这个特性每每会直接影响到系统的架构方式。
不过在某些场景下,咱们也能够选用其余的语言。例如Scala,它一样提供了基于Actor模型的消息传递并发机制。可是它的调度方式与Erlang不一样(事实上因为平台功能限制,它也没法实现Erlang的调度方式)。因为Scala的Actor模型构建与JVM之上,所以它只能准备一个线程池,让其中的线程不断地处理消息的传递及处理任务,而额外的任务则会在队列中等待。所以,Scala使用的并非Erlang那样彻底公平的调度方式,可是这样反而能够优先处理先出现的任务,保证稳定的吞吐量。
所以,Erlang和Scala这两种不一样的调度机制,决定了它们适合不一样的应用场景,或是系统架构的不一样方式。我相信Facebook选用Erlang构建聊天平台,Twitter选用Scala构建消息中间件都是有这方面考虑的。
固然,调度方式更像是由平台决定,而不是语言决定的。不过在刚才的特定问题上我认为二者实际上是统一的。由于Erlang既是门语言,也表明了一个平台。而Scala虽然是JVM平台上众多语言之一,但也只有它可以优雅的实现Actor模型的消息传送机制。我始终认为,一个语言特性只有真正“好用”,它才能被人们广为接受。例如,使用Java语言能实现Actor模型吗?能,可是它缺乏Scala那样灵活的函数式语法,以及模式匹配等特性,所以没法构建出一个好用、易用的Actor框架,天然也就无人问津了。这其实也是“语言影响思惟方式”的典型案例之一。
异步及并行是现在系统构建不可或缺的因素。现在的新语言大都在这方面下了很大功夫。除了Scala和Erlang之外,在微软.NET平台上的新语言F#引入的创新特性“计算表达式(Computation Expression)”,使用相似于Monad的机制大幅度简化了异步程序的开发难度。而JVM中的Clojure语言也引入了软件事务内存(STM,Software Transactional Memory)。咱们几乎能够这么说,现在每种新出现的语言都有独特的“杀手级”特性,它们都是影响系统开发的重要因素,使用语言自己的支持能够显著下降系统的开发难度,增长可维护性和健壮性,这些并不是是由架构改进能够轻松得到的效果。
现在“多语言”开发逐渐成为一种趋势,例如在Facebook的各个子系统分别使用了C、C++,Erlang,Java等多种语言/平台,而后使用PHP做为黏合剂链接起来。而Twitter也绝不例外地使用了Ruby,C,Scala和Java。如今的系统日趋复杂,几乎没有任意一种工具能够彻底适合系统的所有开发,为系统不一样的组成部分选择合适的语言,也是现在架构师须要面临的挑战。
与过去不一样,如今即便肯定构建系统所用的平台以后──如JVM,也会发现语言的选择余地会有不少,不一样的语言的确也有不一样的特性,能够带来一些特别的优点。例如,利用Ruby的动态特性,即可以方便地进行单元测试。而系统的生产部分代码,可能即可以选用Scala等静态编译型语言,以便借助更完整的静态检查工具来确保更加稳固的产品质量。
对于新语言的选取,不一样风格的架构师会采起不一样的策略。例如保守的架构师可能会根据语言所在社区是否活跃,语言相关资源是否丰富,相应的程序员是否容易招聘来考虑语言或平台的选择。这种作法是十分正常的。可是万事都讲究个平衡,在某些状况下“保守”和“抱残守缺”或“固步自封”之间仅一步之遥。
我我的的风格相对比较“激进”,十分乐于吸取和尝试一些新事物。个人建议是,每一个技术团队都应该挑选出几个技术水平较高,经验较为丰富的成员,普遍的吸取新事物的发展,并在合适的时候向团队及生产环境作出提案,以改进系统的开发效率或者质量。由这些高级技术人员进行引导,每每能够较好的预估新技术对于产品的影响,即使出现了一些问题也能够设法自行解决。
据我了解,一些较为活跃的技术团队,尤为是一些Web 2.0产品的技术团队,在这方面都有比较好的实践。
提问:最近不少公司有向NoSQL方向发展趋势,不少架构师也关心是否须要将关系数据库转向NoSQL,请问能给正在选型的架构师哪些建议?
回答:个人我的见解是,NoSQL自己是好东西,可是在使用的氛围方面稍有一些扭曲。多是受到关系型存储方式的“压抑”过久,现在冒出一个NoSQL运动让人眼前一亮,不禁得热血沸腾起来。
NoSQL的出现,本来不是为了彻底取代关系型数据库,而是为了应对关系型数据库在性能和伸缩性方面的缺陷而提出的存储方式。NoSQL不该该是“No SQL”,更为稳当的方式应该是“Not Only SQL”。
放眼现在比较成功的NoSQL应用,彷佛除了Google因为数据规模,资源沉淀等缘由以外,其余系统大都是将NoSQL做为一种优化的手段在使用,而并不是是做为系统的主要存储方式,它们主要使用的存储方式依然是MySQL等关系型数据库。而事实上,各系统也是在架构演变过程当中,发现关系型数据库成为了系统优化的瓶颈,进而在必定程度上引入NoSQL存储方式以改善性能。
例如就在不久以前,SourceForge宣布将在系统中引入MongoDB,而Twitter也打算开始使用由Facebook建立的Cassandra。可是以SourceForge与Twitter目前基于关系型数据库所支撑起来的规模,也已是无数系统难以企及的目标了。更况且,如Stack Overflow这样号称全世界最大的程序员网站,做为存储后端也只是使用了单台关系型数据库。
毕竟,关系型数据库的性能并不是差到不可接受,NoSQL的优点也只有在达到必定规模时才能体现出来。并且,除了存储方式之外,系统中能够优化的地方还有不少。例如最传统的,缓存,一个实现较好的缓存机制能够减小95%以上的数据库访问,这对于系统性能的影响也是至关巨大的。
在目前,使用NoSQL存储方式的另外一个不便之处即是工具的缺失。我在项目中也使用了MongoDB,一个十分明显的体会即是,对于MongoDB的操做比关系型数据库要麻烦很多。例如在访问关系型数据库时能够利用现成的映射工具,通过多年发展此类工具也已经变得很是灵活、高效,可以应对绝大部分的使用场景。而在使用MongoDB时,我彷佛又有了回到当年裸写JDBC的感受了,甚至对于某些平台来讲,连一个成熟的驱动(例若有链接池的支持)都须要亲自动手开发一个。对于一个有经验的开发人员来讲,便写一些“够用”的代码天然不是难事,不过这的确也是一件会影响投入产出比的事情。
此外,NoSQL虽然性能高,但这也是经过在必定程度上牺牲数据的完整性或一致性的保证换来的,传统的关系型数据库却在这方面投入了很大的精力,例如事务机制,虽然会下降性能,可是却保证了数据的一致性。可是,现在的NoSQL存储几乎都没有提供相似的机制(毕竟有个没法回避的CAP规律),这样多个相关的操做一旦中断(例如出现了异常),则很容易形成数据“此长彼短”的现象。并且,现在的NoSQL产品出于性能考虑,几乎无一例外会带有必定程度的缓存机制,不会将新建或更新的数据直接写入磁盘。所以,若是没有一个集群环境,在遇到突发情况时则极可能带来数据丢失的状况。对此,如MongoDB明确指出,它对于单机的持久性并不十分重视,它的设计人员以此换来更为重要的参数:性能。这意味着,一旦使用NoSQL做为主要存储方式,则每每会须要同步跟进一些周边措施,例如可能要在保证数据的最终一致性方面投入较多的精力。
当咱们肯定要选择NoSQL存储方式的时候,则必须根据本身的业务特征选取特别的NoSQL产品。目前NoSQL主要分为四大类:BigTable,Key-Value,文档型,及图数据库。它们有各自的性能优点及适用范围。例如Key-Value存储方式支持的查询方式很是有限,可是因为结构简单,它的性能和伸缩性可谓傲视群雄。而文档型数据库,如MongoDB,它所支持的查询方式很是灵活,并内置Map Reduce机制,能够直接输入JavaScript脚本进行一些特殊的数据处理及汇总。而如Neo4j这样的图数据库,因为直接支持“节点”、“(有向)关系”等概念,则对于一些关系型数据库、文档型数据库难以应对或建模的查询或遍历方式(如最短路径计算),就有了很是直接、天然且高效的支持。
总之,架构师选择的不是SQL或NoSQL自己,而应该只是“最合适”的东西。
提问:不少架构师都喜欢学习Google, Facebook等大型系统的经验,但另外很多架构师则认为绝大多数网站都不会成长成一个“大型网站”。绝大多数工程师都没有能力创建和维护一个相似GFS的系统。对绝大多数网站而言,把时间耗在所谓“大型网站”的架构上没有意义,你怎样看待这种说法,架构师应该如何选择关注的方向和领域?
回答:我在这方面的见解是,虽然Google,Facebook等大型系统的规模对于绝大多数人来讲多是永远没法接触到的,可是它们的经验及措施可能会给咱们带来一些其余方面的体会。
例如,Map Reduce本来是函数式编程中再普通不过的概念和手段,可是Google将它和GFS等其余基础设施一结合,便成为了一个无比强大的分布式计算技术。可是,Map Reduce它自己仍是十分简单的东西(Google Map Reduce实现的复杂点主要仍是在于GFS),它也并不是Google专有的东西,咱们受到这样的启发也能够将其用到别处。例如在MongoDB和CouchDB中都内置了MapReduce支持,在去年的QCon Beijing大会中,FreeWheel公司在它们的广告平台中,也使用了本身的方式实现了Map Reduce计算机制。
所以,即使是没法成为真正的巨人,也能够关注巨人成长过程当中所吸收的经验教训,从中也能够获得一些启发。即便是看成一个有趣的故事去了解也好,即便是为了打开眼界也好。有时,咱们须要的可能只是一个不经意的提示。
提问:目前国内不少互联网门户都在作微博产品,你以为微博技术架构的主要难点在哪些方面?
回答:从复杂度上面来讲,微博产品的业务是相对比较简单的,我认为它在技术架构上有两大要素:消息传递与缓存。
微博产品从产品性质上来讲几乎彻底就是一个消息分发平台,所以一个良好的消息传递机制是相当重要的。当一个用户发出消息以后,它能够被许多人观察到。对于一个名人来讲,被数十万用户所追随是一件很是普通的事情。那么此时,若是指望全部的追随者都即时地看到消息这几乎是件不可能的事情,所以在实现时咱们每每须要构建一个消息队列,将消息快速地派送至队列中等待处理,最终将这条消息“陆续地”显示在每一个追随者的时间轴上。这里势必会产生延迟,但对于业务质量来讲并不是不可接受。但显然这个延迟也不能够太长,在Twitter上这个平均延迟是500毫秒,从绝对数值上看并不算过短,但也已够用。Twitter在这方面的处理方式是利用Scala的Actor模型及Apache Mina编写了一个分布式的消息传输框架Kestrel,它具备快速、轻量(包括注释才不到2000行代码)、持久、稳定等特性,但不具有事务性,也不保证消息的顺序处理。所以能够这么说,Kestrel是一个Twitter根据自身需求“定制”出的消息传输机制。
另一个要点即是每一个大型系统都不可或缺的缓存机制。有人说缓存就比如万能膏药,哪儿不舒服就再哪儿擦点便能见效。这话有必定道理。如Twitter便设计了相对复杂的多级缓存机制,几乎对于每一个IO密集的地方都进行了缓存。例如记录ID序列的向量缓存(Vector Cache),纪录每条消息等具体内容的行缓存(Row Cache)。此外,因为它的API访问量很大,仅仅是从消息内容转化成API的输出形式(可能只是一些字符串链接操做)也会消耗较多代价,所以Twitter还为消息的API输出形式设计了片段缓存(Fragment Cache)。最后天然还有对某些热门页面的页面缓存(Page Cache)。除了页面缓存以外,其余缓存的命中率都在95%以上,可见缓存机制对于Twitter系统的重要程度。值得注意的是,向量缓存及行缓存都是Write Through的,这意味着基本上全部的新数据都在缓存中存在一份拷贝。正如Twitter的Evan Weaver在QCon London 2009会议上讲的那样:Everything runs from memory in Web 2.0。
最后,对于微博类应用来讲,可能会由于某个突发事件形成访问量暴增的现象,如何抵抗住消息轰炸也是一个重要的课题。例如Twitter使用了云计算的方式来应对此类问题,在须要的时候它便会租用更多的计算资源。不过增长服务器只是纯硬件的投入,而架构设计可否顺利且充分地利用起新增的设备也是一个值得关注的地方。从这个角度看,一个高效的分布式消息传递机制在这里会扮演重要的角色。若是有了合适的消息机制,首先可以将消息负载较为容易地平衡至多台服务器上,其次即便是在压力增大的状况下,响应时间虽然会按线性增加,可是系统的吞吐量仍是能够保持在正常的水平。
提问:不少工做2-3年的软件工程师谈到职业规划都是但愿往架构师方向发展,请问能给这些正在成长的工程师哪些建议?如何才能成为一个优秀的架构师?
回答:其实我也不知如何给出一些有效而具体的建议。我认为架构师不是一个职位或是职责,而更像是一种思惟方式。其实只要打开眼界,不断吸取和关注技术及业务的发展,待积累到一个合适的时候即可以对系统架构提出本身的思路及建议的时候,那你就是一个架构师了。
其实每一个程序员均可以是架构师。