几次尝试后 SSE 服务器停止响应

Posted

技术标签:

【中文标题】几次尝试后 SSE 服务器停止响应【英文标题】:SSE server stops responding after a few attempts 【发布时间】:2020-04-02 19:08:54 【问题描述】:

当我尝试在 express 上执行 SSE 时,服务器每次尝试 5 次后都会停止响应。 我希望它可以无限期地正常工作。

如果我在一段时间后离开页面,则会出现此错误:

“POST http://localhost:3000/api/updatenet::ERR_EMPTY_RESPONSE”

在服务器上:

"MaxListenersExceededWarning: 可能的 EventEmitter 检测到内存泄漏。 11 个消息监听器添加到 [EventEmitter]。 使用emitter.setMaxListeners() 增加限制"

这两个错误都不会立即出现。

我尝试更改标头并发送不同的状态,但除了破坏正常工作之外没有任何效果。

总的来说,我对表达和节点还很陌生,我真的不知道我在这里做错了什么。

我的服务器是这样设置的:

app.get("/api/update", (req, res, next) => 
    res.status(200).set(
        "Content-Type": "text/event-stream; charset=utf-8",
        "Cache-Control": "no-cache, no-transform",
        "Transfer-Encoding": "chunked",
        Connection: "keep-alive"
    );
    app.on("message", data => 
        res.write(`data: $JSON.stringify(data)\n\n`);
    );
);

app.post("/api/update", (req, res, next) => 
    const message = req.body.type;
    app.emit("message", 
        title: "Update",
        message,
        timestamp: new Date()
    );
);

我的客户可以这样近似:

import React, Component from "react";

class Button extends Component 
    source = new EventSource("api/update");

    messageHandler = event => 
        console.log(event.data);
    ;

    componentDidMount = () => 
        this.source.onmessage = this.messageHandler;
    ;

    render = () => (
        <button
            onClick=() => 
                fetch("/api/update", 
                    method: "POST",
                    headers: 
                        Accept: "application/json",
                        "Content-Type": "application/json"
                    ,
                    body: JSON.stringify(type: "button")
                );
            
        />
    );


export default Button;

【问题讨论】:

***.com/questions/9768444/… 【参考方案1】:

这部分:

class Button extends Component 
    source = new EventSource("api/update");

是原因:您一次最多可以同时拥有 5 或 6 个 EventSource 连接。 (SSE(EventSource): why no more than 6 connections?)

通常,您在每次渲染时都会打开新的 EventSource,而不会关闭旧的。每次渲染都会打开新实例。在旧连接关闭之前,您不应该打开新连接。

我使用这种方法: 1. 将您的 EventSource 侦听器存储在 useRef 中,该侦听器保存在所有渲染中。 2. 在你的监听函数上使用回调

const evtSrc = useRef(null)
const listenEvt = useCallback(() =>  
  if (!evtSrc.current) 
   evtSrc.current = new EventSource("api/update");
  
 , [])
    然后您要求使用 useEffect 挂钩在挂载时创建新的 EventSource 连接,并在每次卸载时关闭它:
    useEffect(() => 
    listenEvt() // componentDidMount
    return () => evtSrc.current.close() // componentDidUnmount
    

希望这会有所帮助。

【讨论】:

onmessage方法应该放在哪里?在 evtSrc.current = new EventSource("api/update");? 之后进入 useCallback 钩子? 是的,在那个函数中你定义了所有的 EventSource 方法。 我设法解决了这个问题,但不幸的是没有使用这个答案:我注意到我只是在发布到服务器时忘记关闭连接。问题出在服务器端,而不是客户端。您所说的重新渲染在类组件中不是正确的,而仅在功能组件中是正确的。无论如何,感谢您花时间回答我的问题。【参考方案2】:

我设法解决了这个问题:我在发布到服务器时忘记关闭连接。 这个:

app.post("/api/update", (req, res, next) => 
    const message = req.body.type;
    app.emit("message", 
        title: "Update",
        message,
        timestamp: new Date()
    );
);

变成这样:

app.post("/api/update", (req, res, next) => 
    const message = req.body.type;
    app.emit("message", 
        title: "Update",
        message,
        timestamp: new Date()
    );
    res.end();
);

【讨论】:

以上是关于几次尝试后 SSE 服务器停止响应的主要内容,如果未能解决你的问题,请参考以下文章

socket.io 在 x 秒/第一次尝试获取响应失败后停止重新发出事件

响应失败后如何停止http响应

如何停止主 ui 线程,直到我得到来自 http 请求的响应

WCF 在大约 10 次左右调用后停止响应(限制)

Docker服务突然停止响应

显示器驱动程序已停止响应 并且已成功恢复 这是啥原因