在Spring Boot Webflux中生成服务器发送事件。

huangapple go评论85阅读模式
英文:

Producing Server Sent Events in Spring Boot Webflux

问题

I have the following code in a SpringBoot application:

class MySse {
    public Mono<ServerResponse> emitEvents(ServerRequest request){
        return ServerResponse.ok()
                .contentType(MediaType.TEXT_EVENT_STREAM)
                .body(Mono.from(Flux.interval(Duration.ofSeconds(1))
                .map(sequence  -> ServerSentEvent.builder()
                        .id(String.valueOf(sequence))
                        .event("periodic-event")
                        .data("SSE - " + LocalTime.now().toString())
                        .build())), ServerSentEvent.class);
    }
}

@Configuration
public class MyRoutesConfiguration {

    @Autowired
    @Bean
    RouterFunction<ServerResponse> sseRoute(MySse mySse) {
        return route(path("/sse").and(method(HttpMethod.GET)), MySse ::emitEvents);
    }

    @Bean
    public MySse mySse() {
        return new MySse();
    }
}

If I navigate to http://localhost (route not shown above but it works)
from there I open DevTools in Chrome and type in the following JavaScript code:

const evtSource = new EventSource("sse/");
evtSource.onmessage = function(event) {
  console.log(event.data);
}

But nothing is printed out...

Breakpoints in the map(...) lambda in MySse::emitEvent are hit every second

But nothing is printed out in the browser's JS console.

if I visit http://localhost/sse I get the following response:

id:0
event:periodic-event
data:SSE - 20:17:12.972706400
英文:

I have the following code in a SpringBoot application:

class MySse {
    public Mono&lt;ServerResponse&gt; emitEvents(ServerRequest request){
        return ServerResponse.ok()
                .contentType(MediaType.TEXT_EVENT_STREAM)
                .body(Mono.from(Flux.interval(Duration.ofSeconds(1))
                .map(sequence  -&gt; ServerSentEvent.builder()
                        .id(String.valueOf(sequence))
                        .event(&quot;periodic-event&quot;)
                        .data(&quot;SSE - &quot; + LocalTime.now().toString())
                        .build())), ServerSentEvent.class);
    }
}

@Configuration
public class MyRoutesConfiguration {

    @Autowired
    @Bean
    RouterFunction&lt;ServerResponse&gt; sseRoute(MySse mySse) {
        return route(path(&quot;/sse&quot;).and(method(HttpMethod.GET)), MySse ::emitEvents)
                ;
    }

    @Bean
    public MySse mySse() {
        return new MySse();
    }
}

If I navigate to http://localhost (route not shown above but it works)
from there I open DevTools in Chrome and type in the following JavaScript code:

const evtSource = new EventSource(&quot;sse/&quot;);
evtSource.onmessage = function(event) {
  console.log(event.data);
}

But nothing is printed out...

Breakpoints in the map(...) lambda in MySse::emitEvent are hit every second

But nothing is printed out in the browswer's JS console.

if I visit http://localhost/sse I get the following response:

id:0
event:periodic-event
data:SSE - 20:17:12.972706400

答案1

得分: 5

public Mono<ServerResponse> emitEvents(ServerRequest request) {
    return ServerResponse.ok()
            .contentType(MediaType.TEXT_EVENT_STREAM)
            .body(BodyInserters.fromServerSentEvents(Flux.interval(Duration.ofSeconds(1))
                    .map(aLong -> ServerSentEvent.<String>builder()
                            .id(String.valueOf(aLong))
                            .event("periodic-event")
                            .data("SSE - " + LocalTime.now().toString())
                            .build())));
}

如果您想要流式传输数据,您需要返回一个 FluxMono 表示一个项目,而 Flux 表示0...n个项目。您触发了 Flux 以每秒的间隔开始发出事件,只要连接保持打开状态,它将每秒向调用客户端发出一个事件。

您可以尝试使用 curl(或者您正在使用的 Chrome 控制台),但如果您使用 curl,则需要使用 -N 标志禁用 curl 的缓冲策略。

curl -N http://localhost:8080/sse

<details>
<summary>英文:</summary>

    public Mono&lt;ServerResponse&gt; emitEvents(ServerRequest request){

        return ServerResponse.ok()
                .contentType(MediaType.TEXT_EVENT_STREAM)
                .body(BodyInserters.fromServerSentEvents(Flux.interval(Duration.ofSeconds(1))
                .map(aLong -&gt; ServerSentEvent.&lt;String&gt;builder()
                                .id(String.valueOf(aLong))
                                .event(&quot;periodic-event&quot;)
                                .data(&quot;SSE - &quot; + LocalTime.now().toString())
                                .build())));
    }

if you want to stream the data you need to return a `Flux`. A `Mono` is **ONE** item, a `Flux` is `0...n` items. You trigger the `Flux` to start emitting on each second interval and as long as the connection is open it will emit an event back to the calling client each second.

you can try it out with for example curl (or the chrome console as you are using) but you need to disable the curl buffering strategy using the `-N` flag if you are using curl.

curl -N http://localhost:8080/sse


</details>



huangapple
  • 本文由 发表于 2020年8月3日 03:19:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/63220120.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定