杂食性程序猿:专注于 Android,web frontend,flutter,node.js 等大前端领域,也会玩玩 python 和 java 等后端技术 ...

2013年09月 的文章归档

共找到 2 篇文章

node.js在生产(线上)环境中的部署的一点思考总结

什么是生产环境,给人造成的字面上的解释是满是隆隆机器的工厂?或者是妇产医院的婴儿房?在Web开发中,开发环境是指已经运行在正式环境中的代码,与其相对的则是在开发环境中的代码,balabalabala...(扯O中...)

希望上边的一段话不会让你认为本文是懒婆娘的裹脚布的开端。
相比于开发环境,生产环境有几个特点:

a). 对性能的需求

开发环境中,对网站的uv(user view)为1,没什么好说的了,怎么方便,怎么能提升开发效率。抄起你的十八般武艺,怎么使得顺手怎么招呼。
总之一句话,开发效率为先。
对比与线上环境,uv是个不确定的数值,说不定今天放了几张小黄图会引来无数狼友的围观。说不定某天也会门庭冷清。
对于性能的支持上,首先从硬件拼起,本文以本站音韵码工(下文中以"本站"来代替)为例。作者本是北漂的屌丝小码农一枚,没有太多预算,所以本站用的是一台512M,双核的BurstNet小破VPS。用脚趾丫都能理解的到,自然是服务器的数量越多,配置越高,网站的性能会越好。
在硬件恒定的情况下,线上环境,当然要把它调整到最佳状态。可以使用node.js的clurster模块,充分的利用多核CPU的特性。
我进行过简单的测试,cluster能使本站的qps能提升了50%,还是非常给力的,下边的继续优化的瓶颈应该就是I/O的操作了。
详见官本站的启动脚本app.js第55行
正常情况下,你很happy的加入了cluster后,你可能会发现session经常会偶发性的失效了。
因为使用了cluster,查看名为node的进程后你会发现已经不止一个进程了,而express.js提供的默认session是无法在多进程之间共享的。
其实在默认的情况下,当你使用了production的方式启动了express.js,命令行里都会有警告的日志,提示默认的session存在内存泄露的风险,官方不建议使用。
我们需要找个中间的媒介存放session数据,这个时候就connect-redis模块在天空一阵巨响后闪亮登场了。
首先要安装了redis,再安装了connect-redis依赖后,用app.js第32的方式,就可以顺利调用Redis存放session数据了。

对于生产环境和开发环境,express.js也就是本站使用的node.js框架,提供了一个很便捷的配置方式:

app.configure('development', function() {
  app.use(express.logger('dev'));
  app.use(express.errorHandler());
});

app.configure('production', function() {
  app.use(express.compress());
  app.use(function(req, resp, next) {
    resp.removeHeader('X-Powered-By');
    next();
  });
});

configure方法的第一个参数是环境名,只要你愿意,除了production,其他的环境你可以叫他小黄,小白,旺财,小强...
线上环境相比于开发环境,多了压缩,去除log,取消打印错误堆栈的特征,用这个方法很容易实现。
对于性能的配置上,最重要的一点是网站静态资源的管理,也可以非常大幅度的提升网站的整体请求速度。
一个糟糕的做法,是把静态资源直接挂载在网站的主域下,尤其像BurstNet这种主机在国外的VPS,访问速度本来就不怎么给力。
比如有个叫general.js的文件,我直接放在了botobe.net/general.js下。
这种做法同时也是占用了node.js的处理资源,把更多的任务交给node.js进程(也就是网站所在的VPS)去处理。
通常的做法,是把静态资源给分离出来。比如本站的静态时挂载在百度的App Engine服务器上的。静态的位置是可配置的。
可参考本站的配置文件config.js第7~13行

还有些基本的前端的优化,比如css放在头部,js放在尾部,以及spirit,减少文件请求等等。这里就不做累赘的重复。

b). 对稳定性的需求

如果线上网站的网站三天两头的挂掉,这也不是我们愿意看到的。
上线前也不会有QA进行严格的测试验证,遇到问题宕掉也是正常情况。但是作为一个个人网站,问题的关键在于,服务宕掉后,能迅速的定位出问题出在了哪里。
起初我是直接来翻看nginx的error和access几十兆的文本日志。
这个方法很管用,不过却很繁琐,因为访问你网站的在绝大部分情况下,不会是某个“人”。而是一些搜索引擎的爬虫,以及一些机器的自动请求,他们会测试网站的各种对外暴露的请求是否OK,“404的请求”, “文章不存在在的请求”,“以及错误文章url的请求”等。
在这种情况下,于是我最终使用了(forever.js)[https://github.com/nodejitsu/forever]来运行node。
至此,妈妈再也没有担心过我的网站会宕机了。同时能根据指定的日志文件,更加有效和快捷的定位问题。

c). 对于安全性的需求

这是个很大的话题,最常见的需要考虑的一个是XSS,一个是数据库安全。
这个也不是一句两句能说的清楚了,从本站点的角度来讲,对mongodb的操作,一定要在启动参数中加上bind_ip选项。
对于XSS的操作,如果是一个团队开发的话,则一定要协定 好,XSS的问题要在前端(包括前端模板的拼装)处理或者是后台处理。
这是每个网站开发都会遇到的问题,这里就不作详细介绍。

d). 整体流程的优化

