使用 Xamarin 使用 Spring SSE

consume Spring SSE with Xamarin

我有一个 spring 启动后端,我想为其实现一个 SSE 端点。我想使用基于 Xamarin Forms 的应用程序使用此端点。

我成功地为双方实现了一些例子,但是,我没有成功地在应用程序上收到任何消息。

对于后端部分,我实现了以下示例:

    @RequestMapping(value = "/event-stream", method = RequestMethod.GET)
    public ResponseEntity<ResponseBodyEmitter> streamEvents() {
        ResponseBodyEmitter emitter = new ResponseBodyEmitter();
        executor.execute(() -> {
            try {
                for (int i = 0; i < 30; i++) {
                    Thread.sleep(2000);
                    var msg = new ResponseTestObject("this is a message from " + new Date(), i);
                    emitter.send(msg, MediaType.APPLICATION_JSON);
                }
                emitter.complete();
            } catch (Exception ex) {
                emitter.completeWithError(ex);
            }
        });
        return new ResponseEntity<>(emitter, HttpStatus.OK);
    }
    @AllArgsConstructor
    public static class ResponseTestObject {
        public String message;
        public int id;
    }

注意:我有意以达到默认 30 秒超时的方式实现它。使用 postman 调用此方法,它将加载所述 30 秒并同时显示所有已发送的消息:

{
    "message": "this is a message from Thu May 05 11:36:25 CEST 2022",
    "id": 0
}{
    "message": "this is a message from Thu May 05 11:36:27 CEST 2022",
    "id": 1
}
[...]
{
    "message": "this is a message from Thu May 05 11:36:51 CEST 2022",
    "id": 13
}{
    "message": "this is a message from Thu May 05 11:36:53 CEST 2022",
    "id": 14
}

在我的应用程序部分,我使用了 ServiceStack ServerEventsClient:

EventClient = new ServerEventsClient(BackendConnector.BACKEND_HOST + "/events/") {
    OnMessage = OnMessage,
    OnException = (ex) => {
        Console.WriteLine("OnException: " + ex.Message);
    }
};
// another REST backend connection is made previously in the app and I use its session cookie for authentication
EventClient.ServiceClient.SetCookie(BackendConnector.SESSION_COOKIE_VALUE, BackendConnector.BackendSessionCookieId);
EventClient.Start();

在我启动客户端后,我让它定期post这样的状态:

Console.WriteLine("SSE " + EventClient.Status);

我能看到的是:

  1. 后端的“订阅”有效。我可以看到请求,它确实启动了 运行 后端线程
  2. 状态日志显示,进入“正在启动”后不久,状态变为“已启动”
  3. “开始”状态将保持这种状态,直到达到 30 秒的超时时间。然后它重新连接并继续循环
  4. 永远不会调用 OnMessage 方法,即使在超时后也不会,就像我在 postman
  5. 中看到的那样

附加说明:我还有一个使用 SseEmitter 的服务器端测试实现。在这种情况下,我也可以在“订阅”期间看到应用程序请求,但它会在某个时候超时并出现异常,并且客户端状态永远不会离开“开始”

我的第一个问题显然是:我错过了什么,我永远不会收到消息?

我的第二个问题是:为什么它首先会超时?根据文档,它会定期发送心跳。我需要在 spring 方面采取不同的方法吗?或者我是否需要为心跳实现一个单独的端点?

感谢您的帮助!


编辑:

按照@mythz 的回答并接受在这里使用 ServiceStack 不是一个好主意,我按照以下示例并使用标准 HttpClient 和 StreamReader 实现了一种简单的方法: https://makolyte.com/event-driven-dotnet-how-to-consume-an-sse-endpoint-with-httpclient/

但是,这导致了我之前使用 postman 或我的浏览器遇到的相同问题。 30 秒超时后,所有消息将批量发送。 因此我也改变了后端部分并使用 spring web flux 代替:

public Flux<ServerSentEvent<String>> streamEvents() {
        return Flux.interval(Duration.ofSeconds(1))
                .map(sequence -> {
                    var msg = new ResponseTestObject("this is a message from " + new Date(), Math.toIntExact(sequence));
                    ObjectMapper mapper = new ObjectMapper();
                    try {
                        return ServerSentEvent.<String>builder()
                                .id(String.valueOf(sequence))
                                .event("periodic-event")
                                .data(mapper.writeValueAsString(msg))
                                .build();
                    } catch (JsonProcessingException e) {
                        return null;
                    }
                });
}

我强烈建议您不要将 ServiceStack 的 ServerEventsClient 与 ServiceStack 的 Server Events Feature 以外的任何东西一起使用,这是它所有类型化的服务器事件客户端设计的目的。

例如为了启用断开的网络连接和 auto-retry 连接功能,客户端会定期发回心跳。这只是 ServiceStack 实现中的一个特性,因为 SSE 标准中没有这样的概念。

这只是一个示例,基本上 C# Server Events Client 中的每个 high-level 都使用了 ServiceStack 服务器功能,这些功能在任何其他第 3 方实现中都不存在。