mongoose 4.0 发布之前,model.save
是不会返回 promise
的,因此,对其或在其 callback 内 yield
都不行,虽然有很多种解决方法,但是感觉,适合于过渡的方法是使用 model.create
来替代。该方法在 3.8.x 中已经可以返回 promise
,具体方法参见http://mongoosejs.com/docs/api.html#model_Model.create
How to start pm2 server in a docker container
直接在 docker 的 container 中运行 PM2 可能会遇到以下问题:1
PM2 dameon PID = 1
0 with /.pm2/pm2.log file not found
这是因为 /.pm2
目录不存在造成的。可能很多人此时会想到去创建该目录,事实上是不必要的,比较好的做法是使用官方提供的 dump
命令。
运行 PM2 的 Dockerfile ,可以参考:
1 | FROM node:0.11.13 |
搞定。
NPM and node-gyp
pm2 中会使用到 fsevents,node-gyp rebuild fsevents 时,会遇到下面这样的错误:1
user "root" does not have permission to access the dev dir
网上找到的讨论,说 node-gyp 不能用在 sudo 下,由于 Mac 上 pkg 安装的 node 需要 sudo 执行 npm,所以这样:1
2sudo chown -R `whoami` ~/.npm
sudo chown -R `whoami` /usr/local/lib/node_modules
之后就不再需要 sudo 安装 npm 了。
重新安装 pm2 ,到了 fsevents 时, node-gyp rebuild 能运行了,但到了一半,错误变成这样:1
2
3
4
5
6
7
8make: *** [Release/obj.target/fse/fsevents.o] Error 1
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack at ChildProcess.onExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:267:23)
gyp ERR! stack at ChildProcess.EventEmitter.emit (events.js:110:17)
gyp ERR! stack at Process.ChildProcess._handle.onexit (child_process.js:1046:12)
gyp ERR! System Darwin 13.3.0
gyp ERR! command "node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
但是安装是成功的…… pm2 可以正常使用。
为探究竟,费尽半天辛苦,发现原来是 node 0.11.13 的问题……可以看这里https://github.com/pipobscure/fsevents/pull/26
无言以对。
Make forever for node.js as a Linux service
由于个人比较喜欢在 Linux 上使用 service ,所以才有了以下的折腾。
forever 与 supervisor 的选择
其实,在本地开发还是比较喜欢用 supervisor 的,因为命令简单。考虑到服务器上可能会运行多个 node server 实例,因此选择 forever 来做 node 服务的管理。
了解 forever ,戳 https://github.com/nodejitsu/forever
了解 supervisor,戳https://github.com/isaacs/node-supervisor
Step 1
安装 forever
1 | sudo npm install -g forever |
(这货依赖不是一般多……)
安装成功后可以看到安装路径,记下待用。以下是我的服务器环境的返回:1
/usr/local/lib/node_modules
注意:在 CentOS 上安装 nodejs / npm 的方式不同,会得到不同的安装路径:make install 一般在 /usr/local/lib/node_modules;yum install 一般在 /usr/lib/node_modules 。
Step 2
创建启动文件
1 | sudo vim /etc/init.d/the_name_you_want |
启动文件内容
1 | #!/bin/bash |
345 是启动级别,99 是启动优先级(序号),01 是关闭优先级(序号)。如果有其他服务依赖于 forever 服务,可以提高优先级,以确保其他服务启动时其可用。
继续:
1 | DEAMON="/your/server/config/file/path" |
DEAMON 处给出配置文件地址,其中文件内容每行为一项任务,每项形如:
name app.js path/of/app/js两处“xxx”是占位符,稍后替换用;
注意右边是值在引号内!!不然 forever 可能会喊它要 string,尤其是 LOG 地址。在 yum 安装环境中遇到过,make 安装环境未重现。
继续
1 | export PATH=$PATH:/usr/local/bin |
注入环境变量,不然找不到 node 或者 -g 安装的包。上面已经提过,地址根据安装方法不同不一致,区别在于有没有 local 这一级,自己根据实际情况定夺。
继续,start 命令:
1 | case "$1" in |
详细说明一下:
- “Name Main Path” 对应配置文件中的内容格式,
- start 后不跟参数,则启动所有配置项;跟上配置项 Name 做参数则仅启动该项
- 已经启动的返回友好提示信息
关于 forever 的说明:
- -w 监视文件的变更,一旦有变更,forever 会重启关联 server
- -a 声明追加内容到已有日志文件 (没有 -a 重启时会直接报错并中断…………谁会每次重新启动来关心 log 日志?!为什么 -a 不是默认值!!!)
- -l 指定日志文件地址
- -m 最大运行次数
- —watchDirectory 监视文件的目录 path
- —pidFile 字面意思
list 命令用来查看正在运行的进程,其他命令顾名思义了。
继续,stop 命令:
1 | stop) |
说明
- 不需要第二参数
- 显示正在运行的任务列表,输入 uid 号以停止相应任务
继续,stopall、restartall 命令:
1 | stopall) |
很简单,不错解释了。
继续,reload|restart 命令:
1 | reload|restart) |
用法说明
- 必须使用第二参数:任务项的 Name
- reload、restart 互为别名
继续,获取运行任务列表:
1 | list) |
没有命令时:1
2
3
4
5
6 *)
echo "Usage: /etc/init.d/noded {start|stop|restart|reload|stopall|restartall|list}"
exit 1
;;
esac
exit 0
到这里,启动文件创建完成了,可以 :wq 了。
Step 3
确认启动文件的一些属性
1 | sudo chown root:root /etc/init.d/the_name_you_want |
启动服务
1 | sudo chkconfig --add the_name_you_want |
基本完成
可以通过如下命令来控制服务了1
2
3
4sudo service the_name_you_want start
sudo service the_name_you_want restart
sudo service the_name_you_want stop
sudo service the_name_you_want list
其他问题
添加忽略监控文件的配置文件1
touch /your/watch/path/.foreverignore
如果没有 .foreverignore 会报一个 error,如下:1
2error: Could not read .foreverignore file.
error: ENOENT, open '/your/watch/path/.foreverignore'
但是你的 app.js 是会正常执行的,应该说一切正常。
而,如果没有 —watchDirectory 参数配置,你仍然会得到一个 error,如下:1
2
3
4
5
6
7error: Could not read .foreverignore file.
error: ENOENT, open '/.foreverignore'
/usr/local/lib/node_modules/forever/node_modules/forever-monitor/node_modules/watch/main.js:63
if (err) throw err;
^
Error: ENOTDIR, readdir '/proc/1/fd/5'
此时,你的 app.js 依然欢快运行,但是你执行 list 命令去看它的时候,它……1
2sudo /etc/init.d/noded list
info: No forever processes running
-w 和 —watchDirectory 要一起出现,就够了,-w 不能直接接收值也忍了,不设置后者直接出现这种奇异现象,真是……(此处省略10000字!!)
End
在 Mac 上用 ImageMagick 转换 SVG
使用 ImageMagick
ImageMagick 是老牌的图片解决方案了,这次在 node.js 项目里本想用它的一个演进项目 GraphicsMagick,只可惜,在同样使用 gm 这个 npm 的情况下,GraphicsMagick 对中文的支持太不好了,于是在折腾一番后,还是选择放弃尝试新的事物,转为继续使用各方面技术支持更广泛的老大哥 ImageMagick。
我的需求是需要将 SVG 转为 Raster Graphics (位图),例如将如下 SVG 转为 JPG:
http://www.w3.org/TR/2009/WD-SVGCompositing-20090430/examples/over.svg
结果转换成 JPG 后,图片变成:
内容细节严重丢失,内嵌图片不见了。
经过各种参数调整,可问题依然。此时,查看官方文档发现一些问题:
ImageMagick 对 SVG 支持三种格式:MSVG, SVG, SVGZ。MSVG 为 ImageMagick 的内建库;SVG 和 SVGZ 则依赖其他一些库,比如,官方推荐的 RSVG。
于是用命令查看对 SVG 的支持情况:1
convert -list format | grep SVG
返回1
2
3MSVG SVG rw+ ImageMagick's own SVG internal renderer
SVG SVG rw+ Scalable Vector Graphics (XML 2.7.8)
SVGZ SVG rw+ Compressed Scalable Vector Graphics (XML 2.7.8)
XML?!坑爹啊!果然不是 RSVG 库,还不是得很奇葩……
好吧!装库!
一堆问题
先安装 librsvg 库,在 Mac 下我目前采用 brew 解决包管理,所以:1
brew install librsvg
结果报错,如下:1
2
3
4
5
6
7
8
9
10
11
12
13librsvg: Unsatisfied dependency: XQuartz
Homebrew does not package XQuartz. Installers may be found at:
https://xquartz.macosforge.org
cairo: Unsatisfied dependency: XQuartz
Homebrew does not package XQuartz. Installers may be found at:
https://xquartz.macosforge.org
pango: Unsatisfied dependency: XQuartz
Homebrew does not package XQuartz. Installers may be found at:
https://xquartz.macosforge.org
gtk+: Unsatisfied dependency: XQuartz 2.3.6
Homebrew does not package XQuartz. Installers may be found at:
https://xquartz.macosforge.org
Error: An unsatisfied requirement failed this build.
这才发现,原来 Mac 10.8 上没有 X11,好吧,安装一个,Apple 官方推荐的下载地址:
是 dmg 文件,如何安装就是不用说了。XQuartz 安装完之后,再次运行安装 librsvg 的命令就势如破竹了。
librsvg 安装好之后需要重新安装 ImageMagick,网上找到 —use-rsvg 这个参数,但结果是又一次坑爹了。一阵安装编译等待之后,依旧显示 (XML 2.7.8)!!!什么情况呢?重新进入无尽的搜索,最后是从 brew 源代码里找到当前 brew 版本中的正确参数 —with-librsvg。源码见:
https://github.com/mxcl/homebrew/blob/master/Library/Formula/imagemagick.rb
结果执行如下命令就成功了…1
brew reinstall imagemagick --with-librsvg --with-webp
检验一下:1
2
3
4convert -list format | grep SVG
MSVG SVG rw+ ImageMagick's own SVG internal renderer
SVG SVG rw+ Scalable Vector Graphics (RSVG 2.36.3)
SVGZ SVG rw+ Compressed Scalable Vector Graphics (RSVG 2.36.3)
另外,如果此时还有问题,识别不到 RSVG 的话,请添加以下环境变量:
1 export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/opt/X11/lib/pkgconfig
而,如果是使用 port 的,命令如下:1
port install imagemagick +rsvg
按理到这里为止,ImageMagick 已经可以正常通过 librsvg 来渲染 SVG 格式的图片了,但是在我的 Mac 环境里始终存在丢失 xlink 进来的外链 image 的问题。于是又进入了茫然……
不要妄自菲薄
为了解决 node.js 下使用 ImageMagick 来处理图片的问题,前后折腾了几个小时,当所有已知问题都解决的时候一切回到原点。很不甘,是哪儿弄错了?于是又折腾了几个小时……
由于使用场景和使用的细节已然是少数需求了,国内相关文字几乎为零,英文站点上的内容也极少。stackoverflow 上尽是提问的,回答了了,更多的是根本没有回答。
突然,因为连续几篇文字提到了 ImageMagick 的不同版本对于类似的 convert 时出现的 bug,于是我怀疑是我环境里 ImageMagick 版本的问题。立刻在服务器环境中进行了测试(ImageMagick 6.5.4-7 with RSVG 2.26.0 on CentOS)果然,一切正常了!OMG!
出错的是 ImageMagick 6.8.6-3 with RSVG 2.36.3 版本,貌似是 RSVG 的错误,具体哪里出错暂时无力再去验证。具体错误表现是:
- xlink:href=”abc.png” 同目录丢失
- xlink:href=”./abc.png” 相对路径丢失
- xlink:href=”/path/to/abc.png” 绝对路径丢失
- xlink:href=”data:image/png;base64,…=” 内嵌 Base64 格式正常
最后得到正确的图像输出:
此时,我想说,虽然常提“不要妄自菲薄”,但是做到还是挺难的。质疑问题需要的是锲而不舍的精神和精湛的技艺,我辈还不具备两者,还没勇气去怀疑知名软件库,真是还需努力!