From 3bb73de7a73a9a023021e14389533e6e12948fe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=8A=B9=EC=A7=84?= Date: Fri, 15 Mar 2024 19:18:13 +0900 Subject: [PATCH] [FEAT] apply rabbitmq dead letter queue --- .../config/rabbitmq/RabbitMQConfig.java | 18 +++++---------- .../domain/deadletter/DeadLetterConsumer.java | 4 ++-- .../messagequeue/FcmConsumer.java | 22 ++++++++++++------- .../path/client/SearchCarPathClient.java | 2 +- .../path/controller/PathController.java | 12 ++++++++++ .../domain/path/service/PathService.java | 21 ++++++++++++++++-- .../place/client/SurroundPlaceClient.java | 2 +- .../plan/client/SearchDestinationClient.java | 2 +- .../domain/plan/service/PlanService.java | 2 +- .../global/advice/GlobalErrorAdvice.java | 7 +++++- .../src/main/resources/application-dev.yml | 8 ++++++- .../src/main/resources/application-prod.yml | 6 +++++ 12 files changed, 76 insertions(+), 30 deletions(-) diff --git a/backend/src/main/java/com/twtw/backend/config/rabbitmq/RabbitMQConfig.java b/backend/src/main/java/com/twtw/backend/config/rabbitmq/RabbitMQConfig.java index ea5d8f31..e9b95665 100644 --- a/backend/src/main/java/com/twtw/backend/config/rabbitmq/RabbitMQConfig.java +++ b/backend/src/main/java/com/twtw/backend/config/rabbitmq/RabbitMQConfig.java @@ -27,11 +27,8 @@ public class RabbitMQConfig { @Bean public Queue locationQueue() { return QueueBuilder.durable(RabbitMQConstant.LOCATION_QUEUE.getName()) - .withArgument( - "x-dead-letter-exchange", RabbitMQConstant.DEAD_LETTER_EXCHANGE.getName()) - .withArgument( - "x-dead-letter-routing-key", - RabbitMQConstant.DEAD_LETTER_ROUTING_KEY.getName()) + .deadLetterExchange(RabbitMQConstant.DEAD_LETTER_EXCHANGE.getName()) + .deadLetterRoutingKey(RabbitMQConstant.DEAD_LETTER_ROUTING_KEY.getName()) .build(); } @@ -50,11 +47,8 @@ public Binding locationBinding() { @Bean public Queue notificationQueue() { return QueueBuilder.durable(RabbitMQConstant.NOTIFICATION_QUEUE.getName()) - .withArgument( - "x-dead-letter-exchange", RabbitMQConstant.DEAD_LETTER_EXCHANGE.getName()) - .withArgument( - "x-dead-letter-routing-key", - RabbitMQConstant.DEAD_LETTER_ROUTING_KEY.getName()) + .deadLetterExchange(RabbitMQConstant.DEAD_LETTER_EXCHANGE.getName()) + .deadLetterRoutingKey(RabbitMQConstant.DEAD_LETTER_ROUTING_KEY.getName()) .build(); } @@ -76,8 +70,8 @@ public Queue deadLetterQueue() { } @Bean - public TopicExchange deadLetterExchange() { - return new TopicExchange(RabbitMQConstant.DEAD_LETTER_EXCHANGE.getName()); + public DirectExchange deadLetterExchange() { + return new DirectExchange(RabbitMQConstant.DEAD_LETTER_EXCHANGE.getName()); } @Bean diff --git a/backend/src/main/java/com/twtw/backend/domain/deadletter/DeadLetterConsumer.java b/backend/src/main/java/com/twtw/backend/domain/deadletter/DeadLetterConsumer.java index aa400460..947fcff2 100644 --- a/backend/src/main/java/com/twtw/backend/domain/deadletter/DeadLetterConsumer.java +++ b/backend/src/main/java/com/twtw/backend/domain/deadletter/DeadLetterConsumer.java @@ -1,7 +1,7 @@ package com.twtw.backend.domain.deadletter; +import com.twtw.backend.domain.notification.dto.NotificationRequest; import lombok.extern.slf4j.Slf4j; - import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -20,7 +20,7 @@ public DeadLetterConsumer(@Value("${slack.url}") final String slackUrl) { } @RabbitListener(queues = "deadletter.queue") - public void handleDeadLetterMessage(final String message) { + public void handleDeadLetterMessage(final NotificationRequest message) { log.error("Dead letter received: {}", message); webClient .post() diff --git a/backend/src/main/java/com/twtw/backend/domain/notification/messagequeue/FcmConsumer.java b/backend/src/main/java/com/twtw/backend/domain/notification/messagequeue/FcmConsumer.java index 93b8c1d1..ea20e229 100644 --- a/backend/src/main/java/com/twtw/backend/domain/notification/messagequeue/FcmConsumer.java +++ b/backend/src/main/java/com/twtw/backend/domain/notification/messagequeue/FcmConsumer.java @@ -1,15 +1,15 @@ package com.twtw.backend.domain.notification.messagequeue; import com.google.firebase.messaging.FirebaseMessaging; -import com.google.firebase.messaging.FirebaseMessagingException; +import com.rabbitmq.client.Channel; import com.twtw.backend.domain.notification.dto.NotificationRequest; - -import lombok.extern.slf4j.Slf4j; - import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.support.AmqpHeaders; +import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; -@Slf4j +import java.io.IOException; + @Component public class FcmConsumer { private final FirebaseMessaging firebaseMessaging; @@ -19,8 +19,14 @@ public FcmConsumer(FirebaseMessaging firebaseMessaging) { } @RabbitListener(queues = "notification.queue") - public void sendNotification(final NotificationRequest request) - throws FirebaseMessagingException { - firebaseMessaging.send(request.toMessage()); + public void sendNotification( + final NotificationRequest request, + final Channel channel, + @Header(AmqpHeaders.DELIVERY_TAG) final long tag) throws IOException { + try { + firebaseMessaging.send(request.toMessage()); + } catch (final Exception e) { + channel.basicNack(tag, false, false); + } } } diff --git a/backend/src/main/java/com/twtw/backend/domain/path/client/SearchCarPathClient.java b/backend/src/main/java/com/twtw/backend/domain/path/client/SearchCarPathClient.java index 1208c822..1347c9d1 100644 --- a/backend/src/main/java/com/twtw/backend/domain/path/client/SearchCarPathClient.java +++ b/backend/src/main/java/com/twtw/backend/domain/path/client/SearchCarPathClient.java @@ -50,7 +50,7 @@ private URI getPathUri(final SearchCarPathRequest request, final UriBuilder uriB } @Override - @CircuitBreaker(name = "backend-a", fallbackMethod = "fallback") + @CircuitBreaker(name = "backend-b", fallbackMethod = "fallback") public SearchCarPathResponse request(final SearchCarPathRequest request) { return webClient .get() diff --git a/backend/src/main/java/com/twtw/backend/domain/path/controller/PathController.java b/backend/src/main/java/com/twtw/backend/domain/path/controller/PathController.java index 10e9dc5e..948bc106 100644 --- a/backend/src/main/java/com/twtw/backend/domain/path/controller/PathController.java +++ b/backend/src/main/java/com/twtw/backend/domain/path/controller/PathController.java @@ -29,9 +29,21 @@ public ResponseEntity searchCarPath( return ResponseEntity.ok(pathService.searchCarPath(request)); } + @PostMapping("/search/car/cache") + public ResponseEntity searchCarPathWithCache( + @RequestBody @Valid SearchCarPathRequest request) { + return ResponseEntity.ok(pathService.searchCarPathWithCache(request)); + } + @PostMapping("/search/ped") public ResponseEntity searchPedPath( @RequestBody @Valid SearchPedPathRequest request) { return ResponseEntity.ok(pathService.searchPedPath(request)); } + + @PostMapping("/search/ped/cache") + public ResponseEntity searchPedPathWithCache( + @RequestBody @Valid SearchPedPathRequest request) { + return ResponseEntity.ok(pathService.searchPedPathWithCache(request)); + } } diff --git a/backend/src/main/java/com/twtw/backend/domain/path/service/PathService.java b/backend/src/main/java/com/twtw/backend/domain/path/service/PathService.java index 6022c2e2..d6cf960c 100644 --- a/backend/src/main/java/com/twtw/backend/domain/path/service/PathService.java +++ b/backend/src/main/java/com/twtw/backend/domain/path/service/PathService.java @@ -6,6 +6,7 @@ import com.twtw.backend.domain.path.dto.client.ped.SearchPedPathResponse; import com.twtw.backend.global.client.MapClient; +import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -23,18 +24,34 @@ public PathService( @Cacheable( value = "carPath", - key = "'searchCarPath'.concat(#request)", + key = "'searchCarPath'.concat(#request.toString())", cacheManager = "cacheManager", unless = "#result.code != 0") + public SearchCarPathResponse searchCarPathWithCache(final SearchCarPathRequest request) { + return carPathClient.request(request); + } + + @CacheEvict( + value = "carPath", + key = "'searchCarPath'.concat(#request.toString())", + cacheManager = "cacheManager") public SearchCarPathResponse searchCarPath(final SearchCarPathRequest request) { return carPathClient.request(request); } @Cacheable( value = "pedPath", - key = "'searchPedPath'.concat(#request)", + key = "'searchPedPath'.concat(#request.toString())", cacheManager = "cacheManager", unless = "#result.features.size() <= 0") + public SearchPedPathResponse searchPedPathWithCache(final SearchPedPathRequest request) { + return pedPathClient.request(request); + } + + @CacheEvict( + value = "pedPath", + key = "'searchPedPath'.concat(#request.toString())", + cacheManager = "cacheManager") public SearchPedPathResponse searchPedPath(final SearchPedPathRequest request) { return pedPathClient.request(request); } diff --git a/backend/src/main/java/com/twtw/backend/domain/place/client/SurroundPlaceClient.java b/backend/src/main/java/com/twtw/backend/domain/place/client/SurroundPlaceClient.java index ef0245bd..e8f355f5 100644 --- a/backend/src/main/java/com/twtw/backend/domain/place/client/SurroundPlaceClient.java +++ b/backend/src/main/java/com/twtw/backend/domain/place/client/SurroundPlaceClient.java @@ -32,7 +32,7 @@ public SurroundPlaceClient(final KakaoProperties kakaoProperties) { } @Override - @CircuitBreaker(name = "backend-a", fallbackMethod = "fallback") + @CircuitBreaker(name = "backend-c", fallbackMethod = "fallback") public SurroundPlaceResponse request(final SurroundPlaceRequest request) { return webClient .get() diff --git a/backend/src/main/java/com/twtw/backend/domain/plan/client/SearchDestinationClient.java b/backend/src/main/java/com/twtw/backend/domain/plan/client/SearchDestinationClient.java index 04a583a0..f795559b 100644 --- a/backend/src/main/java/com/twtw/backend/domain/plan/client/SearchDestinationClient.java +++ b/backend/src/main/java/com/twtw/backend/domain/plan/client/SearchDestinationClient.java @@ -32,7 +32,7 @@ public SearchDestinationClient(final KakaoProperties kakaoProperties) { } @Override - @CircuitBreaker(name = "backend-a", fallbackMethod = "fallback") + @CircuitBreaker(name = "backend-d", fallbackMethod = "fallback") public SearchDestinationResponse request(final SearchDestinationRequest request) { return webClient .get() diff --git a/backend/src/main/java/com/twtw/backend/domain/plan/service/PlanService.java b/backend/src/main/java/com/twtw/backend/domain/plan/service/PlanService.java index 02fa1f79..ac46a6c5 100644 --- a/backend/src/main/java/com/twtw/backend/domain/plan/service/PlanService.java +++ b/backend/src/main/java/com/twtw/backend/domain/plan/service/PlanService.java @@ -57,7 +57,7 @@ public class PlanService { @Cacheable( value = "planDestination", - key = "'searchPlanDestination'.concat(#request)", + key = "'searchPlanDestination'.concat(#request.toString())", cacheManager = "cacheManager", unless = "#result.results.size() <= 0") public PlanDestinationResponse searchPlanDestination(final SearchDestinationRequest request) { diff --git a/backend/src/main/java/com/twtw/backend/global/advice/GlobalErrorAdvice.java b/backend/src/main/java/com/twtw/backend/global/advice/GlobalErrorAdvice.java index e7deb1ac..0513a437 100644 --- a/backend/src/main/java/com/twtw/backend/global/advice/GlobalErrorAdvice.java +++ b/backend/src/main/java/com/twtw/backend/global/advice/GlobalErrorAdvice.java @@ -3,7 +3,7 @@ import com.twtw.backend.global.exception.AuthorityException; import com.twtw.backend.global.exception.EntityNotFoundException; import com.twtw.backend.global.exception.WebClientResponseException; - +import io.github.resilience4j.circuitbreaker.CallNotPermittedException; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -30,4 +30,9 @@ public ResponseEntity authority(final AuthorityException e) { public ResponseEntity interrupted(final InterruptedException e) { return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage())); } + + @ExceptionHandler(CallNotPermittedException.class) + public ResponseEntity callNotPermitted(final CallNotPermittedException e) { + return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage())); + } } diff --git a/backend/src/main/resources/application-dev.yml b/backend/src/main/resources/application-dev.yml index 59426285..40836840 100644 --- a/backend/src/main/resources/application-dev.yml +++ b/backend/src/main/resources/application-dev.yml @@ -56,4 +56,10 @@ resilience4j.circuitbreaker: waitDurationInOpenState: 10000 instances: backend-a: - base-config: default \ No newline at end of file + base-config: default + backend-b: + base-config: default + backend-c: + base-config: default + backend-d: + base-config: default diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml index 3489da36..740a2f1d 100644 --- a/backend/src/main/resources/application-prod.yml +++ b/backend/src/main/resources/application-prod.yml @@ -66,6 +66,12 @@ resilience4j.circuitbreaker: instances: backend-a: base-config: default + backend-b: + base-config: default + backend-c: + base-config: default + backend-d: + base-config: default jwt: secret: ${JWT_SECRET} kakao-map: