HTML5 EventSource 发送多个请求

11t*_*ion 4 javascript java spring eventsource

我正在尝试使用 Spring SseEmitter实现服务器发送事件,如此Youtube 视频中所述

我能够启动事件流并从服务器发送的事件接收数据。

但是,我可以看到EventStream从客户端发出并到达服务器的多个类型的请求。我理解它的方式,EventSource应该发送一个HTTP请求,然后应该保持half duplex与服务器的连接,使用哪个服务器将事件发送到客户端。

为什么它会定期发送请求?是不是就像轮询而不是半双工连接?

波纹管是我正在使用的代码。

服务器代码

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter.SseEventBuilder;

@RestController
public class TestService {
    private List<SseEmitter> subscriberList = Collections.synchronizedList(new ArrayList<>());

    @RequestMapping("inbox")
    public SseEmitter inbox() {
        SseEmitter subscriber = new SseEmitter();
        subscriberList.add(subscriber);

        subscriber.onCompletion(() -> {
            subscriberList.remove(subscriber);
            System.out.println("Removed the completed event emitter");
        });

        System.out.println("Subscriber arrived");
        return subscriber;
    }

    @RequestMapping("message")
    public String message(@RequestParam("message") String message) {
        System.out.println("SubscriberList size " + subscriberList.size());
        for(SseEmitter subscriber : subscriberList) {
            try {
                SseEventBuilder eventBuilder = SseEmitter.event().name("group1").data(message);
                subscriber.send(eventBuilder);
            } catch (Exception e) {
                e.printStackTrace();
            }   
        };
        return message;
    }
}
Run Code Online (Sandbox Code Playgroud)

客户端代码

$(function () {
    console.log("Started");
    var eventSource = new EventSource("/inbox");
    eventSource.addEventListener('error', function(e) {
      if (e.currentTarget.readyState == EventSource.CLOSED) {
          console.log("Connection is closed")
      } else {
        source.close();
        console.log("Closing connection");
      }
    });
    eventSource.addEventListener("group1", function (event) {
        console.log(event.data);
        document.querySelector("body").innerHTML += "<div>" + event.data + "</div>";
    });
});
Run Code Online (Sandbox Code Playgroud)

Bellow 是来自 chrome 的客户端网络选项卡屏幕截图

在此处输入图片说明

这是服务器端日志

Subscriber arrived
Removed the completed event emitter
Subscriber arrived
Removed the completed event emitter
Subscriber arrived
Removed the completed event emitter
Subscriber arrived
Removed the completed event emitter
Subscriber arrived
Removed the completed event emitter
Subscriber arrived
Removed the completed event emitter
Subscriber arrived
Run Code Online (Sandbox Code Playgroud)

Tom*_*mer 5

这里的问题是服务器意外地关闭了连接,而不是在保持打开状态时继续工作。当这种情况发生时,客户端重新发送请求以打开连接并开始流式传输服务器发送的事件。然后服务器一次又一次地关闭连接,导致无限循环。

确保这种情况的一种方法是设置该retry字段以增加浏览器的等待时间(默认值约为 2-3 秒)。另一种方法是while (true) {}在服务器端有一个,就在请求到达之后。

另请查看有关 SSE 的这篇文章