大约从2004年吧,在Java作为面向表现层Web技术解决方案风生水起的年代,整个开发业界都陶醉在Java EE无所不能的幻象里,而一度忽略了脚本语言的价值。另一个极端,PHP、Python、Ruby社区死硬坚持自己是世界的主宰。但是,一切都没有停止,新的技术不断跃跃欲试。

2011年出场的是一门历史悠久的语言:JavaScript

——当年大家认为的“小玩意”,现在摇身一变,一口吞食了服务端开发的半壁市场。

这就是 Node.js,一个服务端运行JavaScript的运行环境

Node.js的出世,多少还是触动了Java界,使开发者重新思考脚本语言的价值。甚至PHP的拥趸者也不再那么频繁提及“PHP是世界最好的Web语言”这个梗,甚至有PHP开发者出了一本《写给PHP开发者的Node.js指南》,可见Node.js的对开发领域的撼动。

Node.js缘何如此卓越?

Node.js的异步非阻塞I/O实现优雅、性能强劲;抑或脚本语言开发敏捷。不过在笔者看来,它成功的原因只有一个,就是采用了JavaScript语言。

举个例子,假设你所在的组织正在使用Node.js替代JSP做Web应用,有一天Node.js工程师请了假,需要Web前端工程师(浏览器端开发者)支援修改一个紧急bug,打开Node.js服务端代码发现一切都是那么熟悉,你可能惊呼:“原来我也可以做服务端发开发的嘛…”。没错,因为都是用JavaScript写的,浏览器端开发者只要稍加学习Web服务端的开发模式,就可以顺便承担了Web服务端开发工作。如果你的组织正在使用Java等其他语言作为服务层,Node.js所在的Web层开发工作将更加单一,获取服务层数据绘出展现传给浏览器端,而这份工作内容更适合由Web前端工程师担当。

还有一个顺便,就是顺便让致力于Web表现层开发的Phper丢了工作(想想此时谁最开心,是不是你的老板?)

——借助同栈语言,Web前端工程师又将Web层服务开发纳入麾下,而这一切都源于Node.js。

Web应用模块化

作为一门新兴服务端运行时技术,Node.js的确再次发明了很多的轮子。

比如,Web层Framework:Express.js、ORM工具Sequelize。

Node.js应用领域极其广泛,途牛首先将其应用在系统Web层,第一个基础设施实现毫不例外,就是Web Framework:LightMVC

——它让你的项目代码整齐安放,这关乎代码演进过程中的可维护性,对任何组织都至关重要。

下面是一个典型的基于LightMVC的Web应用工程目录(为了简化说明,我们虚拟了一个项目Mars,运行在域名mars.org上):

有经验的Web层开发者对上图描述的是什么了然于胸,不过我曾过听一些Phper的一些人评价说:“Node.js也没什么创新嘛”。这个论断显然肤浅,因为Web层解决的问题是同一的,无关于编程语言。所以你可以说这是重复发明轮子,但,这可能是一个更好的轮子。

63313-2x7axa1a105.png

随着工程规模逐步扩大,按业务等维度拆分相对独立的目录,每个目录内聚完整的功能,这是个很自然的想法,工程目录结构可能演化如下:

60776-4lhqv00ybzd.png

这里,bundleA、bundleB、bundleC目录即为每组功能代码的集合,它内聚了一组紧密相关的页面功能,分别是一个完整的微小应用,包括业务逻辑和展现,这个和通常的一组SDK是有本质区别的。

LightMVC也为这样的模块定义了一个专用名词:bundle(以下都称bundle)。

再后来,bundle的拥有者和开发者越来越独立:每个bundle都可能由独立的部门开发、独立的GIT/SVN等管控代码,工程目录可能自然演化为如下模式:

62511-moe7k3lgez.png

node_modules目录下下载了来自NPM仓库的包(NPM是Node.js的包管理工具,它查看package.json声明的依赖包,自动下载NPM仓库里的代码到node_modules目录)。

模块的开发者单独开发和维护自己的bundle项目,开发好后发布运行时代码到NPM仓库,由Mars(主项目)显式引用安装,像下图所示的那样。

75700-aemui5rru3.png

这里Mars仍然是整个系统的管控者。

开发时,bundle开发时需要依赖Mars作为一个完整的Web项目调试;

运行时,也要作为Mars的一个依赖包发布,就像Mars的寄生。

随着独立bundles的扩张,想必用不了多久,Mars的规模将膨胀为巨无霸而失控。

