Node.js适合做主流大型系统吗?

这是来自richardrodger一篇博文,主要阐述微服务和模式匹配(消息),通过Node.js建立一个Http服务器作为微服务,微服务之间通过消息传递,以微服务构建模块,进而模块化构建一个大型系统。原文大意如下:

Node.js能做大型系统吗?回答是肯定的,沃尔玛(Walmart)和Paypal的案例已经证明,那么大型的Node.JS适合主流的开发者吗?如果你认为Node.js这样的大型系统类似Java和.Net,那么你肯定认为不行:javascript在支持复杂系统扩展性方面是一个弱性的语言。它不是强类型语言,而且有一半的语言是无法使用的。

尽管如此,我们已经建立一些大型的NodeJS系统,下面是我们的总结:

传统的主流的最大型的系统是庞大的铁板一块的monolithic,一个特别大的代码库,有很多文件,几千个类无数个配置文件。如果在javascript中建立这样的体制,可能会发疯。那些将Node.js看成类似蜘蛛侠的人只会推崇使用Java和.NET建立庞大的系统。

铁板一块式monolithic系统是坏的
什么是铁板一块monolithic系统,最简单定义就是,你如果拿出来一部分,整体就会运行失败,每个部分连接到其他部门,相互依存。这个词也用来指一块整石头。

这个词语来自古希腊,建筑直接在洛矶山的悬崖上凿成,由于直接雕刻整个山,他们是无法修复的,城市最终陷入下降。软件系统的复杂性随着时间推移,难以修复和延伸,成本不断提高。

铁板一块式的软件产生的开发流程和方法危害更大,因为系统有这么多依赖,你必须关注如何让开发者在改变它们时非常小心,大量精力用于防止代码失败。从瀑布到敏捷,都是为了铁板一块式的软件服务,都是为了让软件变得越来越大。

模块化系统是好的
作为软件开发者,应该努力避免建立铁板式系统,那就是建立模块化系统,当一个部分丢失时,整个系统还是可以运行。虽然像集装箱那样建立软件的模块化是一个很艰难的过程,但是我们知道这是处理复杂性的唯一方法。

对象是地狱
为实现模块化系统,面向对象范式受到推崇,其实对象比你想象中的要相差很多,他们来自天真的数学观点,是一种纯柏拉图的东西,以相同的属性和特征划分对象,表面上看合理,实际你在打破了现实世界的真实面貌,现实世界是混乱,甚至都打破了数学世界。请问集合是包含自身还是不包含自身呢(banq注:罗素的逻辑悖论)。

对象的最终弱点是,他们只是促成了向铁板式庞大系统发展,对象只是一个系统需要的袋袋,什么都可以往里面装,你有属性,私有和公有,你有方法,也许覆盖了其父类,你也可以有状态。
有太多的东西可以装入对象,结果一个巨大的类包含了最纠结最痛苦的大量逻辑。

NodeJs的模式是简单的
有一个例子认为计算斐波纳契数是CPU密集型,而Node.js只有一个线程,那么计算斐波纳契数的性能是可怕的。你可以使用异步委派到到操作系统,使用processes 过程而不是线程处理,为了避免CPU密集型的计算,你需要将计算任务放入队列,然后异步处理它们。

线程是出了名的难,Node.js的胜利就是避免了它们,你的代码变得易懂。

有一个问题是在javascript中如何实现单例,实际上在现实中很少会碰到这种问题,因为模块已经帮你照看一切,最好的方式是你使用Node.js建立一个Server,通过网络和其他部分通信。

NodeJS确实需要你学习一些新模式,主要是回调模式,回调函数的签名有一个规律:如果有错误发生,第一个对象是错误对象,否则,第一个参数是空的,第二个总是返回的结果。

回调函数自然地来自Node.js的事件处理循环, JavaScript最初用来处理浏览器中用户界面事件,现在非常适合处理服务器端事件果。

