-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
서버가 클라이언트에게 Ping/Pong 요청 #776
Changes from all commits
74a10d8
4fa1eae
ed17a87
48fcc60
33f4148
9b879eb
edd8e7f
e9323a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,16 +16,25 @@ | |
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.context.ApplicationEventPublisher; | ||
import org.springframework.scheduling.annotation.Scheduled; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.socket.PingMessage; | ||
import org.springframework.web.socket.TextMessage; | ||
import org.springframework.web.socket.WebSocketSession; | ||
|
||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
import static com.ddang.ddang.websocket.handler.dto.WebSocketAttributeKey.CONNECTED; | ||
import static com.ddang.ddang.websocket.handler.dto.WebSocketAttributeKey.USER_ID; | ||
|
||
@Slf4j | ||
@Component | ||
@RequiredArgsConstructor | ||
public class ChatWebSocketHandleTextMessageProvider implements WebSocketHandleTextMessageProvider { | ||
|
@@ -115,7 +124,7 @@ private TextMessage createTextMessage( | |
} | ||
|
||
private boolean isMyMessage(final WebSocketSession session, final Long writerId) { | ||
final long userId = Long.parseLong(String.valueOf(session.getAttributes().get("userId"))); | ||
final long userId = Long.parseLong(String.valueOf(session.getAttributes().get(USER_ID.getName()))); | ||
|
||
return writerId.equals(userId); | ||
} | ||
|
@@ -136,6 +145,39 @@ private void updateReadMessageLog( | |
|
||
@Override | ||
public void remove(final WebSocketSession session) { | ||
log.info("{} 연결 종료", session); | ||
sessions.remove(session); | ||
} | ||
|
||
@Scheduled(fixedDelay = 60000) | ||
public void sendPingSessions() { | ||
final Set<WebSocketSession> webSocketSessions = getWebSocketSessions(); | ||
|
||
webSocketSessions.parallelStream() | ||
.forEach(this::sendPingMessage); | ||
} | ||
|
||
private Set<WebSocketSession> getWebSocketSessions() { | ||
return sessions.getChatRoomSessions() | ||
.values() | ||
.stream() | ||
.flatMap(webSocketSessions -> webSocketSessions.getSessions().stream()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 칭찬오! flatMap을 사용해주셨네요 👍🏻 |
||
.collect(Collectors.toSet()); | ||
} | ||
|
||
private void sendPingMessage(final WebSocketSession session) { | ||
final Map<String, Object> attributes = session.getAttributes(); | ||
final boolean connected = (boolean) attributes.get(CONNECTED.getName()); | ||
if (!connected) { | ||
sessions.remove(session); | ||
return; | ||
} | ||
|
||
attributes.put(CONNECTED.getName(), false); | ||
try { | ||
session.sendMessage(new PingMessage()); | ||
} catch (IOException e) { | ||
log.error("ping 보내기 실패 : {} ", session); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 질문ping 전송에 실패하면 로그만 찍고 다시 ping을 전송하지 않아도 괜찮을까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 ping이 실패한 경우 다시 ping 요청을 수행하더라도 실패할 것이라 생각했습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 실패한 로직을 재전송하는 부분이 필요할 것 같다는 생각이 들었어요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 다음 회의에서 이에 대해 한번 논의해 보기로 해요! |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,26 +7,29 @@ | |
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.socket.CloseStatus; | ||
import org.springframework.web.socket.PongMessage; | ||
import org.springframework.web.socket.TextMessage; | ||
import org.springframework.web.socket.WebSocketSession; | ||
import org.springframework.web.socket.handler.TextWebSocketHandler; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import static com.ddang.ddang.websocket.handler.dto.WebSocketAttributeKey.CONNECTED; | ||
import static com.ddang.ddang.websocket.handler.dto.WebSocketAttributeKey.TYPE; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class WebSocketHandler extends TextWebSocketHandler { | ||
|
||
private static final String TYPE_KEY = "type"; | ||
|
||
private final WebSocketHandleTextMessageProviderComposite providerComposite; | ||
private final ObjectMapper objectMapper; | ||
|
||
@Override | ||
protected void handleTextMessage(final WebSocketSession session, final TextMessage message) throws Exception { | ||
final String payload = message.getPayload(); | ||
final TextMessageDto textMessageDto = objectMapper.readValue(payload, TextMessageDto.class); | ||
session.getAttributes().put(TYPE_KEY, textMessageDto.type()); | ||
session.getAttributes().put(TYPE.getName(), textMessageDto.type()); | ||
|
||
final WebSocketHandleTextMessageProvider provider = providerComposite.findProvider(textMessageDto.type()); | ||
final List<SendMessageDto> sendMessageDtos = provider.handleCreateSendMessage(session, textMessageDto.data()); | ||
|
@@ -38,9 +41,15 @@ protected void handleTextMessage(final WebSocketSession session, final TextMessa | |
|
||
@Override | ||
public void afterConnectionClosed(final WebSocketSession session, final CloseStatus status) { | ||
final String type = String.valueOf(session.getAttributes().get(TYPE_KEY)); | ||
final String type = String.valueOf(session.getAttributes().get(TYPE.getName())); | ||
final TextMessageType textMessageType = TextMessageType.valueOf(type); | ||
final WebSocketHandleTextMessageProvider provider = providerComposite.findProvider(textMessageType); | ||
provider.remove(session); | ||
} | ||
|
||
@Override | ||
public void handlePongMessage(WebSocketSession session, PongMessage message) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 필수제 눈에는 왜 Final이 이렇게 잘 보이는 걸까요..? |
||
final Map<String, Object> attributes = session.getAttributes(); | ||
attributes.put(CONNECTED.getName(), true); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.ddang.ddang.websocket.handler.dto; | ||
|
||
import lombok.Getter; | ||
|
||
@Getter | ||
public enum WebSocketAttributeKey { | ||
|
||
USER_ID("userId"), | ||
BASE_URL("baseUrl"), | ||
CONNECTED("connected"), | ||
TYPE("type"); | ||
|
||
private final String name; | ||
|
||
WebSocketAttributeKey(final String name) { | ||
this.name = name; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
칭찬
스케줄러를 잘 사용해주셨네요 👍👍🏻