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

SSE server stops responding after a few attempts

当我尝试在 express 上执行 SSE 时,服务器每次都在恰好 5 次尝试后停止响应。 我想让它无限期地工作。

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

"POST http://localhost:3000/api/update net::ERR_EMPTY_RESPONSE"

在服务器上:

"MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 message listeners added to [EventEmitter]. Use emitter.setMaxListeners() to increase limit"

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

我尝试更改 headers 并发送不同的状态,但没有任何效果,只是打破了正在运行的状态。

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

我的服务器 set-up 是这样的:

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;

这部分:

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

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

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

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

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

希望这对您有所帮助。

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

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();
});