使用Spring的服务器发送的事件


在这里,我们将讨论使用Spring将单向异步事件发送到任何Web应用程序。您可以在Spring中使用SseEmitter类发送单向事件。已经有一种流行的解决方案可用于使用Websocket发送双向事件。使用WebSocket,服务器和客户端都可以使用客户端和服务器之间的双向连接相互通信。SSE仅用于使用HTTP协议从服务器向客户端发送单向事件。

需要具备Spring和Java线程的基础知识。

上证所涉及的步骤:

  1. 客户端打开一个HTTP连接
  2. 服务器可以异步发送任何数量的消息到该连接
  3. 服务器可以关闭连接,也可以由于某些网络错误或服务器端的任何异常而关闭连接。
  4. 如果由于服务器错误或网络错误而关闭连接,客户端将自动尝试重新连接

Events 服务器可以在关闭连接之前发送多个事件。服务器发送的消息应基于文本,并且消息以关键字开头,后跟冒号(:),然后是字符串消息。“数据”是代表客户消息的关键字。

data: message1
data: message2
data: message3
data: message4

如果是多条消息,则消息应用空白行分隔,否则客户端会将它们视为单个事件。在上述情况下,所有4条消息都将被视为一个事件。当您在Spring中发送多条消息时,用于发出消息的org.springframework.web.servlet.mvc.method.annotation包中的SseEmitter类将处理下一行和关键字格式。您还可以将消息中的对象作为JSON字符串发送,客户端可以解析该字符串

data:{"message":"message1","userId":"1"}
data:{"message":"message2","userId":"2"}
data:{"message":"message3","userId":"3"}

使用SseEmitter创建事件 在Spring中,您可以创建一个异步控制器,该控制器将向客户端发出多个消息。您可以创建一个普通的GET类型控制器,该控制器也可以接受来自客户端的多个查询参数。

import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@GetMapping("/emitter")
public SseEmitter eventEmitter(@RequestParam String userId) {
   SseEmitter emitter = new SseEmitter(12000); //12000 here is the timeout and it is optional   

   //create a single thread for sending messages asynchronously
   ExecutorService executor = Executors.newSingleThreadExecutor();
   executor.execute(() -> {
       try {
           for (int i = 0; i < 4; i++) {
              emitter.send("message" + i);           
           }       
       } catch(Exception e) {
            emitter.completeWithError(e);       
       } finally {
            emitter.complete();       
       }   
   });
   executor.shutdown();
   return emitter;
}

在这里,首先,您必须创建一个SseEmitter对象,该对象具有可选的超时作为构造函数中的参数。

在这里,我们创建了一个线程并从中发送4条消息。您也可以从多个不同的线程发送多个消息。您可以在不同的线程中调用多个API,并异步发出这些API的响应。

如果发生错误,则可以使用完整的错误 方法发送特殊事件。这将指示客户端有关错误的信息,我们稍后将检查如何处理客户端中的错误。

最后,在发送完所有消息之后,您必须通过调用complete方法关闭连接来完成发射器。

调用此控制器的输出将是多个消息,并用空行分隔,以便客户端将它们视为单独的事件:

data: message0
data: message1
data: message2
data: message3

客户端事件处理 您可以在任何JavaScript库或框架中处理事件,例如react,angular等。

SSE是单向的,但是客户端也可以通过使用新的请求参数打开单独的连接来与服务器通信。

为了处理事件,客户端可以使用服务器API的URL打开与EventSource对象的连接。大多数流行的浏览器都支持EventSource。

const eventSource = new EventSource

('http://localhost:8080/emitter?userId=123');

这将打开与服务器的连接。您可以通过为名为“ message ”的事件添加事件侦听器来处理从服务器返回的事件。对于服务器中所有成功的事件,将在注册事件侦听器时作为第二个参数传递的回调方法以一个参数作为事件对象来调用。可以使用属性'data'从事件对象中检索从服务器发送的消息,因此event.data将保存服务器发送的消息。此event.data可以是简单字符串或JSON字符串。如果它是JSON字符串,则必须对其进行解析:

