如何在 Sapper 上使用 worker_threads?
How to use worker_threads on Sapper?
我正在尝试使用 nodejs worker_threads 在 Sapper 上启动一个线程,但看起来它也在尝试启动另一个 Polka 实例。
你可以在这个 repo 上找到我的代码,但它基本上有一个到 http://localhost:3000/worker
的路由,可以获取服务器端 worker\run.js
。它调用具有 worker_threads 逻辑的 worker.js
。
run.js
在 worker.js
上调用 main() 函数并创建一个新线程,该线程在工作线程实例中重新加载自身,这次匹配 if (!isMainThread) {...}
条件,执行 console.logs (我尝试了其他用途并出现了同样的问题)。
/* worker.js */
import { Worker, isMainThread } from 'worker_threads';
export const main = () => new Promise(function (resolve, reject) {
if (!isMainThread) return;
// This re-loads the current file inside a Worker instance.
const wk = new Worker(__filename);
wk.on("online", () => console.log("Worker UP"));
wk.on("message", (msg) => {
console.log("message ~>", msg);
resolve(msg);
});
wk.on("exit", (code) => console.warn("exit ~>", code));
wk.on("error", (err) => {
console.error("error ~>", err);
reject(err);
});
})
if (!isMainThread) {
console.log('Inside Worker!');
console.log(isMainThread);
}
但是returns出现如下错误(注意console.log(isMainThread)
没有执行):
> sapper dev
✔ client (3.3s)
✔ server (3.3s)
> Listening on http://localhost:3000
✔ service worker (84ms)
Worker UP
Inside Worker!
error ~> Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2] (net.js:1313:16)
at listenInCluster (net.js:1361:12)
at Server.listen (net.js:1449:7)
at Polka.listen (/mnt/disk2/adr/lab2/pollers/worker/node_modules/.pnpm/registry.npmjs.org/polka/1.0.0-next.11/node_modules/polka/build.js:59:22)
at Object.<anonymous> (/mnt/disk2/adr/lab2/pollers/worker/__sapper__/dev/server/server.js:3119:3)
at Module._compile (internal/modules/cjs/loader.js:1133:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1153:10)
at Module.load (internal/modules/cjs/loader.js:977:32)
at Function.Module._load (internal/modules/cjs/loader.js:877:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12) {
code: 'EADDRINUSE',
errno: 'EADDRINUSE',
syscall: 'listen',
address: '::',
port: 3000
}
exit ~> 1
编辑:
分析汇总的输出后:
/* __sapper__/dev/server/server.js */
'use strict';
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var sirv = _interopDefault(require('sirv'));
var polka = _interopDefault(require('polka'));
var compression = _interopDefault(require('compression'));
var fs = _interopDefault(require('fs'));
var path = _interopDefault(require('path'));
var worker_threads = require('worker_threads');
var Stream = _interopDefault(require('stream'));
var http = _interopDefault(require('http'));
var Url = _interopDefault(require('url'));
var https = _interopDefault(require('https'));
var zlib = _interopDefault(require('zlib'));
if (!worker_threads.isMainThread) {
console.log('Inside Worker!');
console.log(worker_threads.isMainThread);
}
var route_0 = /*#__PURE__*/Object.freeze({
__proto__: null
});
const main = () => new Promise(function (resolve, reject) {
if (!worker_threads.isMainThread) return;
// This re-loads the current file inside a Worker instance.
const wk = new worker_threads.Worker(path.join(__dirname, "threads.js"));
wk.on("online", () => console.log("Worker UP"));
wk.on("message", (msg) => {
console.log("message ~>", msg);
resolve(msg);
});
wk.on("exit", (code) => console.warn("exit ~>", code));
wk.on("error", (err) => {
console.error("error ~>", err);
reject(err);
});
});
...
Rollup 将所有文件捆绑到一个文件中,因此 Worker(__filename)
在一个线程中再次加载它。
所以我把线程中要加载的文件放到一个单独的文件中:
/* thread.js */
import { isMainThread } from 'worker_threads';
if (!isMainThread) {
console.log('Inside Worker!');
console.log(isMainThread);
}
并将 Worker 实例的构造更改为:
const wk = new Worker(path.join(__dirname, "thread.js"));
现在错误已更改为:
"Cannot find module '/mnt/disk2/adr/lab2/pollers/worker/__sapper__/dev/server/thread.js'"
问题现在归结为汇总配置,以保持 thread.js 与 server.js 分开。
Sapper 以自己的方式处理汇总配置,否则我可以这样设置 rollup.config.js
:
export default [
{
input: "server.js",
output: {
file: "dist/server.js",
format: "cjs",
},
},
{
input: "thread.js",
output: {
file: "dist/thread.js",
format: "cjs",
},
},
];
现在的问题是:有没有办法在不修改 Sapper 代码的情况下做到这一点?
为了解决这个问题,至少在 sapper v0.27.0 上,我在 rollup.config.js
:
的 server.input 中添加了我想作为线程执行的文件
export default {
server: {
input: { ...config.server.input(), threads: "src/routes/worker/threads.js" },
output: config.server.output(),
}
}
config.server.intput()
returns { server: `${__chunk_3.src}/server.js` }
,然后添加 threads: "path_to_thread_file"
就可以了。
我正在尝试使用 nodejs worker_threads 在 Sapper 上启动一个线程,但看起来它也在尝试启动另一个 Polka 实例。
你可以在这个 repo 上找到我的代码,但它基本上有一个到 http://localhost:3000/worker
的路由,可以获取服务器端 worker\run.js
。它调用具有 worker_threads 逻辑的 worker.js
。
run.js
在 worker.js
上调用 main() 函数并创建一个新线程,该线程在工作线程实例中重新加载自身,这次匹配 if (!isMainThread) {...}
条件,执行 console.logs (我尝试了其他用途并出现了同样的问题)。
/* worker.js */
import { Worker, isMainThread } from 'worker_threads';
export const main = () => new Promise(function (resolve, reject) {
if (!isMainThread) return;
// This re-loads the current file inside a Worker instance.
const wk = new Worker(__filename);
wk.on("online", () => console.log("Worker UP"));
wk.on("message", (msg) => {
console.log("message ~>", msg);
resolve(msg);
});
wk.on("exit", (code) => console.warn("exit ~>", code));
wk.on("error", (err) => {
console.error("error ~>", err);
reject(err);
});
})
if (!isMainThread) {
console.log('Inside Worker!');
console.log(isMainThread);
}
但是returns出现如下错误(注意console.log(isMainThread)
没有执行):
> sapper dev
✔ client (3.3s)
✔ server (3.3s)
> Listening on http://localhost:3000
✔ service worker (84ms)
Worker UP
Inside Worker!
error ~> Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2] (net.js:1313:16)
at listenInCluster (net.js:1361:12)
at Server.listen (net.js:1449:7)
at Polka.listen (/mnt/disk2/adr/lab2/pollers/worker/node_modules/.pnpm/registry.npmjs.org/polka/1.0.0-next.11/node_modules/polka/build.js:59:22)
at Object.<anonymous> (/mnt/disk2/adr/lab2/pollers/worker/__sapper__/dev/server/server.js:3119:3)
at Module._compile (internal/modules/cjs/loader.js:1133:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1153:10)
at Module.load (internal/modules/cjs/loader.js:977:32)
at Function.Module._load (internal/modules/cjs/loader.js:877:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12) {
code: 'EADDRINUSE',
errno: 'EADDRINUSE',
syscall: 'listen',
address: '::',
port: 3000
}
exit ~> 1
编辑:
分析汇总的输出后:
/* __sapper__/dev/server/server.js */
'use strict';
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var sirv = _interopDefault(require('sirv'));
var polka = _interopDefault(require('polka'));
var compression = _interopDefault(require('compression'));
var fs = _interopDefault(require('fs'));
var path = _interopDefault(require('path'));
var worker_threads = require('worker_threads');
var Stream = _interopDefault(require('stream'));
var http = _interopDefault(require('http'));
var Url = _interopDefault(require('url'));
var https = _interopDefault(require('https'));
var zlib = _interopDefault(require('zlib'));
if (!worker_threads.isMainThread) {
console.log('Inside Worker!');
console.log(worker_threads.isMainThread);
}
var route_0 = /*#__PURE__*/Object.freeze({
__proto__: null
});
const main = () => new Promise(function (resolve, reject) {
if (!worker_threads.isMainThread) return;
// This re-loads the current file inside a Worker instance.
const wk = new worker_threads.Worker(path.join(__dirname, "threads.js"));
wk.on("online", () => console.log("Worker UP"));
wk.on("message", (msg) => {
console.log("message ~>", msg);
resolve(msg);
});
wk.on("exit", (code) => console.warn("exit ~>", code));
wk.on("error", (err) => {
console.error("error ~>", err);
reject(err);
});
});
...
Rollup 将所有文件捆绑到一个文件中,因此 Worker(__filename)
在一个线程中再次加载它。
所以我把线程中要加载的文件放到一个单独的文件中:
/* thread.js */
import { isMainThread } from 'worker_threads';
if (!isMainThread) {
console.log('Inside Worker!');
console.log(isMainThread);
}
并将 Worker 实例的构造更改为:
const wk = new Worker(path.join(__dirname, "thread.js"));
现在错误已更改为:
"Cannot find module '/mnt/disk2/adr/lab2/pollers/worker/__sapper__/dev/server/thread.js'"
问题现在归结为汇总配置,以保持 thread.js 与 server.js 分开。
Sapper 以自己的方式处理汇总配置,否则我可以这样设置 rollup.config.js
:
export default [
{
input: "server.js",
output: {
file: "dist/server.js",
format: "cjs",
},
},
{
input: "thread.js",
output: {
file: "dist/thread.js",
format: "cjs",
},
},
];
现在的问题是:有没有办法在不修改 Sapper 代码的情况下做到这一点?
为了解决这个问题,至少在 sapper v0.27.0 上,我在 rollup.config.js
:
export default {
server: {
input: { ...config.server.input(), threads: "src/routes/worker/threads.js" },
output: config.server.output(),
}
}
config.server.intput()
returns { server: `${__chunk_3.src}/server.js` }
,然后添加 threads: "path_to_thread_file"
就可以了。