Spring SSEEmitter中途完成

时间:2016-03-30 11:39:15

标签: html5 spring spring-mvc server-sent-events

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

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

我在这里尝试一个简单的事情..用户点击按钮订阅.. 服务器无论何时向浏览器发送响应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;
}

公共类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>

2 个答案:

答案 0 :(得分:2)

您的建造者未被使用;您创建并配置构建器,但随后直接发送带有'sseEmitter.send'的简单消息。试试这个:

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

还有一件事:为什么你在subscribe方法中已经调用了send方法?此时,尚未返回SseEmitter。此消息是否传递给客户?

这是一个excellent article从JavaScript角度解释SSE(不是Spring)。您将在此处看到,您可以通过在流上调用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,但我认为这是错误的。收到的对象eEvent。您需要从中获取EventSource对象:e.currentTarget

答案 1 :(得分:1)

您需要使用 SseEmitter 的第二个构造函数,该构造函数接受 Long timeout 参数。请参考以下代码 -

@RequestMapping("/subscribe")
public SseEmitter subscribe() {
  SseEmitter sseEmitter = new SseEmitter(Long.MAX_VALUE) // for maximum timeout

以下是此构造函数的Java-doc副本 -

/**
 * Create a SseEmitter with a custom timeout value.
 * <p>By default not set in which case the default configured in the MVC
 * Java Config or the MVC namespace is used, or if that's not set, then the
 * timeout depends on the default of the underlying server.
 * @param timeout timeout value in milliseconds
 * @since 4.2.2
 */

我认为Tomcat中SSE连接的默认超时是40秒。但不确定。