Mars(主应用)作为项目协作的单点,常常面临下面这些恼人的事情:

1、bundle项目依赖与主应用的基础设施(如环境配置和公共请求处理),每个bundle的开发者需要搭建和主应用类似的项目组织结构,并从主项目里拿到这些公共依赖,否则无法调试。

2、bundle的开发者每发布一个特性,必须主动推动主应用集成和发布。bundle开发者总是要同时维护自己和Mars两个GIT/SVN应用仓库,对吗?因为,要在Mars里显示引用bundle,总是要做必要的配置修改。

bundle 依附于主项目才能运行,所以看似独立,其实并不独立。

想象一下,如果有5个团队同时在增强5个bundles,这个场景可能是多么混乱: 48882-vcg3tsnlh.png

bundle开发者甚至来自第三方组织,也可以闭源,由于上述协作的强依赖,问题和风险可能会更多。

另一方面,开发者自豪地宣称类似这种bundle渐进软件增强模式为插件模式。如果全社会协作开发各种bundle增强一个应用,貌似无所不能呢。当然现实并不理想,”规模”让感觉变了味。接下来,我们仍然把更多关注点放在一个组织内部的应用协同上。

Web微应用化

Web应用通常走到“模块化”就停滞不前了,代以人为控制bundle的数量来控制项目规模,这可以使前面阐述的模块化问题:依赖主项公共设施和主项目集中管控一定程度弱化,但,解决不了本质问题。

考虑下面的项目配置结构:

68366-nyopm86aakm.png

bundleA、bundleA、bundleC开发时作为独立的Web应用,所有公共元素(不止基础SDK),独有的Web元素:配置、请求过滤都可以抽象出来给每个bundle项目引用,各项目在此基础上可以对这些公共元素进一步进行个性化定制。开发调试完成后,仍然作为独立的Web应用发布。

在这种协作模式下,大家发现Mars本身已经不复存在了,虽然逻辑上仍然属Mars项目。而各个bundle却真的不是bundle了,就是一个完整的、高内聚小Web应用,我们称之为微应用(Micro App)。

和上面的模块化比起来,变化的要点在哪?

  • 每个bundle蜕变为独立的Web应用,独立开发和发布;
  • 所有公共元素都要抽象。除了基础SDK;
  • Web独有的元素:请求过滤、配置。

微应用貌似 bundle 模式的倒装,根本上没有引入任何新的概念,一切在于思维的转变。一个庞大Web应用拆分微应用时,必须做到:

公共配置、公共初始化服务、请求过滤等设施的剥离,仅仅SDK抽象是不足以降低开发、协作成本的。

敏捷实施

Docker

如果一个项目拆分为数十个微应用项目,将不得不为每个项目配套完整的软硬件运行环境,传统的运维方法不堪重负。

Docker的兴起为这一切提供了便利。途牛自研的运维管理系统,可以保证在一分钟内创建一个完整的Web应用,便利地发布和回滚线上应用,这为我们微应用的推广实施提供了强有力的支撑。

应用边界

在用户看来,www.mars.org 是一个网站。但“微应用”是站在开发者的视角。在微应用实施后,原来Web服务器的配置就很容易地从逻辑分开。比如,将原来大量的rewrite等本属于应用内部的配置工作迁移回应用内部。

CDN也可以根据应用标识符溯源静态资源,从而不必将静态资源手动发布到CDN服务器,只要遵循一定的命名规则放进应用项目本身即可

微应用很多,更强化了应用边界的重要性。应用边界清晰,维护人员职责划分更清楚,问题定位也就更快更准确。

软硬件监控

微应用上线后,本质上是一个大网站,业务划分不同而已。所有的微应用运行日志收集都聚合到一个巨大的日志仓库,附上应用名作为分类,可供开发者全维度检索和分析。

另外,我们也为LightMVC开发了一个管理bundle,可以实时监控和分析系统的硬件负载、内存异常分析等。

所有软硬件均设定异常阈值报警,使线上问题得到及时通知和解决。

途牛运维系统对线上应用的可监控性、快速伸缩性的支持,完美地确保了Node.js应用在途牛的敏捷实施。

最后

Node.js的应用远不局限于Web层应用,Jeff·Atwood 早在2007年就提出:“ 任何可以使用JavaScript来编写的应用,并最终也会由JavaScript编写。” 希望下次和大家一起探讨Node.js在其他领域的应用。

原文地址:https://mp.weixin.qq.com/s/TI8VgCpToFQDrITLyac3Rg | 作者:郭君龙

标签: node.js

添加新评论