Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@

import java.time.LocalDateTime;

import static org.example.tablenow.global.constant.TimeConstants.TIME_YYYY_MM_DD_HH_MM_SS;

@Getter
public class EventJoinResponseDto {
private final Long eventJoinId;
private final Long eventId;
private final Long storeId;
private final String storeName;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = TIME_YYYY_MM_DD_HH_MM_SS)
private final LocalDateTime eventTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = TIME_YYYY_MM_DD_HH_MM_SS)
private final LocalDateTime joinedAt;
private final String message;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,24 @@

import java.time.LocalDateTime;

import static org.example.tablenow.global.constant.TimeConstants.TIME_YYYY_MM_DD_HH_MM_SS;

@Getter
public class EventResponseDto {
private final Long eventId;
private final Long storeId;
private final String storeName;
private final String content;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = TIME_YYYY_MM_DD_HH_MM_SS)
private final LocalDateTime openAt;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = TIME_YYYY_MM_DD_HH_MM_SS)
private final LocalDateTime endAt;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = TIME_YYYY_MM_DD_HH_MM_SS)
private final LocalDateTime eventTime;
private final int limitPeople;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = TIME_YYYY_MM_DD_HH_MM_SS)
private final LocalDateTime createdAt;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = TIME_YYYY_MM_DD_HH_MM_SS)
private final LocalDateTime updatedAt;
private final EventStatus status;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.example.tablenow.domain.user.entity.User;
import org.example.tablenow.domain.user.service.UserService;
import org.example.tablenow.domain.event.message.dto.EventOpenMessage;
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

