我使用Node.js创建了一个图像生成服务器
概述
-
- 如果要在Node.js中生成图像,有一种方式是使用服务器端的Canvas
-
- 使用Hapi.js创建路径很方便
- 将二进制数据保存到Redis时需要进行选项设置
事物的开始
大家如何以眼睛确认大量的数据呢?生成表格然后目测吗?嗯,这个方法不错。但并不是所有的用户都擅长目测搜索。当数据达到一定量时,图表和图片往往更易于理解。这次我尝试了使用nodejs生成图片,并结合hapi框架和redis缓存。
生成图像的服务器
最初我考虑在客户端使用canvas。虽然也想过在浏览器上控制显示,但由于操作过多而被否定了。然而,如果要显示所有目标图像,每个页面大约有100至300张图片,全部在浏览器中显示是不现实的,所以我决定在服务器上生成图像并采取一种部分缓存的方式。幸运的是,存在canvas模块,可以几乎与在客户端上写代码一样。
安装画布
由于Canvas是一个本机模块,因此在进行构建之前需要安装库和VS。请参考官方说明中的链接。本次教程以Windows为例进行。
-
- 安装Python2.7版本
-
- 安装Visual Studio 2013
-
- 安装GTK 2
-
- 将zip文件解压到C:\GTK等合适的文件夹
-
- 设置路径
-
- 安装libjpeg-turbo
- 确认成功安装npm install canvas
如果操作系统和node.js(可能确切地说是V8)的版本一致,可以重复使用已构建的二进制模块,因此在多台机器上进行部署时,可能更方便的方法是在一台机器上进行构建,然后将其复制到其他机器上。
使用Canvas
可以通过生成上下文并进行绘图的方式来使用,与浏览器的使用方式相同,然后使用buffer/DataURL将绘图保存为png或jpeg格式。
绘画范例 lì)
const Canvas = require('canvas');
// Context生成
let image = Canvas.image;
let canvas = new Canvas(200,200);
let ctx = canvas.getContext('2d');
// 文字描画
ctx.font = "30px Lato"
ctx.rotate(.1);
ctx.fillText('Canvas', 50, 100);
// ライン描画
let te = ctx.measureText('Canvas');
ctx.stroke.style = 'rgba(0,0,0,0.7)'
ctx.beginPath();
ctx.lineTo(50,102);
ctx.lineTo(50 + te.width, 102);
ctx.stroke();
// 書き出し
canvas.toBuffer((err, buf)=>{
fs.writeFile("image.png", buf);
})

Hapi.js 哈皮.js
说到HTTP服务器,express非常易于构建,但这次我选择了hapijs。为什么选择hapijs呢?
Hapi.js进行快乐编码 – 第1部分:路由和Swagger插件(由Masato先生分享)
我对hapi-swagger有点兴趣,所以我想了解一下它的具体情况。
- 使用 Joi 进行参数/查询的验证变得轻松
中文:虽然在这方面确实令人愉悦,但是要迁移的话,插件和使用方法都会有很大的变化,需要学习很多设置方法,所以还没有完全掌握,无法谈论。
首先,设置ID和number实现每一个URL生成一张图片。
const lib = require('../lib/image');
server.route({
method: 'GET',
path: '/image/{id}/{num}',
// config にtagsとvalidateを設定。swaggerにも反映される
config: {
tags: ["api", "image"],
validate: {
params: {
id: Joi.string().regex(/^[a-zA-Z0-9]{3,20}$/),
num: Joi.number().integer().min(10).max(1000),
}
}
}
handler: (req, rep)=>{
lib(req.params,(err, buf)~>{
rep(buf).type('image/png');
})
}
});
用 Redis 进行缓存
画像生成服务器已经成功搭建,但在正式环境的实测中,每张生成需要花费大约100-200毫秒的时间,一个页面(约200张)需要10-20秒…这样的性能无法一直使用。我们将把生成服务器并行化,以增加单位时间内的生成数量,并在生成后将结果缓存到Redis中一段固定时间内。

当谈到使用方法时,遇到了需要考虑其他方面的困难。
-
- 写入的二进制文件已损坏
- 如果redis崩溃,App也会崩溃
我只谈论关于对应的处理。我们将使用node redis包。
二进制文件损坏
当我在redis-cli中查看时,它存在,但当我尝试在node.js中读取时,却发现已损坏。实际上,当我查看data.length时,它变得较小。这是前辈们给我留下的记录。
使用 node_redis 无法正确读取二进制数据。
如果要在node redis中读取和写入二进制文件,需要在选项设置中添加”detect_buffers”,然后通过键的缓冲区输入来获取Buffer数据格式。顺便说一下,如果将”return_buffers”设置为true,所有返回的数据都会以二进制形式返回。
const redis = require('redis');
const client = redis.createClient({detect_buffers:true}); // detect_buffesをtrueにする
// client.setex("key", exipre, data) // 文字列や数字などは文字列でよい
client.setex(new Buffer("key"), exipre, data) // keyをBufferに変換
当Redis出现故障时,应用程序也会崩溃。
仅仅创建了一个客户端,当Redis宕机时会一同崩溃。
相比而言,即使较慢但能够生成,也比成为牺牲品要更好,因此要进行相应的处理。
首先是设置一个`Client.on(‘error’,(err))`。如果没有监听器,会抛出异常导致崩溃。
另一个是在进行`get`操作之前,添加一个活动确认,如果已经宕机,则跳过缓存相关的操作。
client.on("error", function (err) {
console.error("Error " + err);
});
if(!client.connected) return fn(cb); // 未接続ならcacheの確認をせずに生成して返すなど
client.get(key,(err, buf)=>{
// hoeghoge
})
最后
hapijs很聪明,听起来不错。由于我还没有完全掌握(尤其是日志方面的处理…),所以还需要一段时间的实验。
我准备了这个存储库和Heroku,所以请参考下面的内容。
这次是我第一次尝试,但Heroku的部署真的很简单啊!
因为找不到立即免费准备redis的方法,所以暂时未应用。
-
- heroku
- Repository