From f69992c26723a8c2fc139fa496a8b0a3bc6fe497 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Fri, 23 Feb 2024 13:50:29 +0800 Subject: [PATCH] feat: set CFORK_WORKER_INDEX env let worker know it's fork index (#116) --- .github/workflows/nodejs.yml | 16 +++++++ .github/workflows/release.yml | 13 ++++++ .gitignore | 1 + History.md => CHANGELOG.md | 0 LICENSE | 21 +++++++++ README.md | 81 ++++++++++----------------------- fixtures/slave.js | 3 ++ fixtures/worker.js | 3 ++ index.js | 20 +++++--- test/cfork.test.js | 42 +++++++++++++++-- test/one_worker_cluster.test.js | 14 +++++- 11 files changed, 145 insertions(+), 69 deletions(-) create mode 100644 .github/workflows/nodejs.yml create mode 100644 .github/workflows/release.yml rename History.md => CHANGELOG.md (100%) create mode 100644 LICENSE diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml new file mode 100644 index 0000000..4b0c095 --- /dev/null +++ b/.github/workflows/nodejs.yml @@ -0,0 +1,16 @@ +name: CI + +on: + push: + branches: [ master ] + + pull_request: + branches: [ master ] + +jobs: + Job: + name: Node.js + uses: node-modules/github-actions/.github/workflows/node-test.yml@master + with: + os: 'ubuntu-latest, macos-latest, windows-latest' + version: '14, 16, 18, 20' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..1c6cbb1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,13 @@ +name: Release + +on: + push: + branches: [ master ] + +jobs: + release: + name: Node.js + uses: node-modules/github-actions/.github/workflows/node-release.yml@master + secrets: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + GIT_TOKEN: ${{ secrets.GIT_TOKEN }} diff --git a/.gitignore b/.gitignore index c0a6490..15719d9 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ results node_modules npm-debug.log coverage/ +package-lock.json diff --git a/History.md b/CHANGELOG.md similarity index 100% rename from History.md rename to CHANGELOG.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b7fb850 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2014 - present node-modules and other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 28aae4b..94d7ec4 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,14 @@ cfork ======= [![NPM version][npm-image]][npm-url] -[![build status][travis-image]][travis-url] +[![Node.js CI](https://github.com/node-modules/cfork/actions/workflows/nodejs.yml/badge.svg)](https://github.com/node-modules/cfork/actions/workflows/nodejs.yml) [![Test coverage][codecov-image]][codecov-url] -[![Known Vulnerabilities][snyk-image]][snyk-url] [![npm download][download-image]][download-url] [npm-image]: https://img.shields.io/npm/v/cfork.svg?style=flat-square [npm-url]: https://npmjs.org/package/cfork -[travis-image]: https://img.shields.io/travis/node-modules/cfork.svg?style=flat-square -[travis-url]: https://travis-ci.org/node-modules/cfork [codecov-image]: https://codecov.io/gh/node-modules/cfork/branch/master/graph/badge.svg [codecov-url]: https://codecov.io/gh/node-modules/cfork -[snyk-image]: https://snyk.io/test/npm/cfork/badge.svg?style=flat-square -[snyk-url]: https://snyk.io/test/npm/cfork [download-image]: https://img.shields.io/npm/dm/cfork.svg?style=flat-square [download-url]: https://npmjs.org/package/cfork @@ -42,32 +37,29 @@ cfork({ exec: '/your/app/worker.js', // slaves: ['/your/app/slave.js'], // count: require('os').cpus().length, -}) -.on('fork', worker => { - console.warn('[%s] [worker:%d] new worker start', Date(), worker.process.pid); -}) -.on('disconnect', worker => { - console.warn('[%s] [master:%s] wroker:%s disconnect, exitedAfterDisconnect: %s, state: %s.', - Date(), process.pid, worker.process.pid, worker.exitedAfterDisconnect, worker.state); -}) -.on('exit', (worker, code, signal) => { - const exitCode = worker.process.exitCode; - const err = new Error(util.format('worker %s died (code: %s, signal: %s, exitedAfterDisconnect: %s, state: %s)', - worker.process.pid, exitCode, signal, worker.exitedAfterDisconnect, worker.state)); - err.name = 'WorkerDiedError'; - console.error('[%s] [master:%s] wroker exit: %s', Date(), process.pid, err.stack); -}) - -// if you do not listen to this event -// cfork will output this message to stderr -.on('unexpectedExit', (worker, code, signal) => { - // logger what you want -}); - -// emit when reach refork times limit -.on('reachReforkLimit', () => { - // do what you want -}); +}).on('fork', worker => { + console.warn('[%s] [worker:%d] new worker start', Date(), worker.process.pid); + }) + .on('disconnect', worker => { + console.warn('[%s] [master:%s] wroker:%s disconnect, exitedAfterDisconnect: %s, state: %s.', + Date(), process.pid, worker.process.pid, worker.exitedAfterDisconnect, worker.state); + }) + .on('exit', (worker, code, signal) => { + const exitCode = worker.process.exitCode; + const err = new Error(util.format('worker %s died (code: %s, signal: %s, exitedAfterDisconnect: %s, state: %s)', + worker.process.pid, exitCode, signal, worker.exitedAfterDisconnect, worker.state)); + err.name = 'WorkerDiedError'; + console.error('[%s] [master:%s] wroker exit: %s', Date(), process.pid, err.stack); + }) + // if you do not listen to this event + // cfork will output this message to stderr + .on('unexpectedExit', (worker, code, signal) => { + // logger what you want + }) + // emit when reach refork times limit + .on('reachReforkLimit', () => { + // do what you want + }); // if you do not listen to this event // cfork will listen it and output the error message to stderr @@ -92,30 +84,7 @@ process.on('uncaughtException', err => { ## License -``` -(The MIT License) - -Copyright (c) 2014 - 2017 node-modules and other contributors - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -``` +[MIT](LICENSE) diff --git a/fixtures/slave.js b/fixtures/slave.js index 15c7352..823b049 100644 --- a/fixtures/slave.js +++ b/fixtures/slave.js @@ -14,6 +14,9 @@ var app = http.createServer(function (req, res) { if (req.url === '/env') { return res.end(process.env.CFORK_ENV_TEST); } + if (req.url === '/worker_index') { + return res.end(`slave worker index: ${process.env.CFORK_SLAVE_WORKER_INDEX}, ${process.env.CFORK_SLAVE_WORKER_COUNT}`); + } res.end(req.method + ' ' + req.url); }).listen(port); diff --git a/fixtures/worker.js b/fixtures/worker.js index 82a3124..8fc59ac 100644 --- a/fixtures/worker.js +++ b/fixtures/worker.js @@ -24,6 +24,9 @@ var app = http.createServer(function (req, res) { if (req.url === '/env') { return res.end(process.env.CFORK_ENV_TEST); } + if (req.url === '/worker_index') { + return res.end(`worker index: ${process.env.CFORK_WORKER_INDEX}, ${process.env.CFORK_WORKER_COUNT}`); + } res.end(req.method + ' ' + req.url); }).listen(port); diff --git a/index.js b/index.js index c3a7d9f..b91e878 100644 --- a/index.js +++ b/index.js @@ -111,8 +111,9 @@ function fork(options) { disconnects[worker.process.pid] = utility.logDate(); if (allow()) { - newWorker = forkWorker(worker._clusterSettings); + newWorker = forkWorker(worker._clusterSettings, worker._clusterWorkerEnv); newWorker._clusterSettings = worker._clusterSettings; + newWorker._clusterWorkerEnv = worker._clusterWorkerEnv; log('[%s] [cfork:master:%s] new worker:%s fork (state: %s)', utility.logDate(), process.pid, newWorker.process.pid, newWorker.state); } else { @@ -141,8 +142,9 @@ function fork(options) { unexpectedCount++; if (allow()) { - newWorker = forkWorker(worker._clusterSettings); + newWorker = forkWorker(worker._clusterSettings, worker._clusterWorkerEnv); newWorker._clusterSettings = worker._clusterSettings; + newWorker._clusterWorkerEnv = worker._clusterWorkerEnv; log('[%s] [cfork:master:%s] new worker:%s fork (state: %s)', utility.logDate(), process.pid, newWorker.process.pid, newWorker.state); } else { @@ -167,18 +169,22 @@ function fork(options) { }); for (var i = 0; i < count; i++) { - newWorker = forkWorker(); + const env = { CFORK_WORKER_INDEX: String(i), CFORK_WORKER_COUNT: String(count) }; + newWorker = forkWorker(null, env); newWorker._clusterSettings = cluster.settings; + newWorker._clusterWorkerEnv = env; } // fork slaves after workers are forked if (options.slaves) { var slaves = Array.isArray(options.slaves) ? options.slaves : [options.slaves]; slaves.map(normalizeSlaveConfig) - .forEach(function(settings) { + .forEach(function(settings, index) { if (settings) { - newWorker = forkWorker(settings); + const env = { CFORK_SLAVE_WORKER_INDEX: String(index), CFORK_SLAVE_WORKER_COUNT: String(slaves.length) }; + newWorker = forkWorker(settings, env); newWorker._clusterSettings = settings; + newWorker._clusterWorkerEnv = env; } }); } @@ -264,12 +270,12 @@ function fork(options) { /** * fork worker with certain settings */ - function forkWorker(settings) { + function forkWorker(settings, workerEnv) { if (settings) { cluster.settings = settings; setupPrimary(); } - return cluster.fork(attachedEnv); + return cluster.fork(Object.assign({}, attachedEnv, workerEnv)); } /** diff --git a/test/cfork.test.js b/test/cfork.test.js index d48bac3..915eb81 100644 --- a/test/cfork.test.js +++ b/test/cfork.test.js @@ -1,12 +1,13 @@ 'use strict'; +var assert = require('assert'); var should = require('should'); var pedding = require('pedding'); var urllib = require('urllib'); var childprocess = require('childprocess'); var path = require('path'); -describe('cfork.test.js', function () { +describe('test/cfork.test.js', () => { var child; var messages = []; @@ -90,12 +91,35 @@ describe('cfork.test.js', function () { should.ifError(err); body.toString().should.equal('😂'); resp.statusCode.should.equal(200); - done(); + urllib.request('http://localhost:1985/worker_index', function (err, body, resp) { + should.ifError(err); + body.toString().should.equal('slave worker index: 0, 1'); + resp.statusCode.should.equal(200); + done(); + }); }); }); }); }); + it('should get CFORK_WORKER_INDEX env value', function (done) { + urllib.request('http://localhost:1984/worker_index', function (err, body, resp) { + should.ifError(err); + const text = body.toString(); + // console.log('%o', text); + assert(text === 'worker index: 0, 4' || text === 'worker index: 1, 4' + || text === 'worker index: 2, 4' || text === 'worker index: 3, 4', text); + resp.statusCode.should.equal(200); + urllib.request('http://localhost:1985/worker_index', function (err, body, resp) { + should.ifError(err); + const text = body.toString(); + assert.equal(text, 'slave worker index: 0, 1'); + resp.statusCode.should.equal(200); + done(); + }); + }); + }); + it('should slave exit', function (done) { urllib.request('http://localhost:1985/exit', function (err) { should.exist(err); @@ -108,7 +132,12 @@ describe('cfork.test.js', function () { urllib.request('http://localhost:1984/async_error', function (err) { console.error('[cfork.test.js] get /async_error error: %s', err); should.exist(err); - err.message.should.containEql('socket hang up'); + // ECONNRESET on windows + if (process.platform === 'win32') { + err.message.should.containEql('ECONNRESET'); + } else { + err.message.should.containEql('socket hang up'); + } done(); }); @@ -117,7 +146,12 @@ describe('cfork.test.js', function () { }, function (err) { console.error('[cfork.test.js] get /hold error: %s', err); should.exist(err); - err.message.should.containEql('timeout'); + // ECONNRESET on windows + if (process.platform === 'win32') { + err.message.should.containEql('ECONNRESET'); + } else { + err.message.should.containEql('timeout'); + } done(); }); }); diff --git a/test/one_worker_cluster.test.js b/test/one_worker_cluster.test.js index 66ea6af..2fa9f04 100644 --- a/test/one_worker_cluster.test.js +++ b/test/one_worker_cluster.test.js @@ -38,7 +38,12 @@ describe('one_worker_cluster.test.js', function() { urllib.request('http://localhost:1984/async_error', function(err) { console.error('[cfork.test.js] get /async_error error: %s', err); should.exist(err); - err.message.should.containEql('socket hang up'); + // ECONNRESET on windows + if (process.platform === 'win32') { + err.message.should.containEql('ECONNRESET'); + } else { + err.message.should.containEql('socket hang up'); + } done(); }); @@ -48,7 +53,12 @@ describe('one_worker_cluster.test.js', function() { }, function (err) { console.error('[cfork.test.js] get /hold error: %s', err); should.exist(err); - err.message.should.containEql('socket hang up'); + // ECONNRESET on windows + if (process.platform === 'win32') { + err.message.should.containEql('ECONNRESET'); + } else { + err.message.should.containEql('socket hang up'); + } done(); }); });