-
Notifications
You must be signed in to change notification settings - Fork 3
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
[BE] fix: 외부 메시지 큐(RabbitMQ) 도입, 분산 환경 채팅 안 되던 버그 수정 #630
Changes from 8 commits
5d59ab0
4653d6a
70caeba
8a618ba
2577ee1
ce25dc9
007feeb
cb9d792
d057381
6f456ce
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 |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.happy.friendogly.chat.config; | ||
|
||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public interface ChatTemplate { | ||
|
||
void convertAndSend(Long chatRoomId, Object payload); | ||
} | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
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 static final String TOPIC_CHAT_PREFIX = "/exchange/chat.exchange/room."; | ||
|
||
private final SimpMessagingTemplate template; | ||
|
||
public InMemoryChatTemplate(SimpMessagingTemplate template) { | ||
this.template = template; | ||
} | ||
|
||
@Override | ||
public void convertAndSend(Long chatRoomId, Object payload) { | ||
template.convertAndSend(TOPIC_CHAT_PREFIX + chatRoomId, payload); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(Long chatRoomId, Object payload) { | ||
rabbitTemplate.convertAndSend("chat.exchange", "room." + chatRoomId, payload); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,39 +15,35 @@ | |
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 | ||
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()) | ||
// ); | ||
Comment on lines
+41
to
+44
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. 일대일 채팅방 만들때 다시 로직 살릴예정! |
||
} | ||
|
||
@MessageMapping("/chat/{chatRoomId}") | ||
@MessageMapping("chat.{chatRoomId}") | ||
public void sendMessage( | ||
@WebSocketAuth Long memberId, | ||
@DestinationVariable(value = "chatRoomId") Long chatRoomId, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
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. 오 이런게 있군용 |
||
@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)); | ||
} | ||
Comment on lines
+94
to
+107
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. 기본 설정만으로는 LocalDateTime을 직렬화하지 못해서 작성함 |
||
} |
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.
interface 빈으로 등록하고 계십니당
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.
반영 완