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

时间:2019-12-09 16:28:29

标签: javascript node.js reactjs express server-sent-events

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

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

  

“ POST http://localhost:3000/api/update net :: ERR_EMPTY_RESPONSE”

并在服务器上:

  

“ MaxListenersExceededWarning:可能的事件发射器   检测到内存泄漏。 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;

2 个答案:

答案 0 :(得分: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.在您的监听函数上使用useCallback

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
    }

希望这会有所帮助。

答案 1 :(得分:0)

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

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