译接上文
支持多线程?怎么做到的?
这个新特性仍处在试验阶段 —— 还不能在生产环境中使用。但我们还是可以随意玩玩的。那从哪开始呢?
从 Node 12 开始及至更高版本中,我们不再需要使用特定的特性标志 –experimental-worker。 Worker 将是默认激活的!
node index.js
现在我们可以充分利用 worker_threads 模块。让我们先写一个简单的带有两个方法的 HTTP 服务器:
- GET /hello(返回带有“Hello World”信息的 JSON 对象)。
- GET /compute(使用一个同步方法重复加载一个大 JSON 文件)。
const express = require('express');
const fs = require('fs');
const app = express();
app.get('/hello', (req, res) => {
res.json({
message: 'Hello world!'
})
});
app.get('/compute', (req, res) => {
let json = {};
for (let i=0;i<100;i++) {
json = JSON.parse(fs.readFileSync('./big-file.json', 'utf8'));
}
json.data.sort((a, b) => a.index - b.index);
res.json({
message: 'done'
})
});
app.listen(3000);
这段代码的运行结果很容易预测。当 GET /compute 和 /hello 被同时调用,我们必须等到 compute 调用完成才能从 hello 得到响应。事件循环被阻塞,直到文件加载完成。
让我们用多线程优化一下吧!
const express = require('express');
const fs = require('fs');
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
if (isMainThread) {
console.log("Spawn http server");
const app = express();
app.get('/hello', (req, res) => {
res.json({
message: 'Hello world!'
})
});
app.get('/compute', (req, res) => {
const worker = new Worker(__filename, {workerData: null});
worker.on('message', (msg) => {
res.json({
message: 'done'
});
})
worker.on('error', console.error);
worker.on('exit', (code) => {
if(code != 0)
console.error(new Error(`Worker stopped with exit code ${code}`))
});
});
app.listen(3000);
} else {
let json = {};
for (let i=0;i<100;i++) {
json = JSON.parse(fs.readFileSync('./big-file.json', 'utf8'));
}
json.data.sort((a, b) => a.index - b.index);
parentPort.postMessage({});
}
很明显,这种语法和我们所知道的 Node.js 集群扩展非常相似。但从这儿就开始变得有趣起来了。
你可以试着同时调用两个路径。注意到什么了吗?。没错,事件循环不再被阻塞,这样我们就能在文件加载期间调用 /hello 了。
现在,这就是我们都翘首以盼的东西!剩下的就是等待稳定版本的 API 出炉了。
渴望更多的 Node.js 新特性?这个 N-API 能够构建 C/C++ 模块!
Node.js 的原生运行速度正是我们青睐这个技术的原因之一。Worker threads 将会更进一步地提升 Node.js 的速度。但仅仅是这样就够了吗?
Node.js 是一种基于 C 语言的技术。当然了,我们把 JavaScript 当作一个主要编程语言来使用。但如果我们能用 C 语言做更加复杂的计算呢?
Node.js 10 版本给我们带来了 N-API。这是一个标准化的 API,适用于原生模块,让用 C/C++ 甚至是 Rust 语言构建模块成为可能。听起来很棒,对吧?
用 C/C++ 构建 Node.js 原生模块变得更加容易。
下面是一个很简单的原生模块示例:
#include <napi.h>
#include <math.h>
namespace helloworld {
Napi::Value Method(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
return Napi::String::New(env, "hello world");
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(Napi::String::New(env, "hello"),
Napi::Function::New(env, Method));
return exports;
}
NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
}
如果你有 C++ 的基础,写一个自定义模块肯定不费吹灰之力。你只需记得在模块结尾将 C++ 的类型转化为 Node.js 类型即可。
接下来我们需要绑定(binding):
{
"targets": [
{
"target_name": "helloworld",
"sources": [ "hello-world.cpp"],
"include_dirs": ["<!@(node -p \"require('node-addon-api').include\")"],
"dependencies": ["<!(node -p \"require('node-addon-api').gyp\")"],
"defines": [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ]
}
]
}
这个简单的配置让我们能够构建 *.cpp 文件,以便于后续在 Node.js 应用中使用。
在用于 JavaScript 代码之前,我们必须进行构建并配置 package.json 文件来查找 gyp 文件(绑定文件)。
{
"name": "n-api-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"install": "node-gyp rebuild"
},
"gypfile": true,
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"node-addon-api": "^1.5.0",
"node-gyp": "^3.8.0"
}
}
当模块准备就绪,我们就可以用 node-gyp rebuild 命令进行构建并导入到 JavaScript 代码中。用法和其他流行的模块的用法一样!
const addon = require('./build/Release/helloworld.node');
console.log(addon.hello());
N-API 以及 Worker threads 赋予我们功能强大的工具,帮我们构建高性能的应用。不用说 API 或仪表板 —— 即使是复杂的数据处理或者机器学习系统都将垂手可得。多么棒啊!
Node.js 会全面支持 HTTP/2 吗?当然了!何乐不为?
我们能够计算得更快。我们能进行分布式计算。那么资源和页面服务方面表现如何?
多年来,我们一直都卡在优秀却陈旧的 http 模块和 HTTP/1.1 上没有进步。随着服务器要提供的资源越来越多,我们越来越受制于加载所花费的时间。针对每个服务器或代理服务器,各个浏览器都有个并发持久连接数上限,特别是 HTTP/1.1 协议下。有了对 HTTP/2 的支持,我们就可以和这个问题吻别了。
那我们该从哪里下手呢?你是否还记得网上每个教程中都会出现的这个 Node.js 基础示例?对,就是这个:
const http = require('http');
http.createServer(function (req, res) {
res.write('Hello World!');
res.end();
}).listen(3000);
在 Node.js 10 版本中,有一个崭新的 http2 模块可以让我们使用 HTTP/2.0!可算是迎来了 HTTP/2.0!
const http = require('http2');
const fs = require('fs');
const options = {
key: fs.readFileSync('example.key'),
cert: fs.readFileSync('example.crt')
};
http.createSecureServer(options, function (req, res) {
res.write('Hello World!');
res.end();
}).listen(3000);
我们心心念念的就是 Node.js 10 版本中全面支持的 HTTP/2。
这些新特性会让 Node.js 的未来一片光明
Node.js 的新特性为我们的技术生态注入了新鲜血液。它们给 Node.js 插上翅膀,让它飞向新的天地。你想到过这个技术有一天会用于图像识别或者数据科学吗?我也从来没有想到过。
这个版本的 Node.js 还带来了更多的人们期盼已久的特性,例如对 ES 模块的支持(虽然仍出于试验阶段);又如 fs 方法的更新,终于让我们能够脱离回调地狱、拥抱 Promise 天堂了。
在下面的折线图中,我们可以发现,经过历年的增长,Node.js 的人气在 2017 年早期达到了巅峰。这并不是增长开始缓慢的迹象,而是标志着这个技术的成熟。
不论如何,我能够清晰地看出,所有这些新的改进和 Node.js 区块链应用(基于 truffle.js 框架)的走红,或可进一步推动 Node.js 的发展,让 Node.js 在新型的项目、角色和环境中梅开二度。
TSH(The Software House)Node.js 团队非常期待 2020 年的到来!