diff --git a/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/properties/RabbitMqProperties.java b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/properties/RabbitMqProperties.java index 9f14d382c..bbf6fdb0e 100644 --- a/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/properties/RabbitMqProperties.java +++ b/pennyway-infra/src/main/java/kr/co/pennyway/infra/common/properties/RabbitMqProperties.java @@ -13,6 +13,7 @@ public class RabbitMqProperties { private final String username; private final String password; private final String virtualHost; + private final int requestedHeartbeat; @Override public String toString() { @@ -22,6 +23,7 @@ public String toString() { ", username='" + username + '\'' + ", password='" + password + '\'' + ", virtualHost='" + virtualHost + '\'' + + ", requestedHeartbeat=" + requestedHeartbeat + '}'; } } diff --git a/pennyway-infra/src/main/java/kr/co/pennyway/infra/config/MessageBrokerConfig.java b/pennyway-infra/src/main/java/kr/co/pennyway/infra/config/MessageBrokerConfig.java index 0dbf8724b..624e728b9 100644 --- a/pennyway-infra/src/main/java/kr/co/pennyway/infra/config/MessageBrokerConfig.java +++ b/pennyway-infra/src/main/java/kr/co/pennyway/infra/config/MessageBrokerConfig.java @@ -78,6 +78,7 @@ public ConnectionFactory createConnectionFactory() { factory.setPassword(rabbitMqProperties.getPassword()); factory.setPort(rabbitMqProperties.getPort()); factory.setVirtualHost(rabbitMqProperties.getVirtualHost()); + factory.setRequestedHeartBeat(rabbitMqProperties.getRequestedHeartbeat()); return factory; } diff --git a/pennyway-infra/src/main/resources/application-infra.yml b/pennyway-infra/src/main/resources/application-infra.yml index dcd31a825..97afa94d1 100644 --- a/pennyway-infra/src/main/resources/application-infra.yml +++ b/pennyway-infra/src/main/resources/application-infra.yml @@ -34,6 +34,7 @@ spring: username: ${RABBITMQ_USERNAME:guest} password: ${RABBITMQ_PASSWORD:guest} virtual-host: ${RABBITMQ_VIRTUAL_HOST:/} + requested-heartbeat: ${RABBITMQ_REQUESTED_HEARTBEAT:20} app: question-address: ${ADMIN_ADDRESS:team.collabu@gmail.com} diff --git a/pennyway-socket/src/main/java/kr/co/pennyway/socket/common/interceptor/handler/inbound/HeartBeatNegotiationInterceptor.java b/pennyway-socket/src/main/java/kr/co/pennyway/socket/common/interceptor/handler/inbound/HeartBeatNegotiationInterceptor.java new file mode 100644 index 000000000..76000ede0 --- /dev/null +++ b/pennyway-socket/src/main/java/kr/co/pennyway/socket/common/interceptor/handler/inbound/HeartBeatNegotiationInterceptor.java @@ -0,0 +1,55 @@ +package kr.co.pennyway.socket.common.interceptor.handler.inbound; + +import kr.co.pennyway.socket.common.interceptor.marker.ConnectCommandHandler; +import lombok.extern.slf4j.Slf4j; +import org.springframework.messaging.Message; +import org.springframework.messaging.simp.stomp.StompCommand; +import org.springframework.messaging.simp.stomp.StompHeaderAccessor; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class HeartBeatNegotiationInterceptor implements ConnectCommandHandler { + private static final String HEART_BEAT_HEADER = "heart-beat"; + + private static final long SERVER_HEARTBEAT_SEND = 25000; // sx + private static final long SERVER_HEARTBEAT_RECEIVE = 25000; // sy + + @Override + public boolean isSupport(StompCommand command) { + return StompCommand.CONNECT.equals(command); + } + + @Override + public void handle(Message message, StompHeaderAccessor accessor) { + String heartbeat = accessor.getFirstNativeHeader(HEART_BEAT_HEADER); + + long clientToServer = SERVER_HEARTBEAT_RECEIVE; + long serverToClient = SERVER_HEARTBEAT_SEND; + + if (heartbeat == null || heartbeat.equals("0,0")) { + log.debug("Client attempted connection without heart-beat. Enforcing server's heart-beat policy: {},{}", + SERVER_HEARTBEAT_SEND, SERVER_HEARTBEAT_RECEIVE); + } + + if (heartbeat != null) { + String[] parts = heartbeat.split(","); + + if (parts.length == 2) { + long cx = Long.parseLong(parts[0]); + long cy = Long.parseLong(parts[1]); + + clientToServer = (cx != 0) ? Math.max(cx, SERVER_HEARTBEAT_RECEIVE) : SERVER_HEARTBEAT_RECEIVE; + serverToClient = (cy != 0) ? Math.max(SERVER_HEARTBEAT_SEND, cy) : SERVER_HEARTBEAT_SEND; + + log.debug("Heart-beat negotiation - Client wants: {}, Server wants: {}", + heartbeat, SERVER_HEARTBEAT_SEND + "," + SERVER_HEARTBEAT_RECEIVE); + } + } + + log.info("Negotiated heart-beat - Client to Server: {}, Server to Client: {}", + clientToServer, serverToClient); + + accessor.setNativeHeader(HEART_BEAT_HEADER, clientToServer + "," + serverToClient); + } +} diff --git a/pennyway-socket/src/main/java/kr/co/pennyway/socket/common/properties/MessageBrokerProperties.java b/pennyway-socket/src/main/java/kr/co/pennyway/socket/common/properties/MessageBrokerProperties.java index 82ebe5a46..758bb2431 100644 --- a/pennyway-socket/src/main/java/kr/co/pennyway/socket/common/properties/MessageBrokerProperties.java +++ b/pennyway-socket/src/main/java/kr/co/pennyway/socket/common/properties/MessageBrokerProperties.java @@ -16,6 +16,8 @@ public class MessageBrokerProperties { private final String clientPassword; private final String userPrefix; private final String publishExchange; + private final int heartbeatSendInterval; + private final int heartbeatReceiveInterval; @Override public String toString() { @@ -28,6 +30,8 @@ public String toString() { ", clientPassword='" + clientPassword + '\'' + ", userPrefix='" + userPrefix + '\'' + ", publishExchange='" + publishExchange + '\'' + + ", heartbeatSendInterval=" + heartbeatSendInterval + + ", heartbeatReceiveInterval=" + heartbeatReceiveInterval + '}'; } } diff --git a/pennyway-socket/src/main/java/kr/co/pennyway/socket/config/WebSocketMessageBrokerConfig.java b/pennyway-socket/src/main/java/kr/co/pennyway/socket/config/WebSocketMessageBrokerConfig.java index 0cfc325d1..09017723c 100644 --- a/pennyway-socket/src/main/java/kr/co/pennyway/socket/config/WebSocketMessageBrokerConfig.java +++ b/pennyway-socket/src/main/java/kr/co/pennyway/socket/config/WebSocketMessageBrokerConfig.java @@ -53,7 +53,9 @@ public void configureMessageBroker(MessageBrokerRegistry config) { .setClientLogin(messageBrokerProperties.getClientId()) .setClientPasscode(messageBrokerProperties.getClientPassword()) .setRelayHost(messageBrokerProperties.getHost()) - .setRelayPort(messageBrokerProperties.getPort()); + .setRelayPort(messageBrokerProperties.getPort()) + .setSystemHeartbeatSendInterval(messageBrokerProperties.getHeartbeatSendInterval()) + .setSystemHeartbeatReceiveInterval(messageBrokerProperties.getHeartbeatReceiveInterval()); config.setUserDestinationPrefix(messageBrokerProperties.getUserPrefix()); config.setPathMatcher(new AntPathMatcher(".")); diff --git a/pennyway-socket/src/main/resources/application.yml b/pennyway-socket/src/main/resources/application.yml index eba37aedb..66d833e46 100644 --- a/pennyway-socket/src/main/resources/application.yml +++ b/pennyway-socket/src/main/resources/application.yml @@ -23,6 +23,8 @@ message-broker: client-password: ${MESSAGE_BROKER_CLIENT_PASSWORD:guest} user-prefix: ${MESSAGE_BROKER_USER_PREFIX:/usr} publish-exchange: ${MESSAGE_BROKER_PUBLISH_EXCHANGE:/topic} + heartbeat-send-interval: ${MESSAGE_BROKER_HEARTBEAT_SEND_INTERVAL:20000} + heartbeat-receive-interval: ${MESSAGE_BROKER_HEARTBEAT_RECEIVE_INTERVAL:20000} jwt: secret-key: