我想知道如何使用fork将请求响应对象从父进程传递到子进程。 我做的是
var child = cp.fork(__dirname + '/childProcess.js', req, res);
child.on('message', function(m)
{
console.log("child process returned data " + m);
});
child.send("hello");
childProcess.js
var req = process.argv[2];
var res = process.argv[3];
process.on('message', (msg) =>
{
console.log("req object :-" + req );
console.log("res object :-" + res);
}
process.send("callparent");
这让我在子进程中未定义。我也试过了child.send("你好",req.socket)。但后来我无法访问子进程中的req方法。它显示为圆形结构。
答案 0 :(得分:2)
根据docs:
child.send(message [,sendHandle] [,callback])
可以传递给
sendHandle
的可选child.send()
参数用于将TCP服务器或套接字对象传递给子进程。子进程将接收该对象作为传递给process.on('message')
事件上注册的回调函数的第二个参数。
这表明您应该在req
函数中发送req.socket
或child.send()
对象。
此外,您没有从注册到req
事件的函数中的第二个参数访问req.socket
或process.on('message')
对象。
答案 1 :(得分:1)
您创建服务器,并将句柄发送到客户端。 例如,如果您使用快递,则可以执行以下操作:
<强> Server.js 强>
child= require('child_process').fork('child.js');
const server = require('net').createServer();
server.on('connection', function(socket) {
child.send('handle', socket);
}).listen(80);
然后你创建一个孩子,并接受句柄:
<强> Child.js 强>
var app=require('express')()
app.get('/',function(req,res){
console.log(req.headers)
res.send('Working!')
})
var myServer=require('http').createServer(app)
process.on('message', function(m, server) {
if (m === 'handle') myServer.listen(handle)
});
更多信息:
答案 2 :(得分:1)
其他答案告诉您将套接字发送给孩子并让孩子听取并处理请求,这不是问的问题(如何向孩子发送req / res)。如果您的目标是将套接字传递给子进程并让孩子监听并处理请求,那么这些答案就可以了。如果您的目标是在主进程中侦听和预处理请求,并将这些请求传递给子进程以进行进一步处理,请参阅以下内容:
简短的回答是,您必须实际明确地将请求和响应发送到子进程(它们不会神奇地出现在子进程中)。但是这有一些挑战。
在我们的应用程序中,我们遇到了类似的问题。我们的主要请求处理器检查请求以查看哪个子进程应处理每个请求,然后将该请求发送到子进程(通过child.send()
)。
在父和子之间传递请求/响应的问题是你必须能够序列化使用send
来回发送的对象,特别是请求对象不能被序列化(它是满的循环引用,引用了许多其他对象等。)
所以我们所做的是从子进程需要的请求中提取数据子集,并通过child.send()
将“轻量级请求”对象发送到子进程,同时保留对原始数据的引用在父进程的队列中请求。当子进程完成处理时,它会通过parent.send()
将其响应数据发送回父进程,并且父进程将其与暂挂队列中的原始请求进行匹配,并使用响应数据满足请求。
这种方法的优点(与在父/子之间制作自定义协议相反)是子请求处理代码仍然在请求/响应对象上运行(只是它们的轻量级版本)。在我们的例子中,它意味着我们可以使用相同的请求处理代码,无论我们是在子进程中调用它,还是让进程监听并直接处理请求。
例如,以下是如何创建一个轻量级,可序列化的请求对象并将其发送到子进程:
var requestData =
{
httpVersion: request.httpVersion,
httpVersionMajor: request.httpVersionMajor,
httpVersionMinor: request.httpVersionMinor,
method: request.method,
url: request.url,
headers: request.headers,
body: request.body
}
child.send(requestData);
您也可以使用lodash pick之类的内容来抓取您想要的字段。
使用此解决方案的棘手部分是,如果您需要在请求/响应上调用任何方法,则必须在父级中执行此操作(实际对象所在的位置 - 所有子级都具有所选数据)。
答案 3 :(得分:0)
以下示例可能有助于更好地说明这一点。 它有一个父项和一个子项,父项创建一个服务器,在客户端连接时,套接字句柄被传递给子项。
孩子只有一个消息循环,在其中接收句柄,并且能够通过它接触到客户端。
$ cat foo.js
const cp = require('child_process')
const n = require('net')
if (process.argv[2] === 'child') {
process.on('message', (m, h) => {
h.end('ok')
})
}
else {
const c = cp.fork(process.argv[1], ['child'])
const s = n.createServer();
s.on('connection', (h) => {
c.send({}, h)
}).listen(12000, () => {
const m = n.connect(12000)
m.on('data', (d) => {
console.log(d.toString())
})
})
}
围绕握手的相关部分进行分析。
[pid 12055] sendmsg(11, {msg_name(0)=NULL, msg_iov(1)=[{"{\"cmd\":\"NODE_HANDLE\",\"type\":\"net.Socket\",\"msg\":{},\"key\":\"6::::12000\"}\n", 70}], msg_controllen=24, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, {14}}, msg_flags=0}, 0) = 70
[pid 12061] recvmsg(3, {msg_name(0)=NULL, msg_iov(1)=[{"{\"cmd\":\"NODE_HANDLE\",\"type\":\"net.Socket\",\"msg\":{},\"key\":\"6::::12000\"}\n", 65536}], msg_controllen=24, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, {12}}, msg_flags=MSG_CMSG_CLOEXEC}, MSG_CMSG_CLOEXEC) = 70
[pid 12061] getsockname(12, {sa_family=AF_INET6, sin6_port=htons(12000), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
[pid 12061] getsockopt(12, SOL_SOCKET, SO_TYPE, [1], [4]) = 0
[pid 12061] write(3, "{\"cmd\":\"NODE_HANDLE_ACK\"}\n", 26) = 26
[pid 12055] <... epoll_wait resumed> {{EPOLLIN, {u32=11, u64=11}}}, 1024, -1) = 1
[pid 12055] recvmsg(11, {msg_name(0)=NULL, msg_iov(1)=[{"{\"cmd\":\"NODE_HANDLE_ACK\"}\n", 65536}], msg_controllen=0, msg_flags=MSG_CMSG_CLOEXEC}, MSG_CMSG_CLOEXEC) = 26
如您所见,父和子参与传输套接字句柄的协议,子进程从这样接收的句柄信息中重新创建net.Socket对象。随后,孩子能够处理此连接下的客户端。
这个协议,以及跨进程传输句柄和工作量的方式,是cluster
模块的核心和灵魂。
答案 4 :(得分:0)
您不能(至少在 Node.js <= 15.13.0 中不能)将 HTTP 请求或响应对象传递给子进程。因此我想出了一个使用 IPC 的解决方案,我将在这篇文章中分享。
首先使用此内容创建一个 package.json
文件:
{
"type": "module"
}
然后使用以下内容创建文件 server.js
:
import * as http from 'http'
import * as child_process from 'child_process'
import * as net from 'net'
import * as fs from 'fs'
import * as crypto from 'crypto'
const ipcPrefix = (process.platform != 'win32' ? '/tmp/' : '\\\\.\\pipe\\')
+ crypto.randomBytes(8).toString('hex')
const subprocess = child_process.fork('subprocess.js', {
stdio: ['pipe', 'pipe', 'inherit', 'ipc']
})
let requestNumber = 0
const server = http.createServer()
.listen(8080)
.on('request', (request, response) => {
if (!subprocess.connected) {
console.error('Subprocess not connected for: '+request.url)
response.statusCode = 500
response.end()
return
}
const {headers, url, method} = request
const ipcPath = ipcPrefix + requestNumber++ // a unique IPC path
try {fs.unlinkSync(ipcPath)} catch{} // delete the socket file if it exist already
let headerChunks = [], headerSize, sizeGotten = 0
net.createServer() // the IPC server
.listen(ipcPath, () => subprocess.send({cmd: 'req', headers, url, method, ipcPath}))
.on('connection', socket => {
socket.on('close', () => {
response.end()
fs.unlinkSync(ipcPath) // clean it up
})
socket.once('data', async chunk => {
headerSize = chunk.readUInt32LE()
headerChunks.push(chunk)
sizeGotten += chunk.byteLength
while (sizeGotten < headerSize) {
let timer
const timedOut = await Promise.race([
// race next packet or timeout timer
new Promise(resolve => {
socket.once('data', chunk => {
headerChunks.push(chunk)
sizeGotten += chunk.byteLength
resolve(false)
})
}),
new Promise(resolve => {timer = setTimeout(resolve, 2000, true)})
])
clearTimeout(timer) // to not leave it hanging
if (timedOut) {
response.statusCode = 500
// response.write('lol, bye') // optional error page
socket.destroy() // this causes the close event which ends the response
console.error('Subprocess response timed out...')
return // break the loop and exit function
}
}
const data = Buffer.concat(headerChunks)
const headerData = data.slice(4, 4+headerSize)
const header = JSON.parse(headerData.toString())
response.writeHead(header.statusCode, header.headers)
if (data.byteLength - 4 - headerSize > 0) {
response.write(data.slice(4 + headerSize))
}
socket.pipe(response) // just pipe it through
//socket.on('data', response.write.bind(response)) // or do this
// but set a timeout on the socket to close it if inactive
socket.setTimeout(2000)
socket.on('timeout', () => {
socket.destroy()
console.log('Subprocess response timed out...')
})
})
})
})
然后创建文件 subprocess.js
。与此内容:
import * as net from 'net'
process.on('message', msg => {
switch (msg.cmd) {
case 'req': {
const socket = net.createConnection({path: msg.ipcPath})
// depending on {headers, url, method} in msg do your thing below
writeHead(socket, 200, {'testHeader': 'something'})
socket.end('hello world '+msg.url)
} break
}
})
function writeHead(socket, statusCode, headers) {
const headerBuffer = new TextEncoder().encode(JSON.stringify({statusCode, headers}))
const headerSize = Buffer.allocUnsafe(4)
headerSize.writeUInt32LE(headerBuffer.byteLength)
socket.write(headerSize)
socket.write(headerBuffer)
}
现在运行服务器 node server
并在浏览器中转到 localhost:8080
以查看其运行情况。
如果您喜欢我的回答,请随时在 GitHub 上赞助我。