我使用Node.js创建了一个图像生成服务器

概述

    1. 如果要在Node.js中生成图像,有一种方式是使用服务器端的Canvas

 

    1. 使用Hapi.js创建路径很方便

 

    将二进制数据保存到Redis时需要进行选项设置

事物的开始

大家如何以眼睛确认大量的数据呢?生成表格然后目测吗?嗯,这个方法不错。但并不是所有的用户都擅长目测搜索。当数据达到一定量时,图表和图片往往更易于理解。这次我尝试了使用nodejs生成图片,并结合hapi框架和redis缓存。

生成图像的服务器

最初我考虑在客户端使用canvas。虽然也想过在浏览器上控制显示,但由于操作过多而被否定了。然而,如果要显示所有目标图像,每个页面大约有100至300张图片,全部在浏览器中显示是不现实的,所以我决定在服务器上生成图像并采取一种部分缓存的方式。幸运的是,存在canvas模块,可以几乎与在客户端上写代码一样。

安装画布

由于Canvas是一个本机模块,因此在进行构建之前需要安装库和VS。请参考官方说明中的链接。本次教程以Windows为例进行。

    1. 安装Python2.7版本

 

    1. 安装Visual Studio 2013

 

    1. 安装GTK 2

 

    1. 将zip文件解压到C:\GTK等合适的文件夹

 

    1. 设置路径

 

    1. 安装libjpeg-turbo

 

    确认成功安装npm install canvas

如果操作系统和node.js(可能确切地说是V8)的版本一致,可以重复使用已构建的二进制模块,因此在多台机器上进行部署时,可能更方便的方法是在一台机器上进行构建,然后将其复制到其他机器上。

使用Canvas

可以通过生成上下文并进行绘图的方式来使用,与浏览器的使用方式相同,然后使用buffer/DataURL将绘图保存为png或jpeg格式。

MethoddescriptionpngStream()png形式でStream書き出しjpegStream(opt)jpeg形式でStream書き出しtoBuffer()png形式で同期書き出しtoBuffer(callback(err, buf))png形式で非同期書き出しtoDataURL([‘image/png’])DataURLで同期書き出しtoDataURL(‘image/png’, [opt], callback(err, img))DataURLで非同期書き出し

绘画范例 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);
})

test.png

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中一段固定时间内。

system.dot.png

当谈到使用方法时,遇到了需要考虑其他方面的困难。

    1. 写入的二进制文件已损坏

 

    如果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
bannerAds