Mockee Nodes

"Happiness only real when shared."

NodeCat 开发笔记



简单记录下开发中遇到的一些问题与解决办法,方便日后查阅:

中间件与框架

NodeCat 使用 Express 框架搭建完成,当然也可以说是 Connect 这个中间件系统协助完成了大部分工作并让整个开发过程变得简单轻松——Express 基于 Connect 构建,中间件的设置方法与其基本一致,比如协助将客户端 post 的内容放入 req.body 的 bodyParser()、处理用 POST 伪装的 HTTP 方法(PUT 和 DELETE 等)的 methodOverride() 以及用于错误处理的 errorHandler()、请求日志的 logger() 等等。此外,Connect 在借鉴 Rack(介于 Ruby web 框架与 Ruby 运行时之间的中间层,类似于 Express 与 node.js 之间的 Connect) 和 ejsgi 后所提出的分层处理 HTTP 请求和响应的想法更是令开发灵活多变。

Connect 1.7.0 提供了一个位于 static() 之上名为 staticCache() 的新的中间件作为 memory cache 服务,ApacheBench 结果显示它比单独使用 static() 中间件或 node-static 的性能都出色:

Then we have the new staticCache() middleware paired with static() serving ~5000rps, a 24% increase over node-static (which also performs memory caching), and ~52% over static() alone.

同样可以为 Express 加入 staticCache:

app.use(express.staticCache());


Express 中实现图片上传功能很简单,利用表单数据解析模块 node-formidable 以及中间件 connect-form 就可以轻松完成。存放图片的路径通常是根据当前日期来设定,所以就会有个多级目录的创建问题,当然这并不困难,引入 path 与 fs 模块写个递归就可以解决(或者直接用 node-mkdirp),需要注意的是创建目录时的权限问题。

模板引擎与 CSS 语言

jade 可以选择将 HTML 压成一行,但并不包括行内脚本;stackoverflow 上有人提出使用 UglifyJS 并为 jade 增加一个 filter 的方法来处理,可行,但却修改了 jade lib 中的文件,所以我并没有在实际项目中使用这个方法。好在 nodecat 的行内脚本大多只进行模块的组织工作并不涉及业务逻辑,代码量小,手动压缩即可。如果你不喜欢 jade,可以随时将 Express 的 view engine 换掉,比如使用熟悉的 Mustache,有个名叫 Stache 的项目可以帮你在 Express 中实现:

app.set('view engine', 'mustache')
app.register(".mustache", require('stache'));


从功能上来说,StylusSaasLESS 都很强大,但更偏爱语法简洁的 Stylus,搭配 nib(如果你使用 LESSLESS Elements 也是很好的选择)可以有条理地规划并高效地书写 CSS(在过去的很长一段时间内,前端工程师都在为处理浏览器兼容性以及不可避免地堆砌冗余样式耗费着大量时间)。有关 Stylus 的配置(compress, debug)和常用方法(function, arithmetic, mixin)都可以在 example 中找到。

数据存储

Express 中,session 中间件默认使用 Connect 内置的内存存储方式,但除此之外还有很多其他的选择,比如使用 connect-mongodb 将 MongoDB 作为 session 的存储介质等。由于我在 NodeCat 中同时使用了 Mongoose,所以换用 connect-mongo 更方便(Stack 上也有人提到 同样的问题):

var mongoose = require('mongoose'),
mongoStore = require('connect-mongo');
// configure
app.use(express.session({
secret: 'nodecat',
store: new mongoStore({ url: app.set('connection') })
}));
// connect to mongodb a second time using mongoose
mongoose.connect(app.set('connection'));


NodeCat 中没有文章分类功能,相较分类我更喜欢标签(tag)这种细粒度的泛描述。每篇笔记(每个 Note Schema 的实例)都有自己对应的 tags 数组,这样,根据具体 tag 的名字就可以方便地找出包含此 tag 的所有文章,如果数据量大,可以为 Note Schema 建立 tags 的索引。

网站首页右侧有个最近回复列表,需要显示最近回复者的名字以及被回复文章的标题和链接。之前对 MongoDB 了解不多,在设计 Note Schema 的时候把 Comments 对象也塞了进去,导致取数据时绕了弯子,后经同事点拨,为 Comment 单独建立了 Schema,将笔记与回复分离,用 id 做关联,这样按创建时间获取回复数据自然就是“最近回复”了。

曾考虑用 disqus 作评论系统,省事省心,速度也不错,但最终还是放弃了,毕竟不是本土服务,链接被重置的情况随时可能发生,伤不起。正如你看到的,目前 nodecat 没有在评论部分多费精力,对于自用的东西多少有些放纵——我假设来到这里的朋友都可以正确地使用自己的名字与 email 并对言论负责。

评论中的用户头像都是通过被 hashlib 模块 md5 加密过的 email 到 Gravatar 换取的,Mongoosevirtual attributes 可以方便地将这个过程封装起来供 comment 实例调用而不必单独存储 avatar 到 mongodb:

Comment.virtual('gravatar').get(function () {
var gravatarHost = 'http://www.gravatar.com/avatar/';
return gravatarHost + hashlib.md5(this.email) + '.jpg?s=' + 32;
});


除此之外还有很多地方都用到了 virtual,比如输出被 node-markdown 解析过的正文与评论、格式化日期和 url 等。同时不免还要用到 Methods,比如 password virtual 的 set function 中将密码掺入 salt 后加密的 encryptPassword method:

User.method('encryptPassword', function (str) {
return crypto.createHmac('sha1', this.salt).update(str).digest('hex');
});


服务器

Cluster 是目前正在使用的服务器管理模块,它可以利用服务器的多核心来动态实现简单的负载均衡以确保网站可以无间断地提供服务。修改 node 程序后无须重启服务就可生效是 Cluster 的特性之一,其丰富的扩展还可以协助完成优雅停机、重启、杀进程、监控网站状况等操作。昨天我还尝试使用了另一个插件 cluster-live,它可以进行实时管理与统计并通过 canvas 绘制动态图表。下面这张是在我本地测试时的截图,小清新风格:

nodecat with cluster live

代码部署

平时工作中规定使用 SVN 和 Mercurial 来对代码进行版本控制,但我更喜欢 Git,个人的小项目也都放在自己的 Git 仓库中,NodeCat 也不例外。除了代码管理,还可以使用 Git 的 hook 协助完成远程 checkout 实现网站的自动部署。如果部署后网站没有变化,可以检查下是否使用了 Connect 的 staticCache 或 nginx(如果用到了的话)的某些配置导致静态文件被缓存住了需要清理,还有可能是自定义了 Cluster 的 reload 方法,但修改的 node.js 文件或目录没有加到其配置中。

暂时想到这么多,随着后续开发的进行肯定会有更多东西需要记录,嗯。

Comments...

  • mk2
    样式很美。。。。我喜欢。。。
  • Kiwi
    恭喜上线,很赞!
  • Jason Lee
    这个插图很赞啊!
  • goddyzhao
    非常清爽,就喜欢这种简单的风格!
    BTW,插图是自己画的吗?如果不是,很想知道哪里找的?
  • mockee
    @goddyzhao
    来自:http://storify.com/nw/nodejs-logo
    点击插画可以直接到达
  • wellee
    很期待你的github上的blog代码。
  • 三桂
    很赞,同楼上期待ing...

New Comment

Comments are parsed with Markdown