使用Spring Boot进行SSE开发
春节 Boot 内置了对SSE(text/event-stream)的支持,我超想试试啊。你也是吧。
我们有一个适合您的SseEmitter。
简单的使用方法
每秒返回当前日期和时间的函数。异步执行的本身请参考 https://qiita.com/takkkun/items/1364fdaac8ae2b9fdc01。
@Service
class SseService {
@Async
fun emitDateTime(emitter: SseEmitter) {
var finished = false
while (!finished) {
TimeUnit.SECONDS.sleep(1)
try {
sse.send(LocalDateTime.now().toString())
}
catch (e: IOException) {
finished = true
}
}
}
}
@Controller
class SseController(private val service: SseService) {
@RequestMapping(/* ... */)
fun sse(): ResponseBodyEmitter {
val emitter = SseEmitter()
service.emitDateTime(emitter)
return emitter
}
}
在这个问题上,将会返回以下的响应。
$ curl -i 'http://localhost:8080/sse'
HTTP/1.1 200
Content-Type: text/event-stream;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 20 Jan 2020 07:01:31 GMT
data:2020-01-20T16:01:32.155751
data:2020-01-20T16:01:33.164949
data:2020-01-20T16:01:34.168721
data:2020-01-20T16:01:35.174836
data:2020-01-20T16:01:36.179743
...
超时
默认超时时间由Spring Boot确定。具体而言,通过”spring.mvc.async.request-timeout”属性来设置。单位为毫秒。
或者可以在SseEmitter的构造函数中指定。在这种情况下,构造函数中提供的值将被优先选择。
如果想要无限期,可以指定为0。
如果发生超时,将调用在SseEmitter#onTimeout中指定的回调函数,并抛出AsyncRequestTimeoutException。
断开与客户端的连接
我认为没有办法在客户端断开连接的瞬间立即检测到它。
但是,如果在客户端被断开的情况下调用SseEmitter#send方法,会抛出IOException(实际上应该是其子类ClientAbortException),因此可以通过捕获该异常来检测。
最终会调用SseEmitter#onError指定的回调函数,这样可以吗?
顺便提一下,如果在@Async标注的方法中发生未处理的异常,由于异步处理机制会记录日志,所以最好还是要正确处理异常(在Java中,IOException被认为是受检异常,因此必须强制处理)。
从服务器上完成
当调用SseEmitter#complete时,响应完成。
虽然我认为几乎都会从客户端断开连接,但根据用途不同,也有可能发生,所以只是预防起见。