일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- docket
- minreplica
- 스프링부트
- SW 마에스트로
- Helm
- keda
- 소프트웨어 마에스트로
- Software maestro
- Kafka
- 동등성
- blue-green
- 0 replica
- Kubernetes
- spring boot
- zset
- Grafana
- hammerDB
- Strimzi
- eks
- logback
- Debezium
- slow query
- yml
- Database
- Leaderboard
- propogation
- Salting
- Benchmarks
- traceId
- SW Maestro
- Today
- Total
김태오
Spring Boot 에서 간단한 WebSocket 구현 본문
우선 WebSocket 을 정의하면 persistent TCP connection 으로 이루어지는 Bi-Directional, full-duplex communication protocol 이다.
잘 와닿지 않으니 정의를 분해하여 이해해보자.
우선 persistent 라 하면, 계속 연결을 유지한다는 뜻이다. REST 와 gRPC 등에서 single-request 이후 connection 이 종료되는 것과 달리 connection 이후 일정 기간 동안 client 와 server 간의 연결이 종료되지 않고 오픈되어있다.
Bi-Directional 은 양쪽의, 즉 client 와 server side 에서 각각의 traffic 을 모두 핸들링할 수 있다는 뜻이다. 일반 HTTP 에서 보통 client 가 요청을 보내고 server 가 응답을 보내는 것과 달리, bi-directional connection 에서는 server 가 요청받지 않은 (response 형식이 아닌) 메시지를 client 로 전송할 수 있다. 이는 notification, live feed 등에서 유용하게 쓰일 수 있다.
full-duplex 는 하나의 party 가 동시간에 메시지를 전송하고 전송받을 수 있다는 뜻이다. server 는 어떤 시간대에서는 메시지를 전송하고 전송받을 수 있고, client 또한 마찬가지이다. 이는 two-way connection 이 동시적이고 dynamic 한 data exchange 를 할 수 있도록 한다. HTTP 에서는 client 에서 server 로 요청을 보내면, server 가 프로세싱 후 response 를 전달할 때까지 기다려야 하며, server 또한 request 가 오기 전에 response 를 보낼 수 없으므로, half-duplex 라 할 수 있다.
즉 WebSocket 은 연결 후 message exchange 에 어떠한 조건도 작용하지 않는다.
우선 WebSocket 은 TCP 연결이기 때문에 handshake 로 시작을 한다. client 는 server 로 우선 일반적인 HTTP request 를 보내는데, 이 때 header 에 upgrade : Websocket 이 담겨있어야 한다. 이는 protocol 을 HTTP 에서 WebSocket 으로 변환해준다.
GET /websocket HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Version: 13
Websocket connection 은 http:// scheme 을 사용하지 않기 때문에 ws:// 혹은 wss:// 로 시작하는 endpoint 를 사용한다. wss 는 https 와 마찬가지로 secure Websocket connection 에 사용된다.
connection 을 종료할 때는 client 나 server 에서 "close frame" 을 전송한다. 이것에는 status code 가 담겨있다.
1000: Normal closure.
1001: Going away (like a server shutting down or a browser tab closing).
1002: Protocol error.
1006: Abnormal closure (no close frame received).
responding party 에서는 자신만의 close frame 을 전송한다. 양쪽의 프레임이 전송되면, TCP connection 이 종료되며, 양쪽의 party 는 연결과 연관된 모든 resource 가 제대로 release 되었다는 것을 보장해야 한다.
간단한 WebSocket config 이다.
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler(), "/websocket")
.setAllowedOrigins("*");
}
@Bean
org.springframework.web.socket.WebSocketHandler webSocketHandler() {
return new WebSocketHandler();
}
}
우선 registerWebSocketHandlers method 는 WebSocketConfigurer 에서 Override 되는데, 특정 URL 로 Websocket handler 를 매핑하기 위해 사용된다. 이 경우에는 /websocket 으로 mapping 하고 있으며, setAllowedOrigins("*") 로 모든 Origin 에서 연결을 가능케 하는 간단한 CORS configuration 을 추가했다.
다음으로 Bean 으로 등록된 WebSocketHandler 이다.
@Slf4j
public class WebSocketHandler implements org.springframework.web.socket.WebSocketHandler {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
log.info("Connection established on session: {}", session.getId());
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
String tutorial = (String) message.getPayload();
log.info("Message: {}", tutorial);
session.sendMessage(new TextMessage("Started processing tutorial: " + session + " - " + tutorial));
Thread.sleep(1000);
session.sendMessage(new TextMessage("Completed processing tutorial: " + tutorial));
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
log.info("Exception occured: {} on session: {}", exception.getMessage(), session.getId());
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
log.info("Connection closed on session: {} with status: {}", session.getId(), closeStatus.getCode());
}
@Override
public boolean supportsPartialMessages() {
return false;
}
}
client side 에서 메시지를 받아보지 않을 것이기 때문에 logging 을 위해 Slf4j annotation 을 추가했다. 메시지 핸들링, 연결 시작과 종료 등 WebSocketHandler 에서 몇 개의 method 를 Override 하였다. handleMessage method 에서는 payload 가 들어오면, 즉시 client 로 하나의 메시지를 전송하며, 1초 후 (여기에 어떠한 서버에서의 프로세스가 들어가는 것이다.) 메시지를 하나 더 전송한다.
Postman 에서 간단히 테스트해본다.
연결이 문제없이 완료되고, "test message" 라는 메시지를 전송하면 즉시 message 가 하나 날라오고, 1초 뒤 (thread.sleep을 설정해놓았기 때문에) message 가 하나 더 날라온다.
정상적인 로깅도 확인할 수 있다.