当你开始使用Node.js的第一件事就是创建回调(像意大利面条)。你结束了大量的缩进代码,使用回调内嵌回调。经过一些练习,你很快学会使用命名良好的函数结构化你的代码,包括异步模块库。

Node.js的另一个重大的模式是流,已经被编写成API,让你轻松 简洁操作和转换数据,你使用管道将数据从一个流接至另外一个恶瘤,你可以读写全双工的数据流。

以正确的方式思考
编程语言会应当让我在一个正确的层次去思考,可惜大多数语言在此惨遭失败。编程语言在我们思考复杂性过程中成为我们的羁绊,最终发生抽象泄漏(http://www.jdon.com/46006)。

我们应该改变到以面向行动为导向的看待世界方式,这样才能建立好一大型系统,为什么设计模式会失败?因为他们使用静态的概念表达世界,而世界并不是这样,我们的大脑也不是这样工作的。

比如我们开始一个系统总是从用例开始,这是和静止的实体无关的,如果是初学者肯定首先编写一段面向过程的代码,然后才建立一些数据结构,我们现在可以遵循这种大脑自然思考方式上做得更好。

什么是新的设计模式?在我们为客户开发的项目中,我们使用了两个主要方式:微服务和模式匹配。

(banq注:之前已经节省了不少翻译,到这里才进入主题)

微服务Micro-Services
我们可以用生物细胞作为灵感构建健壮的可扩展的系统。生物细胞具有许多有趣的性质。它们体积小,单一用途。

我们不是建立一个铁板式的100 000行的代码库,建设100个小型服务,每个服务有100行代码。弗雷德·乔治(程序员无政府状态的发明者)是这种方法的最大支持者之一,称这些小程序为微服务。

每个微服务执行一个非常有限的任务。这有很好的效果,他们很容易验证。您一眼就能看到他们,测试与否都不重要的。在人的方面,这也意味着这种代码很容易改写,甚至使用不同的语言也是这样。如果一个初级工程师写不好微服务实现,你可以把它扔掉,并重写。微服务易于更换。微服务之间通过发送消息相互通信。您可以直接通过内部HTTP发送这些消息,或者使用消息队列应对大规模。

微服务易于扩展和伸缩,能够达到更精细的粒度级别。目前我们还没有发现所有这些过程的管理过于繁重。

一般来说,你可以使用监控工具来确保服务保持正常运行。一个服务死了处理就变得相对简单。你会有一个重要的服务运行多个实例,然后很快重新启动。如果一些奇怪的事情发生了,也还是重新启动。事实上,这可以让你的系统变得非常强大,可以防止各种腐败现象的积聚。

微服务让你部署任何系统都很容易。刚开始更换一些服务,看看会发生什么。回滚是微不足道的 - 重新启动回归旧版本。微服务也让您的开发团队可伸缩,无论是在个人和团队。个人的大脑更容易和微服务一起工作,因为考虑的范围是如此之小,并且很少有副作用。您可以专心于当前的用例。如果扩展到团队也是很容易,纳入新的服务,知晓会有团队成员之间的一些依赖和堵塞。

最后,微服务以软件的独立单元让你映射到你的用例。他们让你考虑发生了什么。这超越了对象概念的范围。

模式匹配

模式匹配可以让我们在一个合适水平思考,模式匹配以消息为核心,服务之间的消息需要发现一个路径以便到达正确的目标服务,模式匹配越简单越好。当然消息可能并不包括所有的属性,这是使用切面关注比较方便,比如记录所有有关保存数据的消息,只是在这些消息出现时抓取,作为日志项目加入,然后继续发送这个消息,你可以加入权限 缓存等等机制(AOP),所有这些不会影响服务。

一个案例是用户注册,你可能有一个基本注册服务,用来保存用户资料到数据库,但是你要发送一个欢迎Email,或者做其他一些逻辑,你并不能继承这个基本类,你只要继承用户注册的消息即可。

微服务和模式匹配能够很容易使用Node.js实现,这个工具包是Seneca(http://senecajs.org/)

senecajs实现微服务案例如下:


require('seneca')()
.add( //加入一个JSON消息匹配模式
{ generate:'id'},
//这个模式是 {...,"generate":"id",...}
function( message, done ) {
//这个模式的Action函数
done( null,
//回调函数
{id:''+Math.random()} )
//发回另外一个JSON消息作为结果{"id":"0.123456789"}
})
.listen()
//侦听消息在缺省Http端口

下面是微服务的客户端:


require('seneca')()
.client()
.act( { generate:'id' }, //发送消息{"generate":"id"}
function( err, result ) {
console.log(JSON.toString(result))
//输出结果{"id":"0.123456789"}
})


这。。又来个批判面相对象的。。。。。。。。。。。到底闹哪样啊。那我们用nodejs编程时,心里要不要有对象思想?

要做微服务的话,Java之类的照样能写。微服务实际上就是几个singleton的服务实例放在那里互相交互(高级点的带队列),不一定要发消息,可以在同一个VM内交互,一样是解耦的。
然后呢... 价值几何?跟分布式系统似的,雷声大雨点小。
面向对象和设计模式为抽象和复用而生,微服务为什么而生?
如果软件开发有这么简单,那可真爽啊。
我们的首席工程师对这类以一蔽百的概念评价为——“卖假药的”。
祖传微服务,包治百病!

2013-12-25 23:51 "@sorra"的内容
我们的首席工程师对这类以一蔽百的概念评价为——“卖假药的”。 ...

不能这么简单下定义,好像当初对云计算定义一样,见我这个新帖:
什么是微服务:http://www.jdon.com/46029

微服务的革命性我认为在于,替代了以前传统使用OSGI实现系统模块化的方式,另外一个特点可能革了Tomcat JBoss GlassFish Websphere weblogic等的命,因为服务不再运行在容器的OSGI中了。

取代OSGi可以。不过这个作者说“对象是地狱”“设计模式是失败的”,就变成兜售假药的性质了。因为微服务解决的压根就是另一个问题。

如果服务无状态,那做成一个库就够了。
如果服务有状态,那么事情绝不简单,比如不同用户的并发冲突、需求冲突怎么处理。(MQ仍显naive)

微服务作为模块化方案/异构系统的解耦方案,当然还是有价值的。

2013-12-26 11:39 "@sorra"的内容
这个作者说“对象是地狱”“设计模式是失败的” ...

是有些极端,但是还是有一些道理在里面。比如对象的继承是坏的,可以使用组合+依赖。

比如我们分析设计时,原来是首先找出实体为主,后来发现实际应该是首先找出找出聚合,然后又发现首先划分聚合所在的上下文(场景)更重要,到现在,我们都明白了分析设计首先是应该找出分界的上下文,但是这个界限也不是一下子能划出来的,因此我们发现,因为我们人类对动词比较敏感,正如学校刚毕业初学者编程序,开始总是从一个过程函数开始,然后才用到数据结构,算法+数据结构,算法在前面,算法是一个函数或动词。

那么现在我们就是从事件开始,因为事件可以从用例功能里面直接映射出来,有了事件,我们能划分上下文边界,正如我们从流程开始设计,才会发现流程中涉及到哪些实体一样。建模风暴这个PPT也谈了这个问题,先是领域事件风暴,然后才是模型风暴,动词在名词之前,而不是以前宣扬的名词模型在动词之前。

另外javascript的设计模式因为是面向函数风格的,类似GoF设计模式的观察者模式,偏重行为模式,而不是过去我们强调的偏向静态的结构性模式。

其实两者有时不矛盾,比如我们发现了结构性的静止的聚合对象,聚合根实体通过领域事件这个行为与外界交互,微服务是交互的发生地,也就是上下文(场景),如下图:


[该贴被banq于2013-12-26 16:14修改过]

话说回来,其实就是新一代的SOA吧 :) 更强调异步了