eventSource.addEventListener("message", (event) => {
   const message = JSON.parse(event.data);
   console.log(message); 
})

除了EventSource中的“消息”外,还有其他两个内置事件,其中一个是“ open ”,一旦从服务器接收到200个状态,就会调用该事件。由于服务器在不等待线程完成的情况下返回发射器,因此'open'事件将不等待接收所有消息,并且在服务器响应200状态后将被调用。另一个事件是“错误”,每当发生网络错误以及服务器通过在发射器上调用“ complete”或“ completeWithError”方法关闭连接时,都将调用此事件。

eventSource.addEventListener("open", (event) => {
   console.log('connection is live');
});
eventSource.addEventListener("error", (event) => {
   if (event.readyState == EventSource.CLOSED) {
      console.log('connection is closed');
   } else {
      console.log("Error occured", event);
   }
   event.target.close();
});

因此,如果在发生任何异常的情况下从spring控制器调用Emitter.completeWithError(exception),则可以在“错误”事件侦听器中捕获该事件。同样,当您调用generator.complete()时,也会调用此“错误”事件侦听器,并且将打印第一个“ if”块中的控制台消息。您可以通过调用在客户端关闭连接event.target.close();,否则,如果出现错误,客户端将继续重试连接。

您可以在spring借助SseEventBuilder创建命名事件。

SseEmitter emitter = new SseEmitter();
SseEmitter.SseEventBuilder sseEventBuilder = SseEmitter.event()
          .id("0") // You can give nay string as id
          .name("customEventName")
          .data("message1")
          .reconnectTime(10000); //reconnect time in millis
emitter.send(sseEventBuilder);

上述消息的输出格式为:

event: customEventName
id: 0
data: message1
retry: 10000

Named Events 在这里,您将从spring控制器发送一个带有名称的事件,因此您可以创建一个具有相同名称的事件侦听器来处理该事件的消息。当您要从服务器发送不同类型的事件,并且客户端可以通过注册与事件相对应的事件侦听器以不同方式处理事件时,此功能很有用。

eventSource.addEventListener("customEventName", (event)=> {
   const message = JSON.parse(event.data);
   console.log(message);
})

ID 在这里,从spring开始,您将发送一个ID(可以是任何字符串值),您可以使用事件对象的lastEventId属性在客户端事件侦听器中检索此ID 。这对于跟踪客户端成功接收到哪些事件很有用。如果发生错误,浏览器将在重新连接请求中发送一个特殊的标头“ Last-Event-ID”(只有在您未关闭“错误”事件监听器中的客户端连接时,才会发生这种情况)连接,那么浏览器将不会在出现错误的情况下重试连接)。服务器将在请求中解析此标头,并可以决定决定接下来发送哪个消息。如果您不想跟踪,则无需随消息一起发送ID。

eventSource.addEventListener("customEventName", (event)=> {
   console.log("Message id is " + event.lastEventId);
})

Retry 浏览器将保持服务器连接打开。服务器可以通过调用“ complete”“ completeWithError”方法来关闭连接,并且这两个事件都在客户端由“错误”事件侦听器处理。在我们的示例中,我们通过在客户端调用event.target.close()关闭“错误”事件侦听器中的连接。但是,如果我们在客户端没有关闭连接,那么如果服务器或某些网络错误关闭了连接,浏览器将重试连接。

默认情况下,浏览器将等待3秒钟,然后再次尝试建立连接,并且浏览器将继续重试,直到从服务器获取200状态为止。服务器可以通过发送此“ retry ”标志来更改此默认的3s等待时间。在此,在上面的示例中,服务器通过发送此“重试”标志来指示浏览器在发生错误的情况下重试连接之前要等待10秒(10000毫秒)。服务器也可以发送该标志值0,如果连接关闭,它将通知浏览器立即重新连接。

这对于SSE来说就是全部,希望您会发现它有用。


原文链接:http://codingdict.com/