js에 익숙한 친구들은 js 是单线程
라는 것을 알고 있습니다. Node에서는 다중 프로세스 단일 스레드 모델이 채택됩니다. JavaScript의 단일 스레드 제한으로 인해 멀티 코어 서버에서는 서버 성능을 최대화하기 위해 여러 프로세스를 시작해야 하는 경우가 많습니다.
Node.js 프로세스 클러스터는 여러 Node.js 인스턴스를 실행하는 데 사용될 수 있으며, 이를 통해 애플리케이션 스레드 간에 작업 부하를 분산할 수 있습니다. 프로세스 격리가 필요하지 않은 경우 대신 단일 Node.js 인스턴스 내에서 여러 애플리케이션 스레드를 실행할 수 있도록 하는 worker_threads
모듈을 사용하세요.
노드는 V0.8 버전 이후 클러스터 모듈을 도입했는데,一个主进程(master) 管理多个子进程(worker) 的方式实现集群
.
클러스터 모듈을 사용하면 서버 포트를 공유하는 하위 프로세스를 쉽게 만들 수 있습니다.
클러스터의 맨 아래 계층은 child_process 모듈입니다. 일반 메시지를 보내는 것 외에도 기본 개체인
TCP
,UDP
등을 보낼 수도 있습니다.cluster
모듈은child_process
모듈과net
모듈을 결합한 응용 프로그램입니다. 클러스터가 시작되면 TCP 서버가 내부적으로 시작되고 TCP 서버 소켓의 파일 설명자가 작업 프로세스로 전송됩니다.
cluster
모듈 애플리케이션에서一个主进程只能管理一组工作进程
. 해당 운영 모드는 child_process
모듈만큼 유연하지는 않지만 더 안정적입니다.
const 클러스터 = require('cluster') 및 복잡한
.isMaster
기본 프로세스를 식별하고 Node<16.isPrimary는.isPrimary
Node>16.isWorker는.isWorker
. .worker는.worker
.workers
id
필드를 키로 사용하여 활성 작업자 프로세스 객체의 해시를 저장합니다. 이를 통해 모든 작업자 프로세스를 쉽게 반복할 수 있습니다. 기본 프로세스에서만 사용할 수 있습니다. cluster.wokers[id] === worker
[기본 프로세스에서].settings
는 읽기 전용, 클러스터 구성 항목입니다. .setupPrimary() 또는 .fork() 메서드를 호출한 후 이 설정 개체에는 기본값을 포함한 설정이 포함됩니다. 이전에는 빈 개체였습니다. 이 개체를 수동으로 변경하거나 설정하면 안 됩니다.cluster.settings
구성 항목 세부 정보:- `execArgv` <string[]>Node.js 실행 파일에 전달된 문자열 매개변수 목록입니다. **기본값:** `process.execArgv`. - `exec` <string> 작업자 프로세스 파일의 파일 경로입니다. **기본값:** `process.argv[1]`. - `args` <string[]> 작업자 프로세스에 전달된 문자열 인수입니다. **기본값:** `process.argv.slice(2)`. - `cwd` <string>작업자 프로세스의 현재 작업 디렉터리입니다. **기본값:** `정의되지 않음`(상위 프로세스에서 상속됨) - `serialization` <string>프로세스 간에 메시지를 보내는 데 사용되는 직렬화 유형을 지정합니다. 가능한 값은 `'json'` 및 `'advanced'`입니다. **기본값:** '거짓'. - `silent` <boolean>상위 프로세스의 표준 입력 및 출력으로 출력을 보낼지 여부입니다. **기본값:** '거짓'. - `stdio` <Array>는 생성된 프로세스의 표준 입력 및 출력을 구성합니다. 클러스터 모듈은 실행을 위해 IPC에 의존하기 때문에 이 구성에는 'ipc' 항목이 포함되어야 합니다. 이 옵션이 제공되면 'silent'가 재정의됩니다. - `uid` <number>는 프로세스의 사용자 ID를 설정합니다. - `gid` <number>는 프로세스의 그룹 ID를 설정합니다. - `inspectPort` <번호> | <Function> 작업자 프로세스의 검사기 포트를 설정합니다. 이는 매개 변수를 사용하지 않고 숫자를 반환하는 숫자 또는 함수일 수 있습니다. 기본적으로 각 작업자 프로세스에는 기본 프로세스의 'process.debugPort'에서 시작하여 증가하는 자체 포트가 있습니다. - `windowsHide` <boolean> 일반적으로 Windows 시스템에서 생성되는 프로세스 콘솔 창을 숨깁니다. **기본값:** '거짓'.
.fork([env])
[기본 프로세스에서] 새 작업자 프로세스를 생성합니다..setupPrimary([settings])
Node>16.setupMaster([settings])
기본 'fork' 동작을 변경하는 데 사용됩니다. 사용 후 설정은 cluster.settings
에 표시됩니다. 모든 설정 변경은 아직 실행 중인 작업자 프로세스가 아닌 .fork()
에 대한 향후 호출에만 영향을 미칩니다. 위의 기본값은 첫 번째 호출에만 적용됩니다. 노드가 16개 미만입니다. [메인 프로세스에서].disconnect([callback])
모든 작업자 프로세스가 연결 해제 및 닫기 핸들을 처리할 때 호출됩니다. [메인 프로세스에서]클러스터를 더욱 안정적이고 강력하게 만들기 위해 cluster
모듈도
'message'
많은 이벤트: 'message' 이벤트를 노출합니다
.'exit'
이벤트, 작업자 프로세스가 종료되면 클러스터 모듈이 'exit'
이벤트를 트리거합니다.Cluster.on('exit', (작업자, 코드, 신호) => { console.log('작업자 %d가 사망했습니다(%s). 다시 시작 중...', 작업자.프로세스.pid, 신호 코드 || 클러스터.포크(); });
'listening'
이벤트, 작업자 프로세스에서 listen()
호출한 후 'listening'
이벤트가 서버에서 트리거되면 기본 프로세스의 cluster
도 'listening'
이벤트를 트리거합니다.Cluster.on('listening', (작업자, 주소) => { 콘솔.로그( `이제 작업자가 ${address.address}:${address.port}에 연결되었습니다`); });
'fork'
이벤트, 새 작업자 프로세스가 생성되면 클러스터 모듈이 'fork'
이벤트를 트리거합니다.Cluster.on('fork', (작업자) => { timeouts[worker.id] = setTimeout(errorMsg, 2000); });
'setup'
이벤트는 .setupPrimary()
호출될 때마다 트리거됩니다.disconnect
이벤트는 작업자 프로세스의 IPC 채널 연결이 끊어진 후 트리거됩니다. 작업자 프로세스가 정상적으로 종료되거나, 종료되거나, 수동으로 연결을 끊을 때Cluster.on('disconnect', (worker) => { console.log(`작업자 #${worker.id}의 연결이 끊어졌습니다`); });
Worker
객체에는 작업자 프로세스의 모든 공개 정보와 메서드가 포함되어 있습니다. 기본 프로세스에서는 cluster.workers
사용하여 가져올 수 있습니다. 작업자 프로세스에서는 cluster.worker
사용하여 가져올 수 있습니다.
.id
프로세스 식별. 각각의 새로운 작업자 프로세스에는 고유한 ID가 부여됩니다. 이 ID는 id
에 저장됩니다. 작업자 프로세스가 활성 상태일 때 이는 cluster.workers
에서 이를 인덱싱하는 키입니다..process
모든 작업자 프로세스는 child_process.fork()
사용하여 생성되며 이 함수에서 반환된 개체는 .process
로 저장됩니다. 작업자 프로세스에는 전역 process
저장됩니다..send(message[, sendHandle[, options]][, callback])
작업자 프로세스 또는 기본 프로세스에 메시지를 보내고 핸들을 사용하도록 선택할 수 있습니다. 기본 프로세스에서는 특정 작업자 프로세스에 메시지를 보냅니다. ChildProcess.send()
와 동일합니다. 작업자 프로세스에서는 기본 프로세스에 메시지를 보냅니다. process.send()
와 동일합니다..destroy()
.kill([signal])
이 함수는 작업자 프로세스를 종료합니다. kill()
함수는 정상적인 연결 해제를 기다리지 않고 작업자 프로세스를 종료합니다. 이는 worker.process.kill()
과 동일한 동작을 갖습니다. 이전 버전과의 호환성을 위해 이 메서드의 별칭은 worker.destroy()
입니다..disconnect([callback])
은 작업자 프로세스로 전송되어 모든 서버를 종료하고 해당 서버에서 'close'
이벤트를 기다린 다음 IPC 채널 연결을 끊는 자체 .disconnect()
를 호출하게 합니다..isConnect()
이 함수는 작업자 프로세스가 IPC 채널을 통해 기본 프로세스에 연결되어 있으면 true
반환하고, 그렇지 않으면 false
를 반환합니다. 작업자 프로세스는 생성 후 마스터 프로세스에 연결됩니다..isDead()
이 함수는 작업자 프로세스가 종료된 경우(종료 또는 신호 수신으로 인해) true
반환합니다. 그렇지 않으면 false
반환합니다.클러스터를 보다 안정적이고 강력하게 만들기 위해 cluster
모듈은
'message'
이벤트라는 많은 이벤트도 노출합니다.Cluster.workers[id].on('message', messageHandler);
'exit'
이벤트, 작업자 프로세스가 종료되면当前worker工作进程
개체가 'exit'
이벤트를 트리거합니다.if (cluster.isPrimary) { const 작업자 = Cluster.fork(); Worker.on('exit', (코드, 신호) => { if (신호) { console.log(`작업자가 ${signal} 신호에 의해 사망했습니다`); } else if (코드 !== 0) { console.log(`작업자가 오류 코드로 종료했습니다: ${code}`); } 또 다른 { console.log('작업자 성공!'); } }); }
'listening'
이벤트, 작업자 프로세스에서 listen()
호출하여 현재 작업자 프로세스를 수신합니다.Cluster.fork().on('listening', (주소) => { // 작업자 프로세스가 수신 중입니다. });
disconnect
이벤트입니다. 작업자 프로세스가 정상적으로 종료되거나, 종료되거나, 수동으로 연결을 끊을 때Cluster.fork().on('disconnect', () => { //현재 작업자 개체에 대한 트리거로 제한됨})
노드에서는 프로세스 간 통신 (IPC)을 사용하여 메인 프로세스와 하위 프로세스 간의 통신을 수행합니다. .send()
(a.send는 Send에 메시지를 보내는 것을 의미함) 메서드를 사용하여 메시지를 보내고 message
이벤트를 수신하여 정보를 수집합니다. 이는 EventEmitter
통합하여 cluster模块
에 의해 구현됩니다. 공식 웹사이트에 있는 간단한 프로세스 간 통신 예제이기도 합니다
process.on('message')
, process.send()
child.on('message')
, child.send()
# 클러스터.isMaster # 클러스터.포크() # 클러스터.작업자 # Cluster.workers[id].on('message', messageHandler); # Cluster.workers[id].send(); # process.on('message', messageHandler); # 프로세스.send(); const 클러스터 = require('클러스터'); const http = require('http'); # 주요 프로세스 if (cluster.isMaster) { // http 요청을 추적합니다. console.log(`기본 ${process.pid}가 실행 중입니다`); numReqs = 0으로 둡니다. // 요청 계산 함수 messageHandler(msg) { if (msg.cmd && msg.cmd === 'notifyRequest') { numReqs += 1; } } // 작업자를 시작하고 informRequest가 포함된 메시지를 수신합니다. // 다중 프로세스 시작(CPU 코어 수) // 작업자 프로세스를 생성합니다. const numCPUs = require('os').cpus().length; for (let i = 0; i < CPU 수; i++) { console.log(i) 클러스터.포크(); } // 클러스터 작업자 기본 프로세스는 하위 프로세스와 통신합니다. for (cluster.workers의 const id) { // ***하위 프로세스의 이벤트 수신 Cluster.workers[id].on('message', messageHandler); // ***cluster.workers[id].send({를 하위 프로세스로 보냅니다. 유형: 'masterToWorker', 보낸 사람: '마스터', 데이터: { 번호: Math.floor(Math.random() * 50) } }); } Cluster.on('exit', (작업자, 코드, 신호) => { console.log(`작업자 ${worker.process.pid}가 사망했습니다`); }); } 또 다른 { # 하위 프로세스 // 작업자 프로세스는 모든 TCP 연결을 공유할 수 있습니다. // 이 예에서는 HTTP 서버입니다. // 작업자 프로세스에는 http 서버가 있습니다. http.Server((req, res) => { res.writeHead(200); res.end('안녕하세요 세계n'); //********! ! ! ! 요청에 대해 마스터에게 알리세요! ! ! ! ! ! ******* //****** 보내기 process.send({ cmd: 'notifyRequest' }); //****** process.on('message', function(message) { 듣기 //xxxxxx }) }).listen(8000); console.log(`작업자 ${process.pid}가 시작되었습니다`); }
NodeJS 프로세스 간의 통신에는 메시지 전달만 포함되며 실제로 개체를 전송하지는 않습니다.
메시지를 보내기 전에 send()
메소드는 메시지를 핸들과 메시지로 조합합니다. JSON.stringify
, 핸들을 전달할 때 전체 객체가 전달되지 않습니다. IPC 채널을 통해 전송되며 모두 문자열이며 전송 후 JSON.parse
통해 객체로 복원됩니다.
코드에 app.listen(port)
이 있는데 왜 여러 프로세스가 동일한 포트를 청취할 수 있습니까?
그 이유는 메인 프로세스가 send() 메소드를 통해 메인 프로세스에 속한 서비스 개체의 핸들을 여러 하위 프로세스에 전송하므로 각 하위 프로세스마다 핸들을 복원한 후 동일한 서비스 개체를 가져오기 때문입니다. 네트워크가 서버에 요청할 때 프로세스 서비스가 선점되므로 동일한 포트에서 수신할 때 예외가 발생하지 않습니다.
# master.js const 포크 = require('child_process').fork; const CPUs = require('os').cpus(); for (let i=0; i<cpus.length; i++) { const 작업자 = 포크('worker.js'); console.log('작업자 프로세스가 생성되었습니다. pid: %s ppid: %s', Worker.pid, process.pid); }
# 작업자.js const http = require('http'); http.createServer((req, res) => { res.end('나는 노동자입니다, pid: ' + process.pid + ', ppid: ' + process.ppid); }).listen(3000);
위의 코드 예에서 콘솔이
node master.js
실행하면 한 작업자만 포트 3000을 수신할 수 있고 나머지 작업자는Error: listen EADDRINUSE :::3000
오류를 발생시킵니다.
发送句柄
기능이 지원됩니다./** * http://nodejs.cn/api/child_process.html#child_process_subprocess_send_message_sendhandle_options_callback * 메시지 * sendHandle */ subprocess.send(message, sendHandle)
상위 프로세스와 하위 프로세스 사이에 IPC 채널이 설정된 후 하위 프로세스 개체의 send 메서드를 통해 메시지가 전송됩니다.二个参数sendHandle 就是句柄,可以是TCP套接字、TCP服务器、UDP套接字等
, 위의 다중 프로세스 포트 점유 문제를 해결하기 위해 기본 프로세스의 소켓을 하위 프로세스에 전달합니다.
# master.js const 포크 = require('child_process').fork; const CPUs = require('os').cpus(); const 서버 = require('net').createServer(); 서버.듣기(3000); process.title = '노드-마스터' for (let i=0; i<cpus.length; i++) { const 작업자 = 포크('worker.js'); # 핸들을 전달합니다.worker.send('server', server); console.log('작업자 프로세스가 생성되었습니다. pid: %s ppid: %s', Worker.pid, process.pid); }
// 작업자.js 노동자를 보자; process.title = '노드 작업자' process.on('message', function (message, sendHandle) { if (메시지 === '서버') { 작업자 = sendHandle; 작업자.on('연결', 함수(소켓) { console.log('나는 작업자입니다, pid: ' + process.pid + ', ppid: ' + process.ppid) }); } });
콘솔이 node master.js
실행하는지 확인하세요.
cluster
이해한다면, 자식 프로세스가 cluster.fork()
통해 생성된다는 것을 알게 될 것입니다. Linux에서는 시스템이 기본적으로 fork
방법을 제공하는데 노드가 시스템의 기본 방법을 직접 사용하는 대신 자체적으로 cluster模块
구현하도록 선택하는 이유는 무엇입니까? 주된 이유는 다음 두 가지입니다.
포크 프로세스는 동일한 포트를 모니터링하므로 포트 점유 오류가 발생합니다.
포크 프로세스 간에 로드 밸런싱이 없어
cluster模块
에서 천둥 떼 현상이 발생하기 쉽습니다. 첫 번째 문제는 현재 프로세스가 master进程
인지 확인하고, 그렇지 않은 경우 포크 worker进程
로 표시되며 포트를 수신하지 않습니다.
두 번째 질문에 대한 응답으로 cluster模块
로드 밸런싱 기능이 내장되어 있습니다. master进程
요청을 수신하기 위해 포트를 수신한 다음 스케줄링 알고리즘을 통해 해당 worker进程
에 요청을 할당합니다. 라운드 로빈, 스케줄링 알고리즘은 환경 변수 NODE_CLUSTER_SCHED_POLICY
를 통해 수정할 수 있습니다.
코드에서 포착되지 않은 예외가 발생하면 프로세스가 종료됩니다. 이때 Node.js는 이를 포착하기 위해 process.on('uncaughtException', handler)
인터페이스를 제공합니다. 작업자 프로세스에서 잡히지 않는 예외가 발생하면 이미 불확실한 상태입니다. 이때 프로세스가 정상적으로 종료되도록 해야 합니다.
+---------+ +---------+ 노동자 | +---------+ +------+----+ 잡히지 않은예외 | +----------------+ | +---------+ | | <----------+ | +----+----+ | 연결 끊기 | 새 작업자 포크 | +------------> + ---------- -> | | 잠깐... | |출구 | +------------> | | 죽다 | | |
되지 않은 예외가 발생할 때와 달리 프로세스에 충돌이나 OOM을 발생시키고 시스템에 의해 종료되는 경우에도 프로세스가 계속 실행되도록 할 수 있는 기회가 있습니다. 현재 프로세스는 직접 종료되고 마스터는 즉시 새 작업자를 포크합니다.
child_process 모듈은 하위 프로세스를 파생시키는 기능, 즉 단순히执行cmd命令的能力
제공합니다. 기본적으로 stdin、 stdout 和stderr 的管道会在父Node.js 进程和衍生的子进程之间建立
. 이러한 파이프라인에는 제한된(플랫폼별) 용량이 있습니다. stdout에 쓸 때 자식 프로세스가 이 제한을 초과하고 출력이 캡처되지 않으면 자식 프로세스는 파이프 버퍼가 더 많은 데이터를 수용할 때까지 차단하고 기다립니다. 이는 셸의 파이프와 동일한 동작입니다. 출력이 소비되지 않으면 { stdio: 'ignore' } 옵션을 사용하세요.
const cp = require('child_process');
API를 통해 생성된 하위 프로세스는 상위 프로세스와 필요한 연결이 없습니다.
Node가
사용됩니다.
fork(modulePath, args)
: Node 프로세스를 독립적인 프로세스로 실행하여 계산 처리와 파일 디스크립터를 Node 메인 프로세스와 분리(자식 프로세스 복사)하고 싶을 때 사용합니다.Non-
spawn(command, args)
: 일부 문제 처리 하위 프로세스 I/O가 많거나 프로세스의 출력량이 많은 경우 execFile(file, args[, callback])을 사용하십시오execFile(file, args[, callback])
외부 프로그램만 실행해야 하는 경우에 사용하십시오.사용자가 입력한 세 가지 동기화 execSync
exec(command, options)
주의하세요execFileSync
, spawnSync
다른 세 가지 방법은 spawn()
의 확장입니다.
. 파생된 Node.js 하위 프로세스는 독립적입니다. 둘 사이에 설정된 IPC 통신 채널을 제외한 상위 프로세스. 각 프로세스에는 자체 메모리와 V8 인스턴스가 있습니다
.
예를 들어, 디렉토리에 Worker.js와 master.js라는 두 개의 파일을 만듭니다.
# child.js const t = JSON.parse(process.argv[2]); console.error(`하위 프로세스 t=${JSON.stringify(t)}`); process.send({hello:`son pid=${process.pid} 아빠에게 프로세스 pid=${process.ppid} hello`를 전해 주세요`}); process.on('메시지', (msg)=>{ console.error(`하위 프로세스 msg=${JSON.stringify(msg)}`); })
# 부모.js const {fork} = require('child_process'); for(let i = 0; i < 3; i++){ const p = fork('./child.js', [JSON.stringify({id:1,name:1})]); p.on('메시지', (msg) => { console.log(`자식의 메시지 msg=${JSON.stringify(msg)}`, ); }); p.send({hello:`${process.pid} 프로세스 id=${i}`}); }
node parent.js
통해 parent.js를 시작한 다음 ps aux | grep worker.js
통해 프로세스 수를 확인하면 이상적으로는 프로세스 수가 CPU 코어 수와 같고 각 프로세스는 하나를 사용한다는 것을 알 수 있습니다. CPU 코어.
이는 고전적인 Master-Worker 모드(마스터-슬레이브 모드)입니다.
실제로 프로세스를 포크하는 것은 비용이 많이 들고, 프로세스를 복사하는 목적은 CPU 리소스를 최대한 활용하는 것이므로 NodeJS는 높은 동시성 문제를 해결하기 위해 단일 스레드에서 이벤트 중심 접근 방식을 사용합니다.
적용 가능한 시나리오 <br/>일반적으로 시간이 많이 걸리는 시나리오에 사용되며 파일 다운로드와 같은 노드를 사용하여 구현됩니다.
Fork는 다중 스레드 다운로드를 구현할 수 있습니다. 파일을 여러 블록으로 나눈 다음 각 프로세스가 일부를 다운로드하고 마지막으로 함께 넣습니다.
const cp = require('child_process'); // 첫 번째 매개변수는 실행할 실행 파일의 이름이나 경로입니다. 여기 에코가 있어요 cp.execFile('echo', ['hello', 'world'], (err, stdout, stderr) => { if (err) { console.error(err) } console.log('stdout: ', stdout); console.log('stderr: ', stderr); });
적용 가능한 시나리오 <br/> ls 등과 같이 오버헤드가 적고 결과에 더 주의를 기울이는 작업에 더 적합합니다.
는 주로 쉘 메소드를 실행하는 데 사용됩니다. 여전히 내부적으로 호출되지만 최대 캐시 제한이 있습니다.
const cp = require('child_process'); cp.exec(`cat ${__dirname}/messy.txt | sort | uniq`, (err, stdout, stderr) => { console.log(stdout); });
적용 가능한 시나리오 <br/>오버헤드가 적고 ls 등과 같은 결과에 더 주의를 기울이는 작업에 더 적합합니다.
단일 작업을
반환합니다. const cp = require('child_process'); const child = cp.spawn('echo', ['hello', 'world']); child.on('오류', console.error); # 출력은 스트림이며 기본 프로세스 stdout, 콘솔로 출력됩니다. child.stdout.pipe(process.stdout); child.stderr.pipe(process.stderr);
멀티태스킹 연결
const cp = require('child_process'); const path = require('경로'); const cat = cp.spawn('cat', [path.resolve(__dirname, 'messy.txt')]); const sort = cp.spawn('sort'); const uniq = cp.spawn('uniq'); # 출력은 스트림입니다. cat.stdout.pipe(sort.stdin); sort.stdout.pipe(uniq.stdin); uniq.stdout.pipe(process.stdout);
적용 가능한 시나리오
generate는 스트리밍이므로 npm install 실행, 설치 프로세스 인쇄 등 시간이 많이 걸리는 작업에 적합합니다.
프로세스가 종료된 후 트리거되고 표준 입출력 스트림(sdtio)이 실행됩니다. 하위 프로세스가 'close'
이벤트가 종료되었습니다. 이 이벤트는 여러 프로세스가 동일한 stdio 스트림을 공유할 수 있으므로 exit
와 다릅니다.
매개변수:
질문: 코드가 존재해야 합니까?
(코드의 주석에서는 아닌 것 같습니다.) 예를 들어 kill
사용하여 하위 프로세스를 종료하는 경우 코드는 무엇입니까?
매개변수:
code, signal, 자식 프로세스가 자체적으로 종료되면 code
종료 코드이고, 그렇지 않으면 null입니다.
자식 프로세스가 신호를 통해 종료되면 signal
프로세스를 종료하라는 신호이고, 그렇지 않으면 null입니다.
둘 중 하나는 null이 아니어야 합니다.
참고 사항 :
exit
이벤트가 트리거되면 하위 프로세스의 stdio 스트림이 여전히 열려 있을 수 있습니다. (시나리오?) 또한 nodejs는 SIGINT 및 SIGTERM 신호를 수신합니다. 즉, nodejs가 이 두 신호를 수신하면 즉시 종료되지 않고 먼저 정리 작업을 수행한 다음 이 두 신호를 다시 발생시킵니다. (시각적으로 js는 이때 데이터베이스 닫기 등과 같은 정리 작업을 수행할 수 있습니다.)
SIGINT
: 인터럽트, 프로그램 종료 신호, 일반적으로 사용자가 CTRL+C를 누를 때 발생하며, 프로세스를 종료하도록 포그라운드 프로세스에 알리는 데 사용됩니다.
SIGTERM
: 종료, 프로그램 종료 신호. 이 신호는 차단 및 처리될 수 있으며 일반적으로 프로그램이 정상적으로 종료되도록 요구하는 데 사용됩니다. 쉘 명령 kill은 기본적으로 이 신호를 생성합니다. 신호를 종료할 수 없으면 SIGKILL(강제 종료)을 시도합니다.
다음과 같은 상황이 발생하면 오류가 발생합니다. 오류가 발생하면 종료가 발생하거나 발생하지 않을 수 있습니다. (마음이 아프다)
process.send()
사용하여 메시지를 보낼 때 트리거됩니다.
매개변수 :
message
는 json 객체 또는 기본 값입니다. sendHandle
, net.Socket 객체 또는 net.Server 객체(클러스터에 익숙한 학생들은 이에 익숙해야 합니다)
: .disconnected()
호출할 때 설정합니다. 거짓으로. 자식 프로세스로부터 메시지를 받을 수 있는지 또는 자식 프로세스에 메시지를 보낼 수 있는지 여부를 나타냅니다.
.disconnect() : 상위 프로세스와 하위 프로세스 간의 IPC 채널을 닫습니다. 이 메서드가 호출되면 disconnect
이벤트가 발생합니다. 하위 프로세스가 노드 인스턴스(child_process.fork()를 통해 생성됨)인 경우 IPC 채널을 종료하기 위해 하위 프로세스 내에서 process.disconnect()
적극적으로 호출할 수도 있습니다.
단일 스레드 문제에 응답합니다. 다중 프로세스 방법은 일반적으로 멀티스레딩을 시뮬레이션하는 데 사용됩니다.
노드 프로세스에 의해 점유됩니다. 7-스레드
노드의 핵심은 노드가 시작된 후 v8의 인스턴스가 생성됩니다
.单线程
이지만 Node이든 브라우저이든 Javascript의 호스트 환경은 다중 스레드입니다.
Javascript가 단일 스레드인 이유는 무엇입니까?
이 문제는 브라우저에서 시작되어야 합니다. 브라우저 환경에서 DOM 작업의 경우 여러 스레드가 동일한 DOM에서 작동하면 DOM 작업이 단일 방식으로만 수행될 수 있다는 것을 의미합니다. DOM 렌더링 충돌을 피하세요. 브라우저 환경에서는 UI 렌더링 스레드와 JS 실행 엔진이 상호 배타적입니다. 하나가 실행되면 다른 하나는 JS 엔진에 의해 결정됩니다.
변경
worker_threads
. isMainThread, 부모 포트, 작업자데이터, 스레드 ID, 메시지채널, 메시지포트, 노동자 } = require('worker_threads'); 함수 메인스레드() { for (let i = 0; i < 5; i++) { const 작업자 = new Worker(__filename, { 작업자데이터: i }); Worker.on('exit', code => { console.log(`main: 작업자가 종료 코드 ${code}`)로 중지되었습니다. }); 작업자.on('메시지', msg => { console.log(`main: receive ${msg}`); 작업자.포스트메시지(msg + 1); }); } } 함수 작업자Thread() { console.log(`작업자: 작업자 날짜 ${workerData}`); parentPort.on('메시지', msg => { console.log(`작업자: ${msg} 수신`); }), parentPort.postMessage(workerData); } if (isMainThread) { 메인스레드(); } 또 다른 { 작업자쓰레드(); }
const 주장 = require('assert'); const { 노동자, 메시지채널, 메시지포트, isMainThread, 상위 포트 } = require('worker_threads'); if (isMainThread) { const 작업자 = 새 작업자(__filename); const subChannel = 새로운 MessageChannel(); Worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]); subChannel.port2.on('메시지', (값) => { console.log('수신:', 값); }); } 또 다른 { parentPort.once('메시지', (값) => { 주장(value.hereIsYourPort 인스턴스ofMessagePort); value.hereIsYourPort.postMessage('작업자가 이것을 보내고 있습니다.'); value.hereIsYourPort.close(); }); }
프로세스는 자원 할당의 최소 단위이고, 스레드는 CPU 스케줄링의 최소 단위입니다.
IPC(프로세스 간 통신)는进程间通信
입니다. 각 프로세스는 생성 후 자신만의 독립적인 주소 공간을 가지므로 IPC를 구현하는 목적은 프로세스 간에 리소스에 대한 액세스를 공유하는 것입니다.
IPC를 구현하는 방법에는 파이프, 메시지 대기열, 세마포어, 도메인 소켓 및 Node.js가 파이프를 통해 구현되는 등 여러 가지가 있습니다.
실제로 상위 프로세스는 하위 프로세스를 생성하기 전에 먼저 IPC 채널을 생성하고 이 IPC를 수신한 다음 하위 프로세스를 생성하고 환경 변수를 통해 IPC 채널과 관련된 파일 설명자를 알려줍니다. NODE_CHANNEL_FD) 자식 프로세스가 시작됩니다. 이때 파일 디스크립터에 따라 IPC 채널이 연결되어 부모 프로세스와 연결됩니다.
핸들은 리소스를 식별하는 데 사용할 수 있는 참조입니다. 여기에는 개체를 가리키는 파일 리소스 설명자가 포함됩니다.
일반적으로 하나의 포트에서 여러 프로세스를 모니터링하려는 경우 기본 프로세스 에이전트 사용을 고려할 수 있습니다.
그러나 이 프록시 솔루션은 각 요청 수신 및 프록시 전달에서 두 개의 파일 설명자를 사용하게 하며 시스템의 파일 설명자는 제한됩니다. 이 접근 방식은 시스템 확장성에 영향을 미칩니다.
그렇다면 핸들을 사용하는 이유는 무엇입니까? 그 이유는 실제 애플리케이션 시나리오에서 IPC 통신 설정에는 더 복잡한 데이터 처리 시나리오가 포함될 수 있기 때문입니다. 핸들은 send()
메서드의 두 번째 선택적 매개 변수로 전달될 수 있으며, 이는 리소스 식별자가 IPC를 직접 전달할 수 있음을 의미합니다. 전송은 위에서 언급한 프록시 전달로 인해 발생하는 파일 설명자의 사용을 방지합니다.
전송을 지원하는 핸들 유형은 다음과 같습니다.
고아 프로세스의 상위 프로세스가 하위 프로세스를 생성한 후 상위 프로세스는 종료되지만 하나 이상의 하위 학부모 프로세스에 해당하는 프로세스는 여전히 생존 중에도 이러한 하위 프로세스는 시스템의 초기 프로세스에 의해 채택되며 해당 프로세스 PPID는 고아 프로세스입니다. 이것은 다음 코드 예제로 설명됩니다.
# Worker.js const http = require ( 'http'); const server = http.createserver ((req, res) => { res.end ( '나는 Worker, Pid :' + process.pid + ', ppid :' + process.ppid); // 현재 작업자 프로세스 PID 및 상위 프로세스 PPID 기록 }); 노동자에게하자; process.on ( 'message', function (message, sendhandle) { if (message === 'server') { 작업자 = SendHandle; worker.on ( '연결', 기능 (소켓) { server.emit ( '연결', 소켓); }); } });
# # # js const fork = 요구 ( 'child_process'). 포크; const server = require ( 'net'). createServer (); Server.Listen (3000); Const Worker = Fork ( 'worker.js'); Worker.Send ( 'Server', Server); Console.log ( '작업자 프로세스 생성, PID : %S PPID : %S', worker.pid, process.pid); process.exit (0); // 하위 프로세스를 만들면 주요 프로세스가 종료됩니다.이 시점에서 생성 된 작업자 프로세스는
테스트를위한 고아 프로세스 콘솔이되고 현재 작업자 프로세스 PID 및 부모 프로세스 PPID가 출력됩니다.
부모 프로세스는 Master.js에서 종료되므로 활동 모니터는 작업자 프로세스 만 보여줍니다.
다시 확인하고, 콘솔 호출 인터페이스를 열면, 작업자 프로세스 5611에 해당하는 PPID가 1 (INIT 프로세스의 경우)이며 현재 고아 프로세스가되었음을 알 수 있습니다.
데몬 프로세스는 백그라운드에서 실행되며 터미널의 영향을받지 않습니다.
Node.js를 개발하는 학생들은 터미널을 열고 node app.js
실행하여 서비스 프로세스를 시작하면 터미널을 닫으면 서비스가 분리됩니다.前台运行模式
.
데몬 프로세스 방법을 사용하는 경우이 터미널에서 서비스 프로세스를 시작하기 위해 node app.js
실행 한 후이 터미널에서 서로 영향을 미치지 않고 다른 작업을 수행 할 수도 있습니다.
자식 프로세스 생성
아동 프로세스에서 새 세션을 만듭니다 (시스템 기능 SetSID 호출)
아동 프로세스의 작업 디렉토리 변경 (예 : "/"또는 "/usr/등)
부모 프로세스 종료
options.detached
세
const spawn = 요구 ( 'child_process'). spawn; 함수 startDaemon () { const daemon = spawn ( 'node', [ 'daemon.js'], { CWD : '/usr', 분리 : 사실, Stdio : '무시', }); console.log ( '데몬 프로세스 시작 부모 프로세스 PID : %S, 데몬 프로세스 PID : %s', process.pid, daemon.pid); 데몬.unref (); } startDaemon ()Daemon.js 파일의 처리 로직은 타이머를 시작 하고이 리소스
가
동시에 종료되지 않도록 10 초마다 실행됩니다.
아동 과정의.const fs = require('fs'); const {console} = require ( 'console'); // 사용자 정의 간단한 로거 const logger = 새 콘솔 (fs.createwritestream ( './ stdout.log'), fs.createwritestream ( './ stderr.log')); setInterval (function () { logger.log ( 'daemon pid :', process.pid, ', ppid :', process.ppid); }, 1000 * 10);
데몬 프로세스는 node.js 버전 소스 코드 주소를 구현합니다
.
실제 작업에서 우리는 PM2, Egg-Cluster 등과 같은 데몬 프로세스에 대한 낯선 사람이 아닙니다 데몬 프로세스는 여전히 프로세스 예외 모니터링, 작업 프로세스 관리 및 스케줄링, 프로세스가 시작된 후 다시 시작하는 것과 같은 높은 프로세스입니다.
5.process.cwd()
입니까
?process.chdir()
통해 얻을 수있는 부모 프로세스의 디렉토리에서 Spawn 명령에 의해 생성 된 하위 프로세스는 CWD 옵션을 지정하여 하위 프로세스의 작업 디렉토리를 설정할 수 있습니다.
통해
파일을 읽을 때 상대 경로로 설정되면 현재 프로세스가 시작된 디렉토리에 대해 검색됩니다 올바른 결과는 얻지 못합니다. 다른 경우, 프로그램에서 참조 된 타사 모듈은 현재 프로세스가 시작된 디렉토리를 기반으로 검색됩니다.
// process.chdir ( '/user/may/documents/test/') // 현재 프로세스 디렉토리 Console.log를 설정합니다 (process.cwd ());