这点对于一个项目的开发来说还是比较重要的。
项目从开发到上线,必然会经历一个从测试环境到正式环境的过程。是不可能直接把开发环境中的代码投放到生产环境中的,尤其是前端的代码,否则会的话,严重的情况下回导致你...................................................被鄙视!
在这个环境转换的过程里,如何做到平滑的过渡,是个很值得思考的问题。
本站则是使用了一个node.js的脚本deploy.js来解决这个问题。
几年的从业经验看来,大部分网站解决线上环境到测试环境前端代码的优化的过程,都是采用的一个脚本自己来完成或是调用ant以及grunt之类的自动化处理的工具来完成包括变量的替换,代码的错误检测,压缩以及优化,自动化测试等等工作。
当然也有其他的方式,这个就取决于整个团队的技术实力是否足够强大,或者是否拥有足够多的时间。
比如人人网就是引入了一个名为OPM的工具(PS. 当然是在人人网鼎盛时期,诸神俱在的时候),支付宝据说使用的一个叫SPM自动工具来完成。
具体的工作原理,我就不在这里班门弄斧了,这些工具也都已经开元并且托管在了Github上,大家可以自行翻阅文档。

e). 后台的管理

相信大部分混迹于互联网的geek们都应该见过workdpress的后台,那可真叫一个赞!
相比于本站,那就是金光灿灿的水晶鞋和底部有一个洞的小塑料拖似得。

我是不会告诉你,这个就是笔者混迹于各个豪华CBD之间的常配装备之一的。
水晶的光芒,刺穿的小破鞋如同海盗船上满是漏洞的破帆似得。

本站自己写了一个专门用于发布文章的发布器进行发布文章。
并且写了个只有寥寥几行html的简易后台操作相应的评论等等。

记在最后:
从谷歌里搜索了一圈,发现介绍node.js部署的文章不是很多,于是尝试着自己撰文一篇。
欢迎交流和拍砖。适合初级选手,大神可略过。
转载请注明出处
http://botobe.net/
本文Github链接

2013.10.28
几个月前来到了鹿妖山,一切安好。
Merci !

观察者模式的在前端开发中的应用

观察者模式,是Gof4在经典书籍《设计模式--可复用面向对象软件的基础》提出的一种设计模式。
何为观察者模式呢?

让我们举个

就一目了然了。

大家应该都有去过银行取款的经历,刚进银行,会有热情的客服MM迎上来让你取号。
取号的过程,就可以理解为加入了某个观察团,对被观察者(柜台)进行观察。
试想,如果没有这个过程,我们会怎么做呢?没错,就是不断的去柜台进行询问和催促:轮到我了没?柜台的GGMM每天要接待上百个业务,被你不断的骚扰说不定会有拿出砍刀的冲动。
被观察者的状态会不断发生变化,变化的时候会通过广播的方式来告知大家。
这样就构成了一个简单的观察者模式。

也许你已经有了大概的了解,在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。
而不是需要观察者不断的去询问被观察者是否要有相应的动作。

再来举个新浪微博的例子:
写个简单的观察者类模拟观察者:

var Observer = (function(global) {
  var Observer = function(data) {
    this.data = data;
    this.evtHandler = {};
  };

  var op = Observer.prototype;

  op.trigger = function(evt, params) {
    if (!this.evtHandler[evt]) {
      return;
    }

    var callbacks = this.evtHandler[evt];
    for (var i = 0; i < callbacks.length; i++) {
      callbacks[i](params);
    }
  };

  op.registObserver = function(evt, callback) {
    if (!this.evtHandler[evt]) {
      this.evtHandler[evt] = [];
    }
    this.evtHandler[evt].push(callback);
  };

  return Observer;
})(this);

我们可以把输入框作为个被观察者,当我们发布了一条普通的状态,整个页面上会产生哪些变化呢?
1. 右侧的微博条数应该会加一(在微博改版后好像就不会加一了,老版是会加一的)

2. 会产生一条新鲜事

3. 发布框的状态会发生改变,右上角的数字会变为140

通过代码的方式,可以如下表示出来

var handler = new Observer();

handler.registObserver('publishUGC', function() {
   // 请求一次新鲜事,并且更新界面
   // ...
})
handler.registObserver('publishUGC', function() {
   // 改变发布框的状态(更新可发布文字)
   // ...
})

handler.registObserver('publishUGC', function() {
   // 
})

这时候你肯定会想,这么做有什么好处呢。代码的复杂度上升了,若是直接在调用了发送新鲜事的接口的回调中几十来行的代码把上诉的1,2,3立即给解决了岂不是更爽?
各路看官应该都会有自己的看法,我的看法如下两点:

  1. 模块之间最大程度的得到了解耦 随着前端的代码结构的越来越复杂,很多团队在开发时候都采用了模块化的方式。使用观察者模式,使代码达到解耦的优势也自然体现出来。
  2. 便于开发人员后续的维护 面向对象编程有个很经典的观点,增加原有系统的功能最好的方式是扩展他,而不是去修改他。 观察者模式做到了能把系统用扩展的方式来增加功能,而不需要在代码中一遍遍的修改,改到最后变得一团糟,最后变得难以维护坑爹骂娘的吐槽着极品的前任(有木有淡淡躺枪的感觉?)。
  3. (欢迎补充或者拍砖)

现在流行的web框架backbone.js,是使用观察者模式的一个很经典的例子,将模型(Module)和视图(View)的修改进行绑定。
改变了模型,绑定在这个模型上的一个或者多个相应的视图也会跟着进行改变。

写的有点乱。
最后的吐槽:
原来的Wordpress网站botobe.com被停掉了,可4我一直在用他的代码排版格式化插件啊!知晓第一时间内为毛有种菊花一紧的赶脚?!
看来我连格式化代码的功能都要投向Github的的怀抱了。

本文Github链接

2013.09.04
一切安好。
Merci !