Mockee Nodes

"Happiness only real when shared."

Mailr Released



距离上一次和几个要好的朋友一起利用闲暇时间鼓捣点儿有意思的东西已经过去六七年的时间了,我很怀念那些时光,那些人。旧事不再重提,如今和 @J, @Tifa 一起组队做 app 确实也让我找回了几分当年的感觉,只是时过境迁,少了学生时代的自在和洒脱。

尽管如此,我们依然乐在其中。一年的时间不算短,但真正能开工的时间少之又少,赶项目、必要的充电、交流分享、翻译阅读、陪伴家人... 我们通常会在 skype 上牢骚几句,然后默契地停工一段时间。这一年中,Trello 是用得最得手的协作工具,我们在上面跟踪进度、共享资源、记录灵感。日子就这样一天天过去。但项目总还是要上线,为了给付出一个交代,我们以一年整为底线设定一个了 Deadline。收尾的过程紧张刺激,所幸如期上架。

@J 是 Mailr 的核心力量,没有 J 就没有 Mailr,其学习能力也真心令我叹服,作为码农,和强手作伴是人生一大快事。@Tifa 则是设计方面的最佳吵伴;) 其设计的整个迭代过程只能用惊喜不断来形容。OK, Special Thanks 到此,鸡皮疙瘩一地了。我想说的其实是,有这样的队友,无论平日工作还是业余合作都无比舒心,我很幸运。

Mailr 可能是第一个专门为豆邮设计的手机客户端,你可以到 http://appfn.com/mailr 下载试用。我们用心在做,但同时更期待大家来找茬儿,帮助我们改进:Mailr 豆瓣小组 VIP 入口

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 文件或目录没有加到其配置中。

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

Fresh start with NodeCat

这是我的新 blog,Codename: NodeCat —— 基于 node.js,对猫偏爱有加,作为开发代号,仅此而已。同时这也是我第一次尝试使用 node 编写 web 应用,其中用到的开源产品多半出自高质高产的 TJ 之手:express-*, cluster-*, jade,后来干脆用 stylus + nib 换掉了 less。除了探索技术可行性外,创建 NodeCat 更多是为了满足个人需求,所以从技术角度而言,目前这个版本并没有太多值得分享的内容。

mockee.com 已有近两年的时间不曾指向过任何有意义的页面了,今天将它重新解析到我那台已连续工作了四年代号为 Kaavie 的服务器上,其实并无特别的打算,只是觉得,应该像从前那样,在一个有归属感的小地方,安静地写下点什么。习惯了用 Ohlife 给自己发私信,第 289 天。希望在这里也可以坚持下去。

清晨

ps, NodeCat 的大部分代码是在这个简单舒服的角落完成的。
现在我同样坐在这里,写下第一篇笔记。