From 5d59ab0aca5e10a5f704d594b6ba1f8125715be4 Mon Sep 17 00:00:00 2001 From: takoyakimchi Date: Wed, 9 Oct 2024 20:47:43 +0900 Subject: [PATCH 1/9] =?UTF-8?q?fix:=20RabbitMQ=20=EB=8F=84=EC=9E=85=20->?= =?UTF-8?q?=20=EB=B6=84=EC=82=B0=20=ED=99=98=EA=B2=BD=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/build.gradle | 15 +-- .../friendogly/chat/config/ChatTemplate.java | 9 ++ .../chat/config/InMemoryChatTemplate.java | 21 ++++ .../chat/config/RabbitChatTemplate.java | 21 ++++ .../chat/controller/ChatSocketController.java | 19 ++- .../chat/service/ChatCommandService.java | 12 +- .../friendogly/config/RabbitMqConfig.java | 108 ++++++++++++++++++ .../friendogly/config/WebSocketConfig.java | 7 +- .../config/WebSocketInterceptor.java | 33 +++--- .../config/WebSocketRabbitMqConfig.java | 79 +++++++++++++ .../src/main/resources/application-dev.yml | 5 + .../src/main/resources/application-local.yml | 5 +- .../src/main/resources/application-prod.yml | 5 + 13 files changed, 295 insertions(+), 44 deletions(-) create mode 100644 backend/src/main/java/com/happy/friendogly/chat/config/ChatTemplate.java create mode 100644 backend/src/main/java/com/happy/friendogly/chat/config/InMemoryChatTemplate.java create mode 100644 backend/src/main/java/com/happy/friendogly/chat/config/RabbitChatTemplate.java create mode 100644 backend/src/main/java/com/happy/friendogly/config/RabbitMqConfig.java create mode 100644 backend/src/main/java/com/happy/friendogly/config/WebSocketRabbitMqConfig.java diff --git a/backend/build.gradle b/backend/build.gradle index cd70651b4..2bf2a32e9 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -53,6 +53,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-websocket' + implementation 'org.springframework.boot:spring-boot-starter-amqp' implementation 'io.jsonwebtoken:jjwt:0.12.6' implementation 'io.micrometer:micrometer-registry-prometheus' implementation "org.springframework.boot:spring-boot-starter-actuator" @@ -91,12 +92,12 @@ tasks.withType(GenerateSwaggerUI) { doFirst { def swaggerUIFile = file("${openapi3.outputDirectory}/openapi3.yaml") - def securitySchemesContent = " securitySchemes:\n" + \ - " APIKey:\n" + \ - " type: apiKey\n" + \ - " name: Authorization\n" + \ - " in: header\n" + \ - "security:\n" + + def securitySchemesContent = " securitySchemes:\n" + \ + " APIKey:\n" + \ + " type: apiKey\n" + \ + " name: Authorization\n" + \ + " in: header\n" + \ + "security:\n" + " - APIKey: [] # Apply the security scheme here" swaggerUIFile.append securitySchemesContent @@ -130,5 +131,5 @@ resolveMainClassName { test { systemProperty 'user.timezone', 'Asia/Seoul' - + } diff --git a/backend/src/main/java/com/happy/friendogly/chat/config/ChatTemplate.java b/backend/src/main/java/com/happy/friendogly/chat/config/ChatTemplate.java new file mode 100644 index 000000000..d230a8f06 --- /dev/null +++ b/backend/src/main/java/com/happy/friendogly/chat/config/ChatTemplate.java @@ -0,0 +1,9 @@ +package com.happy.friendogly.chat.config; + +import org.springframework.stereotype.Component; + +@Component +public interface ChatTemplate { + + void convertAndSend(String destination, Object payload); +} diff --git a/backend/src/main/java/com/happy/friendogly/chat/config/InMemoryChatTemplate.java b/backend/src/main/java/com/happy/friendogly/chat/config/InMemoryChatTemplate.java new file mode 100644 index 000000000..f7cbcee9e --- /dev/null +++ b/backend/src/main/java/com/happy/friendogly/chat/config/InMemoryChatTemplate.java @@ -0,0 +1,21 @@ +package com.happy.friendogly.chat.config; + +import org.springframework.context.annotation.Profile; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Component; + +@Component +@Profile("local") +public class InMemoryChatTemplate implements ChatTemplate { + + private final SimpMessagingTemplate template; + + public InMemoryChatTemplate(SimpMessagingTemplate template) { + this.template = template; + } + + @Override + public void convertAndSend(String destination, Object payload) { + template.convertAndSend(destination, payload); + } +} diff --git a/backend/src/main/java/com/happy/friendogly/chat/config/RabbitChatTemplate.java b/backend/src/main/java/com/happy/friendogly/chat/config/RabbitChatTemplate.java new file mode 100644 index 000000000..f976dcc0d --- /dev/null +++ b/backend/src/main/java/com/happy/friendogly/chat/config/RabbitChatTemplate.java @@ -0,0 +1,21 @@ +package com.happy.friendogly.chat.config; + +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +@Component +@Profile("!local") +public class RabbitChatTemplate implements ChatTemplate { + + private final RabbitTemplate rabbitTemplate; + + public RabbitChatTemplate(RabbitTemplate rabbitTemplate) { + this.rabbitTemplate = rabbitTemplate; + } + + @Override + public void convertAndSend(String destination, Object payload) { + rabbitTemplate.convertAndSend(destination, payload); + } +} diff --git a/backend/src/main/java/com/happy/friendogly/chat/controller/ChatSocketController.java b/backend/src/main/java/com/happy/friendogly/chat/controller/ChatSocketController.java index 161baa91b..44222255f 100644 --- a/backend/src/main/java/com/happy/friendogly/chat/controller/ChatSocketController.java +++ b/backend/src/main/java/com/happy/friendogly/chat/controller/ChatSocketController.java @@ -3,7 +3,6 @@ import com.happy.friendogly.auth.WebSocketAuth; import com.happy.friendogly.chat.dto.request.ChatMessageSocketRequest; import com.happy.friendogly.chat.dto.request.InviteToChatRoomRequest; -import com.happy.friendogly.chat.dto.response.InviteToChatRoomResponse; import com.happy.friendogly.chat.service.ChatCommandService; import com.happy.friendogly.chat.service.ChatRoomQueryService; import com.happy.friendogly.common.ApiResponse; @@ -16,7 +15,6 @@ import org.springframework.messaging.handler.annotation.MessageExceptionHandler; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.Payload; -import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.web.bind.annotation.RestController; @RestController @@ -24,31 +22,28 @@ public class ChatSocketController { private final ChatCommandService chatCommandService; private final ChatRoomQueryService chatRoomQueryService; - private final SimpMessagingTemplate template; public ChatSocketController( ChatCommandService chatCommandService, - ChatRoomQueryService chatRoomQueryService, - SimpMessagingTemplate template + ChatRoomQueryService chatRoomQueryService ) { this.chatCommandService = chatCommandService; this.chatRoomQueryService = chatRoomQueryService; - this.template = template; } - @MessageMapping("/invite") + @MessageMapping("/invite") // TODO: 1대1 채팅방 구현 시 수정 필요 public void invite( @WebSocketAuth Long senderMemberId, @Payload InviteToChatRoomRequest request ) { chatRoomQueryService.validateInvitation(senderMemberId, request); - template.convertAndSend( - "/topic/invite/" + request.receiverMemberId(), - new InviteToChatRoomResponse(request.chatRoomId()) - ); +// template.convertAndSend( +// "/topic/invite/" + request.receiverMemberId(), +// new InviteToChatRoomResponse(request.chatRoomId()) +// ); } - @MessageMapping("/chat/{chatRoomId}") + @MessageMapping("chat.{chatRoomId}") public void sendMessage( @WebSocketAuth Long memberId, @DestinationVariable(value = "chatRoomId") Long chatRoomId, diff --git a/backend/src/main/java/com/happy/friendogly/chat/service/ChatCommandService.java b/backend/src/main/java/com/happy/friendogly/chat/service/ChatCommandService.java index 5f8bbf6c5..b2782becd 100644 --- a/backend/src/main/java/com/happy/friendogly/chat/service/ChatCommandService.java +++ b/backend/src/main/java/com/happy/friendogly/chat/service/ChatCommandService.java @@ -4,6 +4,7 @@ import static com.happy.friendogly.chat.domain.MessageType.ENTER; import static com.happy.friendogly.chat.domain.MessageType.LEAVE; +import com.happy.friendogly.chat.config.ChatTemplate; import com.happy.friendogly.chat.domain.ChatMessage; import com.happy.friendogly.chat.domain.ChatRoom; import com.happy.friendogly.chat.domain.MessageType; @@ -16,7 +17,6 @@ import com.happy.friendogly.member.repository.MemberRepository; import com.happy.friendogly.notification.service.NotificationService; import java.time.LocalDateTime; -import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,27 +24,27 @@ @Transactional public class ChatCommandService { - private static final String TOPIC_CHAT_PREFIX = "/topic/chat/"; + private static final String TOPIC_CHAT_PREFIX = "/exchange/chat.exchange/room."; private static final String EMPTY_CONTENT = ""; private final MemberRepository memberRepository; private final ChatRoomRepository chatRoomRepository; private final ChatMessageRepository chatMessageRepository; private final NotificationService notificationService; - private final SimpMessagingTemplate template; + private final ChatTemplate chatTemplate; public ChatCommandService( MemberRepository memberRepository, ChatRoomRepository chatRoomRepository, ChatMessageRepository chatMessageRepository, NotificationService notificationService, - SimpMessagingTemplate template + ChatTemplate chatTemplate ) { this.memberRepository = memberRepository; this.chatRoomRepository = chatRoomRepository; this.chatMessageRepository = chatMessageRepository; this.notificationService = notificationService; - this.template = template; + this.chatTemplate = chatTemplate; } public void sendEnter(Long senderMemberId, Long chatRoomId) { @@ -73,7 +73,7 @@ public void sendLeave(Long senderMemberId, Long chatRoomId) { private void sendAndSave(MessageType messageType, String content, ChatRoom chatRoom, Member senderMember) { ChatMessageSocketResponse chat = new ChatMessageSocketResponse(messageType, content, senderMember, LocalDateTime.now()); notificationService.sendChatNotification(chatRoom.getId(), chat); - template.convertAndSend(TOPIC_CHAT_PREFIX + chatRoom.getId(), chat); + chatTemplate.convertAndSend(TOPIC_CHAT_PREFIX + chatRoom.getId(), chat); chatMessageRepository.save(new ChatMessage(chatRoom, messageType, senderMember, content)); } } diff --git a/backend/src/main/java/com/happy/friendogly/config/RabbitMqConfig.java b/backend/src/main/java/com/happy/friendogly/config/RabbitMqConfig.java new file mode 100644 index 000000000..e4d52fb03 --- /dev/null +++ b/backend/src/main/java/com/happy/friendogly/config/RabbitMqConfig.java @@ -0,0 +1,108 @@ +package com.happy.friendogly.config; + +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.rabbitmq.client.ConnectionFactory; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import org.springframework.amqp.core.AmqpAdmin; +import org.springframework.amqp.core.Binding; +import org.springframework.amqp.core.BindingBuilder; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.core.TopicExchange; +import org.springframework.amqp.rabbit.annotation.EnableRabbit; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitAdmin; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Configuration +@EnableRabbit +@Profile("!local") +public class RabbitMqConfig { + + private static final String CHAT_QUEUE_NAME = "chat.queue"; + private static final String CHAT_EXCHANGE_NAME = "chat.exchange"; + private static final String ROUTING_KEY = "*.room.*"; + + private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + @Value("${spring.rabbitmq.host}") + private String host; + + @Value("${spring.rabbitmq.username}") + private String username; + + @Value("${spring.rabbitmq.password}") + private String password; + + @Value("${spring.rabbitmq.stomp-port}") + private int port; + + @Bean + public Queue queue() { + return new Queue(CHAT_QUEUE_NAME, true); + } + + @Bean + public TopicExchange topicExchange() { + return new TopicExchange(CHAT_EXCHANGE_NAME, true, false); + } + + @Bean + public Binding binding(Queue queue, TopicExchange topicExchange) { + return BindingBuilder.bind(queue) + .to(topicExchange) + .with(ROUTING_KEY); + } + + @Bean + public ConnectionFactory connectionFactory() { + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setHost(host); + connectionFactory.setPort(port); + connectionFactory.setUsername(username); + connectionFactory.setPassword(password); + connectionFactory.setVirtualHost("/"); + return connectionFactory.getRabbitConnectionFactory(); + } + + @Bean + public AmqpAdmin amqpAdmin(RabbitTemplate rabbitTemplate) { + RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitTemplate.getConnectionFactory()); + rabbitAdmin.declareExchange(topicExchange()); + return rabbitAdmin; + } + + @Bean + public RabbitTemplate rabbitTemplate( + org.springframework.amqp.rabbit.connection.ConnectionFactory connectionFactory) { + + RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); + rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter()); + return rabbitTemplate; + } + + @Bean + public Jackson2JsonMessageConverter jackson2JsonMessageConverter() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + objectMapper.registerModule(javaTimeModule()); + return new Jackson2JsonMessageConverter(objectMapper); + } + + @Bean + public Module javaTimeModule() { + return new JavaTimeModule() + .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(TIME_FORMAT)) + .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(TIME_FORMAT)); + } +} diff --git a/backend/src/main/java/com/happy/friendogly/config/WebSocketConfig.java b/backend/src/main/java/com/happy/friendogly/config/WebSocketConfig.java index e3b09b25e..b2569cc56 100644 --- a/backend/src/main/java/com/happy/friendogly/config/WebSocketConfig.java +++ b/backend/src/main/java/com/happy/friendogly/config/WebSocketConfig.java @@ -4,6 +4,7 @@ import com.happy.friendogly.auth.service.jwt.JwtProvider; import java.util.List; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; @@ -11,6 +12,7 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; +@Profile("local") @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @@ -19,7 +21,8 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { private final WebSocketErrorHandler webSocketErrorHandler; private final JwtProvider jwtProvider; - public WebSocketConfig(WebSocketInterceptor webSocketInterceptor, + public WebSocketConfig( + WebSocketInterceptor webSocketInterceptor, WebSocketErrorHandler webSocketErrorHandler, JwtProvider jwtProvider ) { @@ -30,7 +33,7 @@ public WebSocketConfig(WebSocketInterceptor webSocketInterceptor, @Override public void configureMessageBroker(MessageBrokerRegistry registry) { - registry.enableSimpleBroker("/topic"); + registry.enableSimpleBroker("/exchange"); registry.setApplicationDestinationPrefixes("/publish"); } diff --git a/backend/src/main/java/com/happy/friendogly/config/WebSocketInterceptor.java b/backend/src/main/java/com/happy/friendogly/config/WebSocketInterceptor.java index b38dd5ae2..b781ab729 100644 --- a/backend/src/main/java/com/happy/friendogly/config/WebSocketInterceptor.java +++ b/backend/src/main/java/com/happy/friendogly/config/WebSocketInterceptor.java @@ -19,8 +19,8 @@ @Component public class WebSocketInterceptor implements ChannelInterceptor { - private static final String TOPIC_CHAT_ENDPOINT = "/topic/chat/"; - private static final String TOPIC_INVITE_ENDPOINT = "/topic/invite/"; + private static final String TOPIC_CHAT_ENDPOINT = "/exchange/chat.exchange/room."; +// private static final String TOPIC_INVITE_ENDPOINT = "/topic/invite/"; private final ClubRepository clubRepository; private final ChatRoomRepository chatRoomRepository; @@ -50,9 +50,10 @@ public Message preSend(Message message, MessageChannel channel) { String accessToken = accessor.getFirstNativeHeader(AUTHORIZATION); long memberId = validateAndExtractMemberIdFrom(accessToken); - if (destination.startsWith(TOPIC_INVITE_ENDPOINT)) { - validateInviteSubscription(memberId, destination); - } + // TODO: 1대1 채팅방 구현할 때 고려하기 +// if (destination.startsWith(TOPIC_INVITE_ENDPOINT)) { +// validateInviteSubscription(memberId, destination); +// } if (destination.startsWith(TOPIC_CHAT_ENDPOINT)) { validateChatSubscription(memberId, destination); @@ -68,9 +69,9 @@ private void validateDestination(String destination) { if (destination.startsWith(TOPIC_CHAT_ENDPOINT)) { return; } - if (destination.startsWith(TOPIC_INVITE_ENDPOINT)) { - return; - } +// if (destination.startsWith(TOPIC_INVITE_ENDPOINT)) { +// return; +// } throw new FriendoglyWebSocketException(String.format("%s는 올바른 sub 경로가 아닙니다.", destination)); } @@ -88,14 +89,14 @@ private long validateAndExtractMemberIdFrom(String accessToken) { } } - private void validateInviteSubscription(long memberId, String destination) { - String rawMemberIdToSubscribe = destination.substring(TOPIC_INVITE_ENDPOINT.length()); - long memberIdToSubscribe = convertToLong(rawMemberIdToSubscribe); - - if (memberId != memberIdToSubscribe) { - throw new FriendoglyWebSocketException("자신의 초대 endpoint만 구독할 수 있습니다."); - } - } +// private void validateInviteSubscription(long memberId, String destination) { +// String rawMemberIdToSubscribe = destination.substring(TOPIC_INVITE_ENDPOINT.length()); +// long memberIdToSubscribe = convertToLong(rawMemberIdToSubscribe); +// +// if (memberId != memberIdToSubscribe) { +// throw new FriendoglyWebSocketException("자신의 초대 endpoint만 구독할 수 있습니다."); +// } +// } private void validateChatSubscription(long memberId, String destination) { String rawChatRoomId = destination.substring(TOPIC_CHAT_ENDPOINT.length()); diff --git a/backend/src/main/java/com/happy/friendogly/config/WebSocketRabbitMqConfig.java b/backend/src/main/java/com/happy/friendogly/config/WebSocketRabbitMqConfig.java new file mode 100644 index 000000000..20c624202 --- /dev/null +++ b/backend/src/main/java/com/happy/friendogly/config/WebSocketRabbitMqConfig.java @@ -0,0 +1,79 @@ +package com.happy.friendogly.config; + +import com.happy.friendogly.auth.WebSocketArgumentResolver; +import com.happy.friendogly.auth.service.jwt.JwtProvider; +import java.util.List; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; +import org.springframework.messaging.simp.config.ChannelRegistration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.util.AntPathMatcher; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +@Profile("!local") +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketRabbitMqConfig implements WebSocketMessageBrokerConfigurer { + + @Value("${spring.rabbitmq.host}") + private String host; + + @Value("${spring.rabbitmq.username}") + private String username; + + @Value("${spring.rabbitmq.password}") + private String password; + + @Value("${spring.rabbitmq.stomp-port}") + private int port; + + private final WebSocketInterceptor webSocketInterceptor; + private final WebSocketErrorHandler webSocketErrorHandler; + private final JwtProvider jwtProvider; + + public WebSocketRabbitMqConfig(WebSocketInterceptor webSocketInterceptor, + WebSocketErrorHandler webSocketErrorHandler, + JwtProvider jwtProvider + ) { + this.webSocketInterceptor = webSocketInterceptor; + this.webSocketErrorHandler = webSocketErrorHandler; + this.jwtProvider = jwtProvider; + } + + @Override + public void configureMessageBroker(MessageBrokerRegistry registry) { + registry.setPathMatcher(new AntPathMatcher(".")); + + registry.enableStompBrokerRelay("/queue", "/topic", "/exchange", "/amq/queue") + .setClientLogin(username) + .setClientPasscode(password) + .setSystemLogin(username) + .setSystemPasscode(password) + .setRelayHost(host) + .setRelayPort(port) + .setVirtualHost("/"); + + registry.setApplicationDestinationPrefixes("/publish"); + } + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + registry.addEndpoint("/connect") + .setAllowedOrigins("*"); + registry.setErrorHandler(webSocketErrorHandler); + } + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(new WebSocketArgumentResolver(jwtProvider)); + } + + @Override + public void configureClientInboundChannel(ChannelRegistration registration) { + registration.interceptors(webSocketInterceptor); + } +} diff --git a/backend/src/main/resources/application-dev.yml b/backend/src/main/resources/application-dev.yml index d5a47d70c..d24ecadcb 100644 --- a/backend/src/main/resources/application-dev.yml +++ b/backend/src/main/resources/application-dev.yml @@ -32,6 +32,11 @@ file: path: firebase-friendogly-private-key.json spring: + rabbitmq: + host: localhost + username: ${RABBITMQ_USERNAME} + password: ${RABBITMQ_PASSWORD} + stomp-port: 61613 datasource: driver-class-name: org.h2.Driver url: jdbc:h2:file:/home/ubuntu/h2_test_data diff --git a/backend/src/main/resources/application-local.yml b/backend/src/main/resources/application-local.yml index 9be8a20fd..271845c44 100644 --- a/backend/src/main/resources/application-local.yml +++ b/backend/src/main/resources/application-local.yml @@ -26,7 +26,10 @@ logging.level.org: SQL: debug orm.jdbc.bind: trace - file: firebase: path: firebase-friendogly-private-key.json +management: + health: + rabbit: + enabled: false diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml index c351e56c8..a795bef2c 100644 --- a/backend/src/main/resources/application-prod.yml +++ b/backend/src/main/resources/application-prod.yml @@ -32,6 +32,11 @@ file: path: firebase-friendogly-private-key.json spring: + rabbitmq: + host: ${RABBITMQ_PROD_HOST} # TODO: 추가 + username: ${RABBITMQ_PROD_USERNAME} + password: ${RABBITMQ_PROD_PASSWORD} + stomp-port: 61613 sql: init: data-locations: From 4653d6afd8d25732cb38334e0eace0a768c39027 Mon Sep 17 00:00:00 2001 From: takoyakimchi Date: Wed, 9 Oct 2024 21:07:08 +0900 Subject: [PATCH 2/9] =?UTF-8?q?fix:=20=EB=88=84=EB=9D=BD=EB=90=9C=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/build.gradle b/backend/build.gradle index 2bf2a32e9..59e75b316 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -53,7 +53,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-websocket' - implementation 'org.springframework.boot:spring-boot-starter-amqp' + implementation 'org.springframework.boot:spring-boot-starter-amqp' // rabbitmq + implementation 'org.springframework.boot:spring-boot-starter-reactor-netty' // rabbitmq implementation 'io.jsonwebtoken:jjwt:0.12.6' implementation 'io.micrometer:micrometer-registry-prometheus' implementation "org.springframework.boot:spring-boot-starter-actuator" From 70caeba0c8c3c016f09fddb209c20a81d0862210 Mon Sep 17 00:00:00 2001 From: takoyakimchi Date: Wed, 9 Oct 2024 21:18:50 +0900 Subject: [PATCH 3/9] =?UTF-8?q?fix:=20converAndSend=20=EC=8B=9C=EA=B7=B8?= =?UTF-8?q?=EB=8B=88=EC=B2=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/happy/friendogly/chat/config/ChatTemplate.java | 2 +- .../happy/friendogly/chat/config/InMemoryChatTemplate.java | 6 ++++-- .../happy/friendogly/chat/config/RabbitChatTemplate.java | 4 ++-- .../happy/friendogly/chat/service/ChatCommandService.java | 6 +++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/com/happy/friendogly/chat/config/ChatTemplate.java b/backend/src/main/java/com/happy/friendogly/chat/config/ChatTemplate.java index d230a8f06..e6061d011 100644 --- a/backend/src/main/java/com/happy/friendogly/chat/config/ChatTemplate.java +++ b/backend/src/main/java/com/happy/friendogly/chat/config/ChatTemplate.java @@ -5,5 +5,5 @@ @Component public interface ChatTemplate { - void convertAndSend(String destination, Object payload); + void convertAndSend(Long chatRoomId, Object payload); } diff --git a/backend/src/main/java/com/happy/friendogly/chat/config/InMemoryChatTemplate.java b/backend/src/main/java/com/happy/friendogly/chat/config/InMemoryChatTemplate.java index f7cbcee9e..b02fe0ba4 100644 --- a/backend/src/main/java/com/happy/friendogly/chat/config/InMemoryChatTemplate.java +++ b/backend/src/main/java/com/happy/friendogly/chat/config/InMemoryChatTemplate.java @@ -8,6 +8,8 @@ @Profile("local") public class InMemoryChatTemplate implements ChatTemplate { + private static final String TOPIC_CHAT_PREFIX = "/exchange/chat.exchange/room."; + private final SimpMessagingTemplate template; public InMemoryChatTemplate(SimpMessagingTemplate template) { @@ -15,7 +17,7 @@ public InMemoryChatTemplate(SimpMessagingTemplate template) { } @Override - public void convertAndSend(String destination, Object payload) { - template.convertAndSend(destination, payload); + public void convertAndSend(Long chatRoomId, Object payload) { + template.convertAndSend(TOPIC_CHAT_PREFIX + chatRoomId, payload); } } diff --git a/backend/src/main/java/com/happy/friendogly/chat/config/RabbitChatTemplate.java b/backend/src/main/java/com/happy/friendogly/chat/config/RabbitChatTemplate.java index f976dcc0d..efe554c3f 100644 --- a/backend/src/main/java/com/happy/friendogly/chat/config/RabbitChatTemplate.java +++ b/backend/src/main/java/com/happy/friendogly/chat/config/RabbitChatTemplate.java @@ -15,7 +15,7 @@ public RabbitChatTemplate(RabbitTemplate rabbitTemplate) { } @Override - public void convertAndSend(String destination, Object payload) { - rabbitTemplate.convertAndSend(destination, payload); + public void convertAndSend(Long chatRoomId, Object payload) { + rabbitTemplate.convertAndSend("chat.exchange", "room." + chatRoomId, payload); } } diff --git a/backend/src/main/java/com/happy/friendogly/chat/service/ChatCommandService.java b/backend/src/main/java/com/happy/friendogly/chat/service/ChatCommandService.java index b2782becd..37a6369b8 100644 --- a/backend/src/main/java/com/happy/friendogly/chat/service/ChatCommandService.java +++ b/backend/src/main/java/com/happy/friendogly/chat/service/ChatCommandService.java @@ -24,7 +24,6 @@ @Transactional public class ChatCommandService { - private static final String TOPIC_CHAT_PREFIX = "/exchange/chat.exchange/room."; private static final String EMPTY_CONTENT = ""; private final MemberRepository memberRepository; @@ -71,9 +70,10 @@ public void sendLeave(Long senderMemberId, Long chatRoomId) { } private void sendAndSave(MessageType messageType, String content, ChatRoom chatRoom, Member senderMember) { - ChatMessageSocketResponse chat = new ChatMessageSocketResponse(messageType, content, senderMember, LocalDateTime.now()); + ChatMessageSocketResponse chat = new ChatMessageSocketResponse(messageType, content, senderMember, + LocalDateTime.now()); notificationService.sendChatNotification(chatRoom.getId(), chat); - chatTemplate.convertAndSend(TOPIC_CHAT_PREFIX + chatRoom.getId(), chat); + chatTemplate.convertAndSend(chatRoom.getId(), chat); chatMessageRepository.save(new ChatMessage(chatRoom, messageType, senderMember, content)); } } From 8a618bad56a457f4139c8cc00e5801bebc7be8b4 Mon Sep 17 00:00:00 2001 From: takoyakimchi Date: Thu, 10 Oct 2024 16:54:08 +0900 Subject: [PATCH 4/9] =?UTF-8?q?refactor:=20RabbitMQ=20=ED=8F=AC=ED=8A=B8?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/application-dev.yml | 5 +++-- backend/src/main/resources/application-prod.yml | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/backend/src/main/resources/application-dev.yml b/backend/src/main/resources/application-dev.yml index d24ecadcb..22e5bfda3 100644 --- a/backend/src/main/resources/application-dev.yml +++ b/backend/src/main/resources/application-dev.yml @@ -33,10 +33,11 @@ file: spring: rabbitmq: - host: localhost + host: ${RABBITMQ_HOST} username: ${RABBITMQ_USERNAME} password: ${RABBITMQ_PASSWORD} - stomp-port: 61613 + port: 3100 # initial connection port: use 3100 instead of 5672 + stomp-port: 9100 # STOMP port: use 9100 instead of 61613 datasource: driver-class-name: org.h2.Driver url: jdbc:h2:file:/home/ubuntu/h2_test_data diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml index a795bef2c..dabcfc146 100644 --- a/backend/src/main/resources/application-prod.yml +++ b/backend/src/main/resources/application-prod.yml @@ -33,10 +33,11 @@ file: spring: rabbitmq: - host: ${RABBITMQ_PROD_HOST} # TODO: 추가 + host: ${RABBITMQ_PROD_HOST} username: ${RABBITMQ_PROD_USERNAME} password: ${RABBITMQ_PROD_PASSWORD} - stomp-port: 61613 + port: 3100 # initial connection port: use 3100 instead of 5672 + stomp-port: 9100 # STOMP port: use 9100 instead of 61613 sql: init: data-locations: From 2577ee1af4f43c6b56e2c112f74070d76f53e511 Mon Sep 17 00:00:00 2001 From: takoyakimchi Date: Thu, 10 Oct 2024 17:06:29 +0900 Subject: [PATCH 5/9] =?UTF-8?q?chore:=20CD=20=EC=8A=A4=ED=81=AC=EB=A6=BD?= =?UTF-8?q?=ED=8A=B8=EC=97=90=20RabbitMQ=20=EA=B4=80=EB=A0=A8=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=EB=B3=80=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/backend-cd-workflow-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backend-cd-workflow-prod.yml b/.github/workflows/backend-cd-workflow-prod.yml index fb127b8d7..f44011278 100644 --- a/.github/workflows/backend-cd-workflow-prod.yml +++ b/.github/workflows/backend-cd-workflow-prod.yml @@ -63,7 +63,7 @@ jobs: cd backend/build/libs echo "File creation time(KR-09:00):" ls -l --time=ctime friendogly-0.0.1-SNAPSHOT.jar - sudo nohup java -jar friendogly-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod --jwt.secret-key=${{ secrets.JWT_SECRET_KEY }} --jwt.access-expiration-time=${{ secrets.JWT_ACCESS_EXPIRATION_TIME }} --jwt.refresh-expiration-time=${{ secrets.JWT_REFRESH_EXPIRATION_TIME }} --kakao.admin-key=${{ secrets.KAKAO_ADMIN_KEY }} --spring.datasource.writer.hikari.jdbc-url=${{ secrets.MYSQL_RDS_WRITER_URL }} --spring.datasource.writer.hikari.username=${{ secrets.MYSQL_RDS_USERNAME }} --spring.datasource.writer.hikari.password=${{ secrets.MYSQL_RDS_PASSWORD }} --spring.datasource.reader.hikari.jdbc-url=${{ secrets.MYSQL_RDS_READER_URL }} --spring.datasource.reader.hikari.username=${{ secrets.MYSQL_RDS_USERNAME }} --spring.datasource.reader.hikari.password=${{ secrets.MYSQL_PASSWORD }} & + sudo nohup java -jar friendogly-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod --jwt.secret-key=${{ secrets.JWT_SECRET_KEY }} --jwt.access-expiration-time=${{ secrets.JWT_ACCESS_EXPIRATION_TIME }} --jwt.refresh-expiration-time=${{ secrets.JWT_REFRESH_EXPIRATION_TIME }} --kakao.admin-key=${{ secrets.KAKAO_ADMIN_KEY }} --spring.datasource.writer.hikari.jdbc-url=${{ secrets.MYSQL_RDS_WRITER_URL }} --spring.datasource.writer.hikari.username=${{ secrets.MYSQL_RDS_USERNAME }} --spring.datasource.writer.hikari.password=${{ secrets.MYSQL_RDS_PASSWORD }} --spring.datasource.reader.hikari.jdbc-url=${{ secrets.MYSQL_RDS_READER_URL }} --spring.datasource.reader.hikari.username=${{ secrets.MYSQL_RDS_USERNAME }} --spring.datasource.reader.hikari.password=${{ secrets.MYSQL_PASSWORD }} --spring.rabbitmq.host=${{ secrets.RABBITMQ_PROD_HOST }} --spring.rabbitmq.username=${{ secrets.RABBITMQ_PROD_USERNAME }} --spring.rabbitmq.password=${{ secrets.RABBITMQ_PROD_PASSWORD }} & echo "start backend server" - name: Wait until WAS being started From ce25dc98ed0577a5722b7dd8bd73344a7d823b68 Mon Sep 17 00:00:00 2001 From: takoyakimchi Date: Thu, 10 Oct 2024 21:34:41 +0900 Subject: [PATCH 6/9] =?UTF-8?q?refactor:=20dev=20=ED=99=98=EA=B2=BD?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=ED=8F=AC=ED=8A=B8=20default=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/application-dev.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/resources/application-dev.yml b/backend/src/main/resources/application-dev.yml index 22e5bfda3..371de481f 100644 --- a/backend/src/main/resources/application-dev.yml +++ b/backend/src/main/resources/application-dev.yml @@ -36,8 +36,8 @@ spring: host: ${RABBITMQ_HOST} username: ${RABBITMQ_USERNAME} password: ${RABBITMQ_PASSWORD} - port: 3100 # initial connection port: use 3100 instead of 5672 - stomp-port: 9100 # STOMP port: use 9100 instead of 61613 + port: 5672 + stomp-port: 61613 datasource: driver-class-name: org.h2.Driver url: jdbc:h2:file:/home/ubuntu/h2_test_data From 007feeb94c2bd2b9cf637249e186f80c10dd87cc Mon Sep 17 00:00:00 2001 From: takoyakimchi Date: Fri, 11 Oct 2024 12:26:30 +0900 Subject: [PATCH 7/9] =?UTF-8?q?chore:=20dev=20CD=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A6=BD=ED=8A=B8=20=ED=99=98=EA=B2=BD=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/backend-cd-workflow-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backend-cd-workflow-dev.yml b/.github/workflows/backend-cd-workflow-dev.yml index a155c2294..4ee974c25 100644 --- a/.github/workflows/backend-cd-workflow-dev.yml +++ b/.github/workflows/backend-cd-workflow-dev.yml @@ -81,5 +81,5 @@ jobs: cd backend/build/libs echo "File creation time(KR-09:00):" ls -l --time=ctime friendogly-0.0.1-SNAPSHOT.jar - sudo nohup java -jar friendogly-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev --jwt.secret-key=${{ secrets.JWT_SECRET_KEY }} --jwt.access-expiration-time=${{ secrets.JWT_ACCESS_EXPIRATION_TIME }} --jwt.refresh-expiration-time=${{ secrets.JWT_REFRESH_EXPIRATION_TIME }} --kakao.admin-key=${{ secrets.KAKAO_ADMIN_KEY }} --spring.datasource.username=${{ secrets.H2_CONSOLE_USERNAME }} --spring.datasource.password=${{ secrets.H2_CONSOLE_PASSWORD }} --spring.h2.console.path=${{ secrets.H2_CONSOLE_ENDPOINT }} & + sudo nohup java -jar friendogly-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev --jwt.secret-key=${{ secrets.JWT_SECRET_KEY }} --jwt.access-expiration-time=${{ secrets.JWT_ACCESS_EXPIRATION_TIME }} --jwt.refresh-expiration-time=${{ secrets.JWT_REFRESH_EXPIRATION_TIME }} --kakao.admin-key=${{ secrets.KAKAO_ADMIN_KEY }} --spring.datasource.username=${{ secrets.H2_CONSOLE_USERNAME }} --spring.datasource.password=${{ secrets.H2_CONSOLE_PASSWORD }} --spring.h2.console.path=${{ secrets.H2_CONSOLE_ENDPOINT }} --spring.rabbitmq.host=${{ secrets.RABBITMQ_HOST }} --spring.rabbitmq.username=${{ secrets.RABBITMQ_USERNAME }} --spring.rabbitmq.password=${{ secrets.RABBITMQ_PASSWORD }} & echo "start backend server" From cb9d79271cb3b4a839986b4de038070cd64e1c8d Mon Sep 17 00:00:00 2001 From: takoyakimchi Date: Fri, 11 Oct 2024 12:37:42 +0900 Subject: [PATCH 8/9] =?UTF-8?q?refactor:=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=AA=85=20rename?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{WebSocketConfig.java => WebSocketLocalConfig.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename backend/src/main/java/com/happy/friendogly/config/{WebSocketConfig.java => WebSocketLocalConfig.java} (94%) diff --git a/backend/src/main/java/com/happy/friendogly/config/WebSocketConfig.java b/backend/src/main/java/com/happy/friendogly/config/WebSocketLocalConfig.java similarity index 94% rename from backend/src/main/java/com/happy/friendogly/config/WebSocketConfig.java rename to backend/src/main/java/com/happy/friendogly/config/WebSocketLocalConfig.java index b2569cc56..f797dc4ee 100644 --- a/backend/src/main/java/com/happy/friendogly/config/WebSocketConfig.java +++ b/backend/src/main/java/com/happy/friendogly/config/WebSocketLocalConfig.java @@ -15,13 +15,13 @@ @Profile("local") @Configuration @EnableWebSocketMessageBroker -public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { +public class WebSocketLocalConfig implements WebSocketMessageBrokerConfigurer { private final WebSocketInterceptor webSocketInterceptor; private final WebSocketErrorHandler webSocketErrorHandler; private final JwtProvider jwtProvider; - public WebSocketConfig( + public WebSocketLocalConfig( WebSocketInterceptor webSocketInterceptor, WebSocketErrorHandler webSocketErrorHandler, JwtProvider jwtProvider From d057381101ee2ca38cc0060d92d785d4196c0095 Mon Sep 17 00:00:00 2001 From: takoyakimchi Date: Fri, 18 Oct 2024 15:45:05 +0900 Subject: [PATCH 9/9] =?UTF-8?q?fix:=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20bean=20=EB=93=B1=EB=A1=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/happy/friendogly/chat/config/ChatTemplate.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/backend/src/main/java/com/happy/friendogly/chat/config/ChatTemplate.java b/backend/src/main/java/com/happy/friendogly/chat/config/ChatTemplate.java index e6061d011..f82a53b2c 100644 --- a/backend/src/main/java/com/happy/friendogly/chat/config/ChatTemplate.java +++ b/backend/src/main/java/com/happy/friendogly/chat/config/ChatTemplate.java @@ -1,8 +1,5 @@ package com.happy.friendogly.chat.config; -import org.springframework.stereotype.Component; - -@Component public interface ChatTemplate { void convertAndSend(Long chatRoomId, Object payload);