Expand Down Expand Up @@ -68,6 +69,7 @@ private void sendNotification(User user, EventOpenMessage message) {

} catch (Exception e) {
log.error("[EventOpenConsumer][Notification] 알림 전송 실패 → userId={}, eventId={}", user.getId(), message.getEventId(), e);
throw new AmqpRejectAndDontRequeueException("[DLQ] 알림 전송 실패 → DLQ로 이동", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.example.tablenow.domain.event.message.consumer;

import lombok.RequiredArgsConstructor;
import org.example.tablenow.domain.event.service.EventOpenRetryService;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import org.springframework.amqp.core.Message;

import static org.example.tablenow.global.constant.RabbitConstant.EVENT_OPEN_DLQ;

@Component
@RequiredArgsConstructor
public class EventOpenDlqReprocessor {

private final EventOpenRetryService eventOpenRetryService;

@RabbitListener(queues = EVENT_OPEN_DLQ)
public void reprocess(Message message) {
eventOpenRetryService.process(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.tablenow.global.constant.RabbitConstant;
import org.example.tablenow.domain.event.message.dto.EventOpenMessage;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import static org.example.tablenow.global.constant.RabbitConstant.EVENT_OPEN_EXCHANGE;

@Slf4j
@Component
@RequiredArgsConstructor
Expand All @@ -17,7 +18,8 @@ public class EventOpenProducer {

public void send(EventOpenMessage message) {
rabbitTemplate.convertAndSend(
RabbitConstant.EVENT_OPEN_EXCHANGE,
EVENT_OPEN_EXCHANGE,
"",
message
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.example.tablenow.domain.event.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

import static org.example.tablenow.global.constant.RabbitConstant.*;

@Slf4j
@Component
@RequiredArgsConstructor
public class EventOpenRetryService {

private final RabbitTemplate rabbitTemplate;

private static final int MAX_RETRY_COUNT = 3;

public void process(Message message) {
MessageProperties props = message.getMessageProperties();
Integer retryCount = (Integer) props.getHeaders().getOrDefault(RETRY_HEADER, 0);

if (retryCount >= MAX_RETRY_COUNT) {
log.error("[EventOpentRetryService] 재시도 최대 횟수 초과 → messageId={}, retryCount={}",
message.getMessageProperties().getMessageId(), retryCount);
return;
}

Message retryMessage = MessageBuilder
.withBody(message.getBody())
.copyProperties(props)
.setHeader(RETRY_HEADER, retryCount + 1)
.build();

rabbitTemplate.send(
EVENT_OPEN_RETRY_EXCHANGE,
EVENT_OPEN_RETRY_ROUTING_KEY,
retryMessage
);

log.info("[EventOpentRetryService] DLQ 메시지 재전송 완료 → retryCount={}, routingKey={}, message={}",
retryCount + 1, EVENT_OPEN_RETRY_ROUTING_KEY, new String(retryMessage.getBody()));
}
}
72 changes: 56 additions & 16 deletions src/main/java/org/example/tablenow/global/config/RabbitConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ public Binding vacancyDlqBinding(){
// 이벤트 오픈 Queue, Exchange, Binding
@Bean
public Queue eventOpenQueue() {
return new Queue(EVENT_OPEN_QUEUE, true);
return QueueBuilder.durable(EVENT_OPEN_QUEUE)
.withArgument("x-dead-letter-exchange", EVENT_OPEN_DLX)
.withArgument("x-dead-letter-routing-key", EVENT_OPEN_DLQ_ROUTING_KEY)
.build();
}

@Bean
Expand All @@ -70,6 +73,43 @@ public Binding eventOpenBinding() {
return bindFanout(eventOpenQueue(), eventOpenExchange());
}

// 이벤트 오픈 DLX 및 DLQ 설정
@Bean
public DirectExchange eventOpenDlx() {
return new DirectExchange(EVENT_OPEN_DLX);
}

@Bean
public Queue eventOpenDlq() {
return buildDlqQueue(EVENT_OPEN_DLQ);
}

@Bean
public Binding eventOpenDlqBinding() {
return bind(eventOpenDlq(), eventOpenDlx(), EVENT_OPEN_DLQ_ROUTING_KEY);
}

// 이벤트 오픈 RetryQueue
@Bean
public Queue eventOpenRetryQueue() {
return QueueBuilder.durable(EVENT_OPEN_RETRY_QUEUE)
.withArgument("x-message-ttl", TTL_MILLIS)
.withArgument("x-dead-letter-exchange", EVENT_OPEN_EXCHANGE)
.build();
}

@Bean
public DirectExchange eventOpenRetryExchange() {
return new DirectExchange(EVENT_OPEN_RETRY_EXCHANGE);
}

@Bean
public Binding eventOpenRetryBinding() {
return BindingBuilder.bind(eventOpenRetryQueue())
.to(eventOpenRetryExchange())
.with(EVENT_OPEN_RETRY_ROUTING_KEY);
}

// 예약 리마인드 등록 Queue, Exchange, Binding
@Bean
public Queue reminderRegisterQueue() {
Expand Down Expand Up @@ -125,7 +165,7 @@ public Binding reminderSendDlqBinding() {
@Bean
public Queue reminderSendRetryQueue() {
return QueueBuilder.durable(RESERVATION_REMINDER_SEND_RETRY_QUEUE)
.withArgument("x-message-ttl", 30000)
.withArgument("x-message-ttl", TTL_MILLIS)
.withArgument("x-dead-letter-exchange", RESERVATION_REMINDER_SEND_EXCHANGE)
.build();
}
Expand Down Expand Up @@ -171,18 +211,6 @@ public Queue storeDeleteDlq() {
return buildDlqQueue(STORE_DELETE_DLQ);
}

private Queue buildMainQueue(String queueName, String dlqRoutingKey) {
return QueueBuilder.durable(queueName)
.withArgument("x-dead-letter-exchange", STORE_DLX)
.withArgument("x-dead-letter-routing-key", dlqRoutingKey)
.withArgument("x-message-ttl", TTL_MILLIS)
.build();
}

private Queue buildDlqQueue(String dlqName) {
return QueueBuilder.durable(dlqName).build();
}

@Bean
public DirectExchange storeExchange() {
return new DirectExchange(STORE_EXCHANGE);
Expand Down Expand Up @@ -279,11 +307,23 @@ public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(Conne
return factory;
}

private Binding bindFanout(Queue queue, FanoutExchange exchange) {
return BindingBuilder.bind(queue).to(exchange);
private Queue buildMainQueue(String queueName, String dlqRoutingKey) {
return QueueBuilder.durable(queueName)
.withArgument("x-dead-letter-exchange", STORE_DLX)
.withArgument("x-dead-letter-routing-key", dlqRoutingKey)
.withArgument("x-message-ttl", TTL_MILLIS)
.build();
}

private Queue buildDlqQueue(String dlqName) {
return QueueBuilder.durable(dlqName).build();
}

private Binding bind(Queue queue, DirectExchange exchange, String routingKey) {
return BindingBuilder.bind(queue).to(exchange).with(routingKey);
}

private Binding bindFanout(Queue queue, FanoutExchange exchange) {
return BindingBuilder.bind(queue).to(exchange);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,18 @@ public class RabbitConstant {
public static final String RESERVATION_REMINDER_REGISTER_QUEUE = RESERVATION_REMINDER_PREFIX + ".register.queue";

// 이벤트 오픈 알림
public static final String EVENT_OPEN_EXCHANGE = EVENT_OPEN_PREFIX + ".fanout";
public static final String EVENT_OPEN_EXCHANGE = EVENT_OPEN_PREFIX + ".exchange";
public static final String EVENT_OPEN_QUEUE = EVENT_OPEN_PREFIX + ".queue";

public static final String EVENT_OPEN_DLX = EVENT_OPEN_PREFIX + ".dlx";
public static final String EVENT_OPEN_DLQ = EVENT_OPEN_PREFIX + ".dlq";
public static final String EVENT_OPEN_DLQ_ROUTING_KEY = EVENT_OPEN_PREFIX + ".dlq.key";

public static final String EVENT_OPEN_RETRY_QUEUE = EVENT_OPEN_PREFIX + ".retry.queue";
public static final String EVENT_OPEN_RETRY_EXCHANGE = EVENT_OPEN_PREFIX + ".retry.exchange";
public static final String EVENT_OPEN_RETRY_ROUTING_KEY = EVENT_OPEN_PREFIX + ".retry.key";

// 가게
public static final String STORE_EXCHANGE = "store.exchange";
public static final String STORE_CREATE = "store.create";
public static final String STORE_UPDATE = "store.update";
Expand All @@ -57,5 +66,7 @@ public class RabbitConstant {
public static final String CHAT_DLX = CHAT_PREFIX + ".dlx";
public static final String CHAT_DLQ = CHAT_PREFIX + ".dlq";

// 공통
public static final int TTL_MILLIS = 30000;
public static final String RETRY_HEADER = "x-retry-count";
}