1. 개요
이 예제에서는 Spring을 사용하여 Server-Sent-Events 기반 API를 구현하는 방법을 살펴봅니다.
간단히 말해, Server-Sent-Events 또는 줄여서 SSE는 웹 애플리케이션이 단방향 이벤트 스트림을 처리하고 서버가 데이터를 내보낼 때마다 업데이트를 수신할 수 있도록 하는 HTTP 표준입니다.
Spring 4.2 버전에서 이미 지원했지만 Spring 5부터는 보다 관용적이고 편리한 방법으로 처리할 수 있습니다.
2. Spring 5 Webflux를 사용한 SSE
이를 달성하기 위해 Reactor 라이브러리에서 제공하는 Flux 클래스 또는 잠재적 으로 이벤트 메타데이터를 제어할 수 있는 ServerSentEvent 엔터티 와 같은 구현을 사용할 수 있습니다 .
2.1. Flux 를 사용한 스트리밍 이벤트
Flux 는 이벤트 스트림의 반응적 표현입니다. 지정된 요청 또는 응답 미디어 유형에 따라 다르게 처리됩니다.
SSE 스트리밍 엔드포인트를 생성하려면 W3C 사양 을 따르고 MIME 유형을 text/event-stream 으로 지정해야 합니다 .
@GetMapping(path = "/stream-flux", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamFlux() {
return Flux.interval(Duration.ofSeconds(1))
.map(sequence -> "Flux - " + LocalTime.now().toString());
}
interval 메서드는 긴 값을 점진적으로 방출하는 Flux 를 생성 합니다 . 그런 다음 해당 값을 원하는 출력에 매핑합니다.
응용 프로그램을 시작하고 Endpoints을 탐색하여 사용해 보겠습니다.
우리는 브라우저가 서버에 의해 초 단위로 푸시되는 이벤트에 어떻게 반응하는지 볼 것입니다. Flux 및 Reactor Core 에 대한 자세한 내용은 이 게시물 에서 확인할 수 있습니다 .
2.2. ServerSentEvent 요소 사용하기
이제 출력 문자열 을 ServerSentSevent 개체로 래핑하고 이 작업의 이점을 살펴보겠습니다.
@GetMapping("/stream-sse")
public Flux<ServerSentEvent<String>> streamEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(sequence -> ServerSentEvent.<String> builder()
.id(String.valueOf(sequence))
.event("periodic-event")
.data("SSE - " + LocalTime.now().toString())
.build());
}
알 수 있듯이 ServerSentEvent 엔터티 를 사용하면 몇 가지 이점이 있습니다 .
- 실제 사례 시나리오에서 필요한 이벤트 메타데이터를 처리할 수 있습니다.
- " text/event-stream " 미디어 유형 선언 을 무시할 수 있습니다.
이 경우 id , 이벤트 이름 및 가장 중요한 이벤트의 실제 데이터 를 지정했습니다.
또한 이벤트 전송을 시도할 때 사용할 재연결 시간을 지정 하는 어노테이션 속성과 재시 도 값을 추가할 수 있습니다.
2.3. WebClient로 서버 전송 이벤트 사용
이제 WebClient 로 이벤트 스트림을 사용하겠습니다 .:
public void consumeServerSentEvent() {
WebClient client = WebClient.create("http://localhost:8080/sse-server");
ParameterizedTypeReference<ServerSentEvent<String>> type
= new ParameterizedTypeReference<ServerSentEvent<String>>() {};
Flux<ServerSentEvent<String>> eventStream = client.get()
.uri("/stream-sse")
.retrieve()
.bodyToFlux(type);
eventStream.subscribe(
content -> logger.info("Time: {} - event: name[{}], id [{}], content[{}] ",
LocalTime.now(), content.event(), content.id(), content.data()),
error -> logger.error("Error receiving SSE: {}", error),
() -> logger.info("Completed!!!"));
}
subscribe 메서드를 사용하면 이벤트를 성공적으로 수신했을 때, 오류가 발생했을 때, 스트리밍이 완료되었을 때 어떻게 진행할 것인지를 나타낼 수 있습니다 .
이 예에서는 Response body을 가져오는 간단하고 직접적인 방법인 검색 방법을 사용했습니다.
이 메서드 는 onStatus 문을 추가하는 시나리오를 처리하지 않는 한 4xx 또는 5xx 응답을 받으면 자동으로 WebClientResponseException 을 발생시킵니다.
반면에 ClientResponse 에 대한 액세스를 제공 하고 실패한 응답에 대한 오류 신호를 보내지 않는 교환 방법도 사용할 수 있었습니다.
이벤트 메타데이터가 필요하지 않은 경우 ServerSentEvent 래퍼 를 우회할 수 있다는 점을 고려해야 합니다 .
3. Spring MVC의 SSE 스트리밍
우리가 말했듯이 SSE 사양은 SseEmitter 클래스가 도입된 Spring 4.2부터 지원되었습니다.
간단히 말해서 우리는 SseEmitter 가 데이터 푸시 작업을 수행하고 이미터 인스턴스를 반환 하는 스레드 인 ExecutorService 를 정의하여 다음과 같은 방식으로 연결을 열어 둡니다.
@GetMapping("/stream-sse-mvc")
public SseEmitter streamSseMvc() {
SseEmitter emitter = new SseEmitter();
ExecutorService sseMvcExecutor = Executors.newSingleThreadExecutor();
sseMvcExecutor.execute(() -> {
try {
for (int i = 0; true; i++) {
SseEventBuilder event = SseEmitter.event()
.data("SSE MVC - " + LocalTime.now().toString())
.id(String.valueOf(i))
.name("sse event - mvc");
emitter.send(event);
Thread.sleep(1000);
}
} catch (Exception ex) {
emitter.completeWithError(ex);
}
});
return emitter;
}
항상 사용 사례 시나리오에 맞는 ExecutorService 를 선택해야 합니다.
우리는 Spring MVC에서 SSE에 대해 더 많이 배울 수 있고 이 흥미로운 예제 을 읽으면 다른 예제를 볼 수 있습니다 .
4. 서버 전송 이벤트 이해
이제 SSE Endpoints을 구현하는 방법을 알았으므로 몇 가지 기본 개념을 이해하여 좀 더 깊이 들어가 보겠습니다.
SSE는 이벤트를 언제든지 단방향으로 스트리밍할 수 있도록 대부분의 브라우저에서 채택한 사양입니다.
'이벤트'는 사양에 정의된 형식을 따르는 UTF-8로 인코딩된 텍스트 데이터의 스트림일 뿐입니다.
이 형식은 줄바꿈으로 구분된 일련의 키-값 요소(id, retry, data 및 이름을 나타내는 이벤트)로 구성됩니다.
댓글도 지원합니다.
사양은 어떤 식으로든 데이터 페이로드 형식을 제한하지 않습니다. 간단한 문자열 이나 더 복잡한 JSON 또는 XML 구조를 사용할 수 있습니다.
마지막으로 고려해야 할 사항은 SSE 스트리밍과 WebSockets 사용의 차이점 입니다.
WebSocket 은 서버와 클라이언트 간에 전이중(양방향) 통신을 제공하는 반면 SSE는 단방향 통신을 사용합니다 .
또한 WebSockets 는 HTTP 프로토콜이 아니며 SSE와 달리 오류 처리 표준을 제공하지 않습니다.
5. 결론
요약하자면, 이 기사에서 우리는 의심할 여지 없이 차세대 시스템을 만들 수 있는 훌륭한 리소스인 SSE 스트리밍의 주요 개념을 배웠습니다.
우리는 이제 이 프로토콜을 사용할 때 내부에서 일어나는 일을 이해할 수 있는 훌륭한 위치에 있습니다.
또한 Github 리포지토리 에서 찾을 수 있는 몇 가지 간단한 예제로 이론을 보완했습니다 .