일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- +9:00
- mysql =
- createEntityGraph
- fractional seconds
- EmbeddedId
- Protecle
- 1*1000
- spring boot
- MYSQL
- AuditingEntityListener
- apatch poi
- pooled-lo
- @CreateDate
- mysql equal null
- 버전 문자열 비교
- getEntityGraph
- @EntityListeners
- getDateCellValue
- yml
- RootGraph
- https
- deserializer
- 오블완
- sendFractionalSeconds
- load order
- 티스토리챌린지
- mysql not equal null
- 운동해서 광명찾자
- MSSQL
- NamedEntityGraph
- Today
- Total
Hello
[JavaScript] SSE + Spring SseEmitter 본문
어떤 이벤트가 생겼을 때 client ui를 업데이트가 필요했습니다. SSE(Server-Sent-Event)라는 것을 알게 되어 이를 공부한 내용을 간략하게 작성합니다.
SSE는 단방향 통신으로 서버에서 전송한 데이터를 클라이언트는 받기만 할 수 있습니다.
HTTP/2(100개)를 사용하지 않으면 SSE 는 열려 있는 최대 연결 수에 대한 제한이 있습니다. HTTP를 사용할 때 브라우저당 6개로 설정 되어 있습니다.
구독
//동일 도메인 구독
const eventSource = new EventSource('/api/subscribe/sse');
//다른 도메인의 이벤트 구독, URI와 옵션 지정
const eventSource = new EventSource('//example.com/api/subscribe/sse', { withCredentials: true });
이벤트 수신
메시지 발송 옵션 중 event(name)을 지정하지 않은 경우 onmessage를 지정한 경우 addEventListener를 사용해 이벤트를 받음
//name을 지정하지 않은 메시지 수신
eventSource.onmessage = function (e) {
//some logic...
};
//name 지정한 메시지 수신
eventSource.addEventListener('change-lang', function(e) {
//some logic...
})
//error handler
eventSource.onerror = function (e) {
alert("EventSource failed.");
};
이벤트 스트림 형식
스트림 종료를 알리기 위해 '\n\n'이 포함되어야합니다.
data: message\n\n
retry : 이벤트 송신을 시도할 때에 사용하는 재연결 시간(reconnection time), 밀리초 단위로 지정.
서버 코드(SSE in Spring)
@RestController
public class SseController {
@Autowired
private SseEmitterService sseEmitterService;
@GetMapping(value = "/api/subscribe/{id}", produces = "text/event-stream")
public SseEmitter subscribe(@PathVariable String id,
@RequestHeader(name = "Last-Event-ID", required = false, defaultValue = "")String lastEventId) {
return sseEmitterService.subscribe(id, lastEventId);
}
}
@Service
public class SseEmitterService {
private final Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();
private static final Long DEFAULT_TIMEOUT = TimeUnit.MINUTES.toMillis(30);
public SseEmitter subscribe(String userId, String lastEventId) {
String key = userId + "_" + System.currentTimeMillis();
SseEmitter sseEmitter = new SseEmitter(DEFAULT_TIMEOUT);
sseEmitterMap.put(key, sseEmitter);
sseEmitter.onCompletion(() -> sseEmitterMap.remove(key));
sseEmitter.onTimeout(() -> sseEmitterMap.remove(key));
if (!lastEventId.isEmpty()) {
sseEmitterMap.keySet().stream().filter(x -> x.startsWith(userId))
.filter(x -> lastEventId.compareTo(x) < 0)
.forEach(x -> {
SseEmitter emitter = sseEmitterMap.get(x);
try {
emitter.send(SseEmitter.event()
.id(x)
.reconnectTime(1000)
.data("washout"));
emitter.complete();
} catch (IOException e) {
sseEmitterMap.remove(x);
emitter.completeWithError(e);
}
});
}
return sseEmitter;
}
public void sendUserBy(String userId, Object lang) {
sseEmitterMap.keySet().stream().filter(x -> x.startsWith(userId))
.forEach(x -> {
SseEmitter emitter = sseEmitterMap.get(x);
try {
emitter.send(SseEmitter.event()
.id(x)
.name("change-lang")
.data(lang));
emitter.complete();
} catch (IOException e) {
sseEmitterMap.remove(x);
emitter.completeWithError(e);
}
});
}
}
Last-Event-ID
D를 설정하게 되면, 브라우저는 마지막에 발생한 이벤트를 추적할 수 있게 됩니다.
서버 연결이 끊어지면 특수 HTTP 헤더 (Last-Event-ID)가 새 요청으로 설정됩니다.
console.log를 통해 event response 내용 확인하면 lastEventId가 담긴 것을 볼 수 있다.
실제 적용하지 못한이유는 탭을 많이 띄어 두고 테스트 했을 때 간헐적으로 한두개의 탭에서 메시지를 받지 못해 기술을 테스트만 하게 되었습니다. 공부를 하며 검색하는 와중에, BroadcastChannel API를 알게되어 사용했습니다.
참고:
https://jsonobject.tistory.com/558
https://developer.mozilla.org/ko/docs/Web/API/Server-sent_events/Using_server-sent_events
https://developer.mozilla.org/ko/docs/Web/API/Server-sent_events
https://stackoverflow.com/questions/11896160/any-way-to-identify-browser-tab-in-javascript
'기타' 카테고리의 다른 글
[JAVASCRIPT] 파일 데이터 -> binary data (0) | 2024.03.11 |
---|---|
[JavaScript] BroadcastChannel API (0) | 2024.02.01 |
gradle, maven denpendencies 경로 확인 (0) | 2023.12.10 |
리눅스 서버 시간 동기화 NTP (0) | 2023.09.25 |
Intellij 프로젝트 한글경로 build fail (TestSuiteExecutionException ) (0) | 2023.08.25 |