这些日子一直在做 Node 方面的尝试,或多或少会收到周围的异样的目光甚至背后的质疑,于是促使我好好思考为什么我在做 Node。网上搜下「为什么要用 Node」,找到的文章多数是介绍 Node 多么多么牛逼,无非是从 Node 本身特性来说,比如:并发、事件驱动、非阻塞 I/O、单线程、流、社区生态…… 诸如此类,很少谈业务场景。
我是「实用主义」者,说过:脱离业务场景谈架构都是耍流氓。因为个人是从一线业务做起的,经过几年对业务的思考,我觉得可以从业务场景来说说为什么我们的业务更适合用 Node。
从业务场景说起
现在我们的业务模块化越来越普遍,很少有业务比较纯粹只有链接一个数据库就可以搞定,往往前台业务后面会有 N 多的 API 服务做支撑。比如:下面两种情况在我们实际开发中经常遇见:
- 某个页面需要的数据来自两个以上接口,而两个接口来自不同的团队 / 部门,比如:用户信息来自账号部门,而 UGC 数据来自业务部门
- 某个页面存在接口依赖,需要先调用接口 A,然后根据接口 A 数据调取接口 B,比如:个性化推荐,往往需要根据某些维度请求推荐系统拿到推荐数据的 ID,至于内容,需要拿 ID 根据页面需要去获取具体元数据
上面两种情况,站在后台开发的角度来看,我们业务模块要分开要独立,而站在前端的角度来看,这些数据都是一个页面需要的,前端希望是一个接口给我返回。这是一个开始。。
当然后台开发,比如 PHP 也有并发请求的解决方案,好(上)心的后台工程师,会帮助在后台统一合并请求处理成一份数据或者接口,然后扔给页面使用。比如在实际开发中,我们的前端会写(并且维护)一个 Template.class.php(我敢说我们 80% 的后台工程师都没看过这个代码。。),在 View 层使用,然后在 Action 当中将数据传给 View 层做渲染,下面的代码:
- $this->render('xxx/xx.tpl', $tplData);
这样增加的沟通成本,降低了开发效率。为了一个页面,需要前端根据页面想要的数据,和后台沟通页面的数据格式,然后后台工程师找他们后面的 API 模块要数据、处理数据。这个过程中会有一些「灰色地带」,不好明确谁做更合适,完全靠自觉。
往往开发的时候会想各种方法来解耦,比如:引入后台模板(smarty 之类),然后约定数据格式,前端根据数据格式来写 Mock 接口,写后台模板的前端就叫「大前端」;再 Low 一点的团队,会采取前端做好页面扔给后台工程师「套页面」,比如:PHP 代码写 HTML,各种 <?php echo xxx;?>,代码很不友好,后台工程师幸福感也急剧下降。
还有一种做法是,干脆后台沦为「代理服务器」,收到请求我转给后面的 API,拿到数据我返回给前端页面,做成可以「跨域」的接口,所以就成了好多 webapp。
另外,站在后台工程师的个人发展来看,可能他们觉得:这些「包接口」的重复性工作,跟自己的晋升和技术发展又有毛线关系呢?
说道这里,肯定有人心里在嘀咕:这是你们大公司才有的问题,我们小公司不会有这样的问题!那我下面再从技术方面来说。
从技术方面说起
从性能优化、工程化、解决方案这三个开发中最最常见的方面来说明为什么前端的事情前端做更合适。
性能优化
前端页面是重要的载体,出现问题或者页面体验不好会对用户造成直接的伤害(我们都是背锅侠)。页面性能这些问题显然是前端的头等大事,但是这些事情跟后台工程师关系多大呢?当你发现该优化的项目都优化完了,剩下的优化项目就需要跟后台工程师一起优化了,而这时候再去 push 后台工程师一起参与前端优化项目。
工程化方案
除了前端页面的性能优化这种项目,还会有一些工程化的工作,帮助提高前端的开发效率和体验,但实际上只有前端是搞不定的,比如:
- 根据打包工具做的 resourcemap,实现页面静态资源的 CDN 地址合并(combo)输出和分开输出(调试阶段)
- 扩展 Smarty 语法,实现模板组件化
- 模拟后台数据接口,输出假数据渲染的页面
这些工程化的工作本身前端自己理解的很清楚,但是后台工程师会有多少了解呢?怎么不可能让对前端不了解的后台工程师参与进来呢?解释要做什么就花费不少时间。
解决方案
再说解决方案,简单点如果我们要实现页面 chunked 输出,将动态和静态数据分开,不依赖接口数据的数据首先展现(比如首屏的 Nav),那么也要依赖后台工程师的代码。再大一点,我们上个类似 Bigpipe 的方案,那么对后台的依赖和改造更大。
技术这些问题怎么办?
上面的这些诉求,有两种方案:前端自己撸袖子来搞,他们或者是直接写类似 PHP 来实现,或者是写不伦不类的 Smarty 扩展代码;再者就是可以出一个「技术产品经理」,专门立项来搞这些项目,由「技术产品经理」来协调两边需求,避免「鸡同鸭讲」。可是业务部门项目压力是非常大的,很少有这样的项目,而专门做技术的团队呢,又很难深入业务,往往高高在上,搞出来的东西要么不合实际、要么太高新尖端,导致水土不服,强推起来,业务团队哀声哉道。我也说过:脱离业务的架构都是耍流氓。。
为什么用 Node
根据上面说的,大概得出使用 Node 有下面的好处:
- 天然的事件驱动可以用于处理并发
- 降低前后端协作成本,提高开发效率
- 职责分明,前端的问题前端 er 自己负责,自己解决
- 前后端同构,前端解决方案同步到 Server-side
- 有利于前后端同学的个人发展
什么业务场景使用 Node
同时,大概得出什么样子的业务场景使用 Node:
- 页面需求大,从样式到性能都一直迭代
- 后端接口丰富,页面数据资源方多
- 纯渲染,对安全性要求不高,无计算能力
- 最后,最重要的是团队的能力(我还说过:脱离团队的架构都是耍流氓。。)
对 Node 的质疑
当谈到「我们要用 Node」,往往会遭到如下具体的质疑(注意是具体,不是纯怼):
- 稳定性:主要是单线程异常处理
- 异步回调导致程序复杂度提高,不利于调试
- 和后台接口的对接能力
- 工作流程:代码部署、上线
- 运维支持:接入、容量管理、日志、监控等
- Node 版本升级太快了,NPM 包靠谱吗?
先说这些前面三个问题:
- 稳定性:选择的 Node 框架对异常处理要友好,进程守候有 forever/pm2 这些包可以帮我们,目前已经成熟,而且 Node 也越来越完善和稳定
- 调试:现在用前端流程的 IDE 很容易调试
- 后台接口通信协议如果过于复杂,可以通过 C 的 Node 模块来解决,PHP 不也是吗?
后面两个问题主要是跟公司运维能力有关系:
- 工作流程:目前百度内部的工作流程做的很好,从代码提交到打包编译和上线都是一整套解决方案,而每个环节之间只需要按照约定进行输入输出即可
- 运维:百度内部的 ORP 是很好的 PaaS 解决方案,提供了虚拟化的实例,Node 代码部署在实例上,通过统一的 nginx 反向代理分发给不同端口号的实例处理,支持资源弹性调度,日志只需要按照约定放在某个文件夹,可以配置采集任务,进行采集和监控
所以我们厂子内部已经为 Node 大规模使用在流程和运维方面已经做好了准备。
最后说下 Node 生态问题,Node 版本的确升级很快,但是只关注 LTS 版本,等发版一段时间之后跟进更新即可,目前百度内部的 Node Runtime 是 6.10 版本,听说很快就要生 7.0 了。对于框架和业务代码用到的 NPM 包,完全可以做版本指定,也可以做自动测试,跑过了 case 则提交进 master,随着下个版本回归上线。
前端写后台代码会有什么问题?
不管怎样,我们用 Node 已经变得顺理成章,但是我们也要知道自己的不足,拓宽自己的视野。
首先是后台思想,在前端开发中,我们的代码是跑在每个用户自己的浏览器里面,代码之间是隔离的,所以不管你代码写的好坏,只要是说的过去,就不会有大的问题,比如:偶尔内存泄漏一下,似乎也影响不大。但是 server 的代码是长久执行下去的,不是用户走了就释放的,所以一个小的内存泄漏,长时间下去也会引起大的问题。再比如:浏览器的 JS 你可以偶尔使用个全局变量(少写个 var),但是如果在 Node 的代码,将用户相关的个性数据放在 global,那么当下个请求过来,而代码还没有处理完当前请求,会导致自己用的数据不是当前用户的,碰到这种问题,只能把个性化的数据一层层的传下去。还有缺乏优化意识或者过度优化,该用缓存的时候不用,不该用的时候乱用。
再举个 case,看下面的代码:
- module.exports = function (ip) {
- var ipfinder = require('ipfinder');
- ipfinder.loadData('ip.data');
- return ipfinder.findSync(ip);
- }
这个是数据实时分析项目的一段类似的代码,ipfinder 是我写的一个 IP 查找库, ipfinder.loadData('ip.data’);会引入一个二进制的 ip 数据库,这个比较消耗资源,写在 module.exports是没有必要的,每次执行 module 都加载一遍,很费资源,拿到 module.exports之外,程序的 CPU 从 99% 降到了 5%....
第二个是安全意识,之前在「Vue 项目重构」中提到 proxy.js的代码弊端,就是缺乏安全意识导致的。前端写后台程序,因为缺乏安全意识,往往在接口设计、页面片段拼接等方面犯错误,比如:接口设计的过于简单,缺乏校验,容易导致 CSRF 攻击,如果有数据库操作,手动拼接 SQL 语句容易导致 SQL 注入。
最后是运维知识,前端工程师写 Node 服务,就不在简单的对浏览器负责,而还应该对服务器负责,服务器的稳定性、各种监控指标都应该有所了解,对于机房配置、资源调配、运维架构、服务架构都应该了熟于心,避免出现线上事故了自己还不知道从哪里排查的窘态。
当然你可能会说,刚刚开始接触是可以允许犯错的,但是要知道:技术的调整是不应该损害产品服务的。以上三点内容需要刚刚转 Node 开发的前端工程师注意加强学习,能力越大责任越大!开始时候可能会犯错和抓瞎,该请教就请教,时间长了能够点亮新的技能点~
【本文为51CTO专栏作者“三水清”的原创稿件,转载请通过微信公众号联系作者获取授权】