html - Spring SSEEmitter 中途完成

标签 html spring spring-mvc server-sent-events

我是服务器发送事件的新手,但不是 Spring 的新手。 制作了一个 Controller ,该 Controller 从 UI 上的按钮触发,该按钮启动 SSEEmitter 并将其传递给另一个线程,该线程在每 4 秒后循环向 UI 发送消息。 到目前为止,我正在运行一个 10 循环,每个循环休眠 4 秒,但突然在第 6 或第 7 循环的迭代周围,我得到异常“线程“Thread-4”java.lang.IllegalStateException 中的异常:ResponseBodyEmitter 已设置完成”。 .

因此,事件源再次重新建立连接,即再次调用 Controller 方法,这当然是我不想要的。

我在这里尝试一件简单的事情.. 用户通过点击按钮进行订阅.. 服务器向浏览器发送响应 10 或 20。据我所知,这就是 SSE 创建的目的。

代码如下:

@RequestMapping("/subscribe")
public SseEmitter subscribe() {
    SseEmitter sseEmitter = new SseEmitter();
    try {
        sseEmitter.send("Dapinder");
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    Runnable r = new AnotherThread(sseEmitter);
    new Thread(r).start();
    return sseEmitter;
}

公共(public)类 AnotherThread 实现 Runnable {

private SseEmitter sseEmitter;

public AnotherThread(SseEmitter sseEmitter) {
    super();
    this.sseEmitter = sseEmitter;
}

@Override
public void run() {
    SseEventBuilder builder = SseEmitter.event();
    builder.name("dapEvent");
    for (int i = 0; i < 10; i++) {
        builder.data("This is the data: " + i +" time.");
        try {
            //sseEmitter.send(builder);
            sseEmitter.send("Data: "+i);
            //sseEmitters.get(1L).send("Hello");
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    sseEmitter.complete();
}

public SseEmitter getSseEmitter() {
    return sseEmitter;
}

public void setSseEmitter(SseEmitter sseEmitter) {
    this.sseEmitter = sseEmitter;
}

function start() {
    var eventSource = new EventSource("http://localhost:8080/HTML5SSE/springSSE/subscribe"); //  /springSSE/connect
      eventSource.onmessage = function(event) {
        document.getElementById('foo').innerHTML = event.data;
    };

}


<button onclick="start()">Subscribe</button>

最佳答案

您的构建器未被使用;您创建并配置了一个构建器,但随后您直接使用“sseEmitter.send”发送了一条普通消息。试试这个:

sseEmitter.send(SseEmitter.event().name("dapEvent").data("This " + i +" time.");

还有一件事:为什么在订阅方法中已经调用了发送方法?此时,SseEmitter 还没有返回。此消息是否传递给客户端?

这是一个 excellent article从 JavaScript 的角度(不是 Spring)解释 SSE。您将在此处看到,您可以通过在流上调用 close 来取消来自客户端的事件流。将其与事件监听器结合使用,您应该拥有所需的东西:

var source = new EventSource('...');
source.addEventListener('error', function(e) {
  if (e.currentTarget.readyState == EventSource.CLOSED) {
    // Connection was closed.
  } else {
    // Close it yourself
    source.close();
  }
});
source.addEventListener('message', function(e) {
  console.log(e.data);
});

注意:文章说e.readyState,但我认为这是错误的。接收到的对象 e 是一个 Event。您需要像这样从中获取 EventSource 对象:e.currentTarget

关于html - Spring SSEEmitter 中途完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36307719/

相关文章:

html - 有没有办法在 html 电子邮件中的 "a:hover"上的 href 链接上设置内联样式?

html - CSS 网格 - 没有媒体查询的最大列数

html - 在下拉菜单中使用 <a> 标签或 <button> 标签 |自举

spring - 由于标准 header ,CORS 预检请求失败

java - 当应用程序启动时,在 spring 中初始化一个方法一次的最佳方法是什么?

html - 从 URL (PATH) 下载 - iPhone

java - 如何设置对象的状态

java - 为什么会出现这个未处理的 org.apache.tiles.impl.CannotRenderException?

java - 构建一个接受任何类型请求的休息包装器

java - 如何最好地将 JTidy 与 Spring servlet 容器一起使用?