使用 node.js http2 模块的服务器发送事件 - 我应该如何将它与流 / pushStream 一起使用?

Posted

技术标签:

【中文标题】使用 node.js http2 模块的服务器发送事件 - 我应该如何将它与流 / pushStream 一起使用?【英文标题】:Server-sent events with node.js http2 module - how should I use it with stream / pushStream? 【发布时间】:2019-02-04 18:54:51 【问题描述】:

我正在尝试实现每 500 毫秒发送大量数据(对象,而不是文件)的服务器。 经过一番阅读,服务器通过 http2 发送事件似乎是最快的选择(由于 http2 是二进制协议,SSE 减少了流量开销)

在 http/1.1 上与SSE 玩了一会儿之后 我一直在尝试在 http2 上做同样的事情。我尝试使用stream 和pushStream 这样做,但没有成功。但是,使用与 http/1.1 相同的方式似乎可行。

我的问题是 - 为什么使用流的服务器 1(见下文)不起作用,而服务器 2 似乎工作正常?工作节点流时我错过了什么吗?

我正在使用节点 v10.9.0 和 chrome 68.0.3440.106

我已经阅读了以下问题和帖子,但仍然无法解决这个问题:

Can I stream a response back to the browser using http2? Node.js HTTP2 server Error: socket hang up HTTP2 / SPDY Push-Stream Verification: How to Test? HTTP/2 Server Push with Node.js Using Server Sent Events with express

服务器 1 - 带有流的 http2(不工作 - 客户端没有收到事件。chrome 将请求描述为未完成的请求):

const fs = require('fs');
const http2 = require('http2');

const HTTPSoptions = 
    key: fs.readFileSync('./cert/selfsigned.key'),
    cert: fs.readFileSync('./cert/selfsigned.crt'),
;
const template = `
<!DOCTYPE html> 
<html>
<body>
    <script type="text/javascript">
        const  source = new EventSource('/sse/');
        source.onmessage = function(e)  
            document.body.innerHTML += e.data + '<br>';
        ;
    </script>
</body>
</html>
`;


const server = http2.createSecureServer(HTTPSoptions);

server.on('stream', (stream, headers, flags) => 
    if (stream.url === 'sse/') 
        console.log(stream.respond);
        stream.respond(
            ':status': 200,
            'content-type': 'text/event-stream'
        );
        setInterval(() => stream ? res.write(`data: $Math.random()\n\n`) : '', 200);
    
);

server.on('request', (req, res) => 
    if(req.url === '/') 
        res.end(template);
    
);



server.listen(3001);

服务器 2 - 带有流的 http2(工作正常):

const fs = require('fs');
const http2 = require('http2');

const HTTPSoptions = 
    key: fs.readFileSync('./cert/selfsigned.key'),
    cert: fs.readFileSync('./cert/selfsigned.crt'),
;
const template = `
<!DOCTYPE html> 
<html>
<body>
    <script type="text/javascript">
        const  source = new EventSource('/sse/');
        source.onmessage = function(e)  
            document.body.innerHTML += e.data + '<br>';
        ;
    </script>
</body>
</html>
`;


const server = http2.createSecureServer(HTTPSoptions);

server.on('request', (req, res) => 
    req.socket.setKeepAlive(true);

    if(req.url === '/sse/') 

        res.writeHead(200, 
            'Content-Type': 'text/event-stream', // <- Important headers
            'Cache-Control': 'no-cache'
        );
        res.write('\n');

        setInterval(() => res.write(`data: $Math.random()\n\n`), 200);
     else 
        res.end(template);
    
);

server.listen(3001);

【问题讨论】:

【参考方案1】:

要获取 http2 流的请求路径,您必须查看 :path 标头 (docs):

if (headers[':path'] === '/sse/') 

您还尝试使用res.write,而res 应该是stream

这是一个基于“服务器 1”实现的工作处理函数:

server.on('stream', (stream, headers, flags) => 
    if (headers[':path'] === '/sse/') 
        stream.respond(
            ':status': 200,
            'content-type': 'text/event-stream'
        );
        setInterval(() => stream.write(`data: $Math.random()\n\n`), 200);
    
);

【讨论】:

以上是关于使用 node.js http2 模块的服务器发送事件 - 我应该如何将它与流 / pushStream 一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Node.js 通过代理发送 HTTP/2 请求?

Node js - http2 的捆绑器

Node.js HTTP2 服务器错误:套接字挂起

Node JS:双向 GRPC 调用会打开多个 http2 连接吗?

node.js HTTP模块URL 模块

使用请求模块在 Node.js 中发送 URL 编码的参数