From 23d300f51c5ce5522db7c92c181e895312bc7d21 Mon Sep 17 00:00:00 2001 From: coehgns Date: Sun, 31 Aug 2025 14:35:19 +0900 Subject: [PATCH 01/12] =?UTF-8?q?refactor=20(=20#34=20)=20:=20AdminGrpcCli?= =?UTF-8?q?ent=EC=97=90=EC=84=9C=20UserGrpcClient=EB=A1=9C=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/entrydsm/feed/global/utils/admin/AdminUtils.kt | 10 +++++----- .../client/UserGrpcClient.kt} | 6 +++--- .../client/dto/response/InternalAdminResponse.kt | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) rename casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/{client/AdminGrpcClient.kt => user/client/UserGrpcClient.kt} (92%) rename casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/{ => user}/client/dto/response/InternalAdminResponse.kt (73%) diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/utils/admin/AdminUtils.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/utils/admin/AdminUtils.kt index 98215f1..8ba70b0 100644 --- a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/utils/admin/AdminUtils.kt +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/utils/admin/AdminUtils.kt @@ -1,8 +1,8 @@ package hs.kr.entrydsm.feed.global.utils.admin import hs.kr.entrydsm.feed.global.exception.InvalidTokenException -import hs.kr.entrydsm.feed.infrastructure.grpc.client.AdminGrpcClient -import hs.kr.entrydsm.feed.infrastructure.grpc.client.dto.response.InternalAdminResponse +import hs.kr.entrydsm.feed.infrastructure.grpc.user.client.UserGrpcClient +import hs.kr.entrydsm.feed.infrastructure.grpc.user.client.dto.response.InternalAdminResponse import org.springframework.security.core.context.SecurityContextHolder import org.springframework.stereotype.Component import java.util.* @@ -12,11 +12,11 @@ import java.util.* * Spring Security의 SecurityContext를 사용하여 현재 인증된 관리자 정보를 조회하고, * gRPC를 통해 관리자 서비스에서 추가 정보를 조회합니다. * - * @property adminGrpcClient 관리자 정보 조회를 위한 gRPC 클라이언트 + * @property userGrpcClient 관리자 정보 조회를 위한 gRPC 클라이언트 */ @Component class AdminUtils( - private val adminGrpcClient: AdminGrpcClient + private val userGrpcClient: UserGrpcClient ) { /** @@ -28,7 +28,7 @@ class AdminUtils( * @throws hs.kr.entrydsm.feed.global.exception.InvalidTokenException 인증 토큰이 유효하지 않은 경우 * @throws io.grpc.StatusRuntimeException gRPC 통신 중 오류가 발생한 경우 */ - suspend fun getCurrentAdmin(): InternalAdminResponse = adminGrpcClient.getAdminInfoByAdminId(getCurrentAdminId()) + suspend fun getCurrentAdmin(): InternalAdminResponse = userGrpcClient.getAdminInfoByAdminId(getCurrentAdminId()) /** * 현재 인증된 관리자의 고유 식별자(UUID)를 조회합니다. diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/client/AdminGrpcClient.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/user/client/UserGrpcClient.kt similarity index 92% rename from casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/client/AdminGrpcClient.kt rename to casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/user/client/UserGrpcClient.kt index eb60c30..8cc97a4 100644 --- a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/client/AdminGrpcClient.kt +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/user/client/UserGrpcClient.kt @@ -1,8 +1,8 @@ -package hs.kr.entrydsm.feed.infrastructure.grpc.client +package hs.kr.entrydsm.feed.infrastructure.grpc.user.client import hs.kr.entrydsm.casper.admin.proto.AdminServiceGrpc import hs.kr.entrydsm.casper.admin.proto.AdminServiceProto -import hs.kr.entrydsm.feed.infrastructure.grpc.client.dto.response.InternalAdminResponse +import hs.kr.entrydsm.feed.infrastructure.grpc.user.client.dto.response.InternalAdminResponse import io.grpc.Channel import io.grpc.stub.StreamObserver import kotlinx.coroutines.suspendCancellableCoroutine @@ -18,7 +18,7 @@ import kotlin.coroutines.resumeWithException * @property channel gRPC 통신을 위한 채널 (user-service로 자동 주입됨) */ @Component -class AdminGrpcClient { +class UserGrpcClient { @GrpcClient("user-service") lateinit var channel: Channel diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/client/dto/response/InternalAdminResponse.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/user/client/dto/response/InternalAdminResponse.kt similarity index 73% rename from casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/client/dto/response/InternalAdminResponse.kt rename to casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/user/client/dto/response/InternalAdminResponse.kt index e4e33cc..5f6575b 100644 --- a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/client/dto/response/InternalAdminResponse.kt +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/user/client/dto/response/InternalAdminResponse.kt @@ -1,4 +1,4 @@ -package hs.kr.entrydsm.feed.infrastructure.grpc.client.dto.response +package hs.kr.entrydsm.feed.infrastructure.grpc.user.client.dto.response import java.util.UUID From 3626ec0cf5e5c3af9fcda4b470f658dd88c8e351 Mon Sep 17 00:00:00 2001 From: coehgns Date: Sun, 31 Aug 2025 14:36:28 +0900 Subject: [PATCH 02/12] =?UTF-8?q?chore=20(=20#34=20)=20:=20DependencyVersi?= =?UTF-8?q?on=EC=97=90=20RESILIENCE4J=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- buildSrc/src/main/kotlin/DependencyVersion.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/buildSrc/src/main/kotlin/DependencyVersion.kt b/buildSrc/src/main/kotlin/DependencyVersion.kt index 9fc4c15..9e8a242 100644 --- a/buildSrc/src/main/kotlin/DependencyVersion.kt +++ b/buildSrc/src/main/kotlin/DependencyVersion.kt @@ -24,4 +24,7 @@ object DependencyVersion { const val OPEN_FEIGN_VERSION = "3.1.4" const val SPRING_CLOUD_CONFIG_VERSION = "2023.0.3" + + //Resilience4j + const val RESILIENCE4J = "2.0.2" } From bd3165fa46fbb6510387f8413856de4fe32c82b9 Mon Sep 17 00:00:00 2001 From: coehgns Date: Sun, 31 Aug 2025 14:36:34 +0900 Subject: [PATCH 03/12] =?UTF-8?q?chore=20(=20#34=20)=20:=20Dependencies?= =?UTF-8?q?=EC=97=90=20RESILIENCE4J=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- buildSrc/src/main/kotlin/Dependencies.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 2113de1..780ea4f 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -58,4 +58,10 @@ object Dependencies { // Spring Cloud Config const val SPRING_CLOUD_CONFIG = "org.springframework.cloud:spring-cloud-starter-config" + + //Resilience4j + const val RESILIENCE4J_CIRCUITBREAKER = "io.github.resilience4j:resilience4j-circuitbreaker:${DependencyVersions.RESILIENCE4J}" + const val RESILIENCE4J_RETRY = "io.github.resilience4j:resilience4j-retry:${DependencyVersions.RESILIENCE4J}" + const val RESILIENCE4J_SPRING_BOOT = "io.github.resilience4j:resilience4j-spring-boot3:${DependencyVersions.RESILIENCE4J}" + const val RESILIENCE4J_KOTLIN = "io.github.resilience4j:resilience4j-kotlin:${DependencyVersions.RESILIENCE4J}" } From 0dd148a29a8d46b835966bce9c2e16261eb69568 Mon Sep 17 00:00:00 2001 From: coehgns Date: Sun, 31 Aug 2025 14:36:53 +0900 Subject: [PATCH 04/12] =?UTF-8?q?chore=20(=20#34=20)=20:=20Dependencies?= =?UTF-8?q?=EC=97=90=20RESILIENCE4J=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- buildSrc/src/main/kotlin/Dependencies.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 780ea4f..9869413 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -60,8 +60,8 @@ object Dependencies { const val SPRING_CLOUD_CONFIG = "org.springframework.cloud:spring-cloud-starter-config" //Resilience4j - const val RESILIENCE4J_CIRCUITBREAKER = "io.github.resilience4j:resilience4j-circuitbreaker:${DependencyVersions.RESILIENCE4J}" - const val RESILIENCE4J_RETRY = "io.github.resilience4j:resilience4j-retry:${DependencyVersions.RESILIENCE4J}" - const val RESILIENCE4J_SPRING_BOOT = "io.github.resilience4j:resilience4j-spring-boot3:${DependencyVersions.RESILIENCE4J}" - const val RESILIENCE4J_KOTLIN = "io.github.resilience4j:resilience4j-kotlin:${DependencyVersions.RESILIENCE4J}" + const val RESILIENCE4J_CIRCUITBREAKER = "io.github.resilience4j:resilience4j-circuitbreaker:${DependencyVersion.RESILIENCE4J}" + const val RESILIENCE4J_RETRY = "io.github.resilience4j:resilience4j-retry:${DependencyVersion.RESILIENCE4J}" + const val RESILIENCE4J_SPRING_BOOT = "io.github.resilience4j:resilience4j-spring-boot3:${DependencyVersion.RESILIENCE4J}" + const val RESILIENCE4J_KOTLIN = "io.github.resilience4j:resilience4j-kotlin:${DependencyVersion.RESILIENCE4J}" } From 2a1e9a42e1ec843e760fd7fc3a15fa70502766cd Mon Sep 17 00:00:00 2001 From: coehgns Date: Sun, 31 Aug 2025 14:37:19 +0900 Subject: [PATCH 05/12] =?UTF-8?q?chore=20(=20#34=20)=20:=20build.gradle.kt?= =?UTF-8?q?s=EC=97=90=20RESILIENCE4J=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- casper-feed/build.gradle.kts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/casper-feed/build.gradle.kts b/casper-feed/build.gradle.kts index 36a3563..42e2128 100644 --- a/casper-feed/build.gradle.kts +++ b/casper-feed/build.gradle.kts @@ -97,6 +97,12 @@ dependencies { // Cloud Config implementation(Dependencies.SPRING_CLOUD_CONFIG) + + // Resilience4j + implementation(Dependencies.RESILIENCE4J_CIRCUITBREAKER) + implementation(Dependencies.RESILIENCE4J_RETRY) + implementation(Dependencies.RESILIENCE4J_SPRING_BOOT) + implementation(Dependencies.RESILIENCE4J_KOTLIN) } protobuf { From 35a65bc3fdd79abe2ce82373d3acf3c54d04a266 Mon Sep 17 00:00:00 2001 From: coehgns Date: Sun, 31 Aug 2025 14:42:10 +0900 Subject: [PATCH 06/12] =?UTF-8?q?feat=20(=20#34=20)=20:=20ResilienceConfig?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/global/config/ResilienceConfig.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/config/ResilienceConfig.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/config/ResilienceConfig.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/config/ResilienceConfig.kt new file mode 100644 index 0000000..bcb2a4f --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/config/ResilienceConfig.kt @@ -0,0 +1,25 @@ +package hs.kr.entrydsm.feed.global.config + +import io.github.resilience4j.circuitbreaker.CircuitBreaker +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry +import io.github.resilience4j.retry.Retry +import io.github.resilience4j.retry.RetryRegistry +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class ResilienceConfig( + private val circuitBreakerRegistry: CircuitBreakerRegistry, + private val retryRegistry: RetryRegistry +) { + + @Bean + fun userGrpcCircuitBreaker(): CircuitBreaker { + return circuitBreakerRegistry.circuitBreaker("user-grpc") + } + + @Bean + fun userGrpcRetry(): Retry { + return retryRegistry.retry("user-grpc") + } +} From 1d0bcff3935927f48341f2d87c42262e25739b5e Mon Sep 17 00:00:00 2001 From: coehgns Date: Sun, 31 Aug 2025 14:42:24 +0900 Subject: [PATCH 07/12] =?UTF-8?q?feat=20(=20#34=20)=20:=20ResilienceExtens?= =?UTF-8?q?ions=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/extension/ResilienceExtensions.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceExtensions.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceExtensions.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceExtensions.kt new file mode 100644 index 0000000..60d21fd --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceExtensions.kt @@ -0,0 +1,22 @@ +package hs.kr.entrydsm.feed.global.extension + +import io.github.resilience4j.circuitbreaker.CircuitBreaker +import io.github.resilience4j.kotlin.circuitbreaker.executeSuspendFunction +import io.github.resilience4j.kotlin.retry.executeSuspendFunction +import io.github.resilience4j.retry.Retry +import kotlinx.coroutines.coroutineScope + +suspend fun executeGrpcCallWithResilience( + retry: Retry, + circuitBreaker: CircuitBreaker, + fallback: suspend () -> T, + block: suspend () -> T +): T = coroutineScope { + try { + circuitBreaker.executeSuspendFunction { + retry.executeSuspendFunction(block) + } + }catch (e: Exception) { + fallback() + } +} From 1df11716cfd1f1ff95e1e6f1be8301b4a0e4ae65 Mon Sep 17 00:00:00 2001 From: coehgns Date: Sun, 31 Aug 2025 14:42:54 +0900 Subject: [PATCH 08/12] =?UTF-8?q?feat=20(=20#34=20)=20:=20UserGrpcClient?= =?UTF-8?q?=EC=97=90=20executeGrpcCallWithResilience=20extension=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../grpc/user/client/UserGrpcClient.kt | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/user/client/UserGrpcClient.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/user/client/UserGrpcClient.kt index 8cc97a4..70a28fa 100644 --- a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/user/client/UserGrpcClient.kt +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/infrastructure/grpc/user/client/UserGrpcClient.kt @@ -2,11 +2,15 @@ package hs.kr.entrydsm.feed.infrastructure.grpc.user.client import hs.kr.entrydsm.casper.admin.proto.AdminServiceGrpc import hs.kr.entrydsm.casper.admin.proto.AdminServiceProto +import hs.kr.entrydsm.feed.global.extension.executeGrpcCallWithResilience import hs.kr.entrydsm.feed.infrastructure.grpc.user.client.dto.response.InternalAdminResponse +import io.github.resilience4j.circuitbreaker.CircuitBreaker +import io.github.resilience4j.retry.Retry import io.grpc.Channel import io.grpc.stub.StreamObserver import kotlinx.coroutines.suspendCancellableCoroutine import net.devh.boot.grpc.client.inject.GrpcClient +import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Component import java.util.UUID import kotlin.coroutines.resume @@ -18,7 +22,10 @@ import kotlin.coroutines.resumeWithException * @property channel gRPC 통신을 위한 채널 (user-service로 자동 주입됨) */ @Component -class UserGrpcClient { +class UserGrpcClient( + @Qualifier("userGrpcRetry") private val retry: Retry, + @Qualifier("userGrpcCircuitBreaker") private val circuitBreaker: CircuitBreaker +) { @GrpcClient("user-service") lateinit var channel: Channel @@ -33,24 +40,31 @@ class UserGrpcClient { * @throws java.util.concurrent.CancellationException 코루틴이 취소된 경우 */ suspend fun getAdminInfoByAdminId(adminId: UUID): InternalAdminResponse { - val adminStub = AdminServiceGrpc.newStub(channel) - - val request = AdminServiceProto.GetAdminIdRequest.newBuilder() - .setAdminId(adminId.toString()) - .build() - - val response = suspendCancellableCoroutine { continuation -> - adminStub.getAdminByUUID(request, object : StreamObserver { - override fun onNext(value: AdminServiceProto.GetAdminIdResponse) { - continuation.resume(value) - } - override fun onError(t: Throwable) { - continuation.resumeWithException(t) - } - override fun onCompleted() {} - }) - } - return InternalAdminResponse(id = UUID.fromString(response.adminId)) + return executeGrpcCallWithResilience( + retry = retry, + circuitBreaker = circuitBreaker, + fallback = { InternalAdminResponse(adminId) } + ) { + val adminStub = AdminServiceGrpc.newStub(channel) + + val request = AdminServiceProto.GetAdminIdRequest.newBuilder() + .setAdminId(adminId.toString()) + .build() + + val response = suspendCancellableCoroutine { continuation -> + adminStub.getAdminByUUID(request, object : StreamObserver { + override fun onNext(value: AdminServiceProto.GetAdminIdResponse) { + continuation.resume(value) + } + override fun onError(t: Throwable) { + continuation.resumeWithException(t) + } + override fun onCompleted() {} + }) + } + + InternalAdminResponse(id = UUID.fromString(response.adminId)) + } } } From cbbf5a5d67ca1aee15f7f75abbfa193b7b6f48ac Mon Sep 17 00:00:00 2001 From: coehgns Date: Tue, 2 Sep 2025 20:22:29 +0900 Subject: [PATCH 09/12] =?UTF-8?q?refactor=20(=20#34=20)=20:=20ResilienceEx?= =?UTF-8?q?tensions=20->=20ResilienceGrpcExtensions=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{ResilienceExtensions.kt => ResilienceGrpcExtensions.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/{ResilienceExtensions.kt => ResilienceGrpcExtensions.kt} (100%) diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceExtensions.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceGrpcExtensions.kt similarity index 100% rename from casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceExtensions.kt rename to casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceGrpcExtensions.kt From 526576be01d229a4731f1b4a2468f214bf0188e6 Mon Sep 17 00:00:00 2001 From: coehgns Date: Tue, 2 Sep 2025 20:23:21 +0900 Subject: [PATCH 10/12] refactor ( #34 ) : ResilienceGrpcExtensions --- .../extension/ResilienceGrpcExtensions.kt | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceGrpcExtensions.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceGrpcExtensions.kt index 60d21fd..e36f25c 100644 --- a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceGrpcExtensions.kt +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceGrpcExtensions.kt @@ -4,19 +4,24 @@ import io.github.resilience4j.circuitbreaker.CircuitBreaker import io.github.resilience4j.kotlin.circuitbreaker.executeSuspendFunction import io.github.resilience4j.kotlin.retry.executeSuspendFunction import io.github.resilience4j.retry.Retry -import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.CancellationException +import org.slf4j.LoggerFactory + +private val log = LoggerFactory.getLogger("ResilienceGrpcExtensions") suspend fun executeGrpcCallWithResilience( retry: Retry, circuitBreaker: CircuitBreaker, fallback: suspend () -> T, block: suspend () -> T -): T = coroutineScope { +): T = try { - circuitBreaker.executeSuspendFunction { - retry.executeSuspendFunction(block) + retry.executeSuspendFunction { + circuitBreaker.executeSuspendFunction(block) } - }catch (e: Exception) { + } catch (ce: CancellationException) { + throw ce + } catch (e: Exception) { + log.warn("gRPC 호출 실패, fallback 실행: {}", e.toString()) fallback() } -} From 8e206cab16f8298b802327b4f4f955beafb681cb Mon Sep 17 00:00:00 2001 From: coehgns Date: Tue, 2 Sep 2025 20:26:34 +0900 Subject: [PATCH 11/12] =?UTF-8?q?docs=20(=20#34=20)=20:=20ResilienceConfig?= =?UTF-8?q?=EC=97=90=20kdoc=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/global/config/ResilienceConfig.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/config/ResilienceConfig.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/config/ResilienceConfig.kt index bcb2a4f..0845e1a 100644 --- a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/config/ResilienceConfig.kt +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/config/ResilienceConfig.kt @@ -7,17 +7,33 @@ import io.github.resilience4j.retry.RetryRegistry import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +/** + * Resilience4j 관련 설정을 담당하는 클래스입니다. + * + * @property circuitBreakerRegistry CircuitBreaker 인스턴스를 생성하고 관리합니다. + * @property retryRegistry Retry 인스턴스를 생성하고 관리합니다. + */ @Configuration class ResilienceConfig( private val circuitBreakerRegistry: CircuitBreakerRegistry, private val retryRegistry: RetryRegistry ) { + /** + * user-grpc 서킷 브레이커를 생성합니다. + * + * @return user-grpc 이름의 CircuitBreaker 인스턴스 + */ @Bean fun userGrpcCircuitBreaker(): CircuitBreaker { return circuitBreakerRegistry.circuitBreaker("user-grpc") } + /** + * user-grpc 리트라이를 생성합니다. + * + * @return user-grpc 이름의 Retry 인스턴스 + */ @Bean fun userGrpcRetry(): Retry { return retryRegistry.retry("user-grpc") From 348f6f85b7e0d445f40a01e3d8cc212ec15cf0d0 Mon Sep 17 00:00:00 2001 From: coehgns Date: Tue, 2 Sep 2025 20:26:42 +0900 Subject: [PATCH 12/12] =?UTF-8?q?docs=20(=20#34=20)=20:=20ResilienceGrpcEx?= =?UTF-8?q?tensions=EC=97=90=20kdoc=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/global/extension/ResilienceGrpcExtensions.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceGrpcExtensions.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceGrpcExtensions.kt index e36f25c..ebcf56d 100644 --- a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceGrpcExtensions.kt +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/global/extension/ResilienceGrpcExtensions.kt @@ -9,6 +9,16 @@ import org.slf4j.LoggerFactory private val log = LoggerFactory.getLogger("ResilienceGrpcExtensions") +/** + * gRPC 호출에 대해 Retry와 CircuitBreaker를 적용합니다. + * + * @param T gRPC 함수의 반환 타입 + * @param retry 적용할 Retry 인스턴스 + * @param circuitBreaker 적용할 CircuitBreaker 인스턴스 + * @param fallback gRPC 호출 실패 시 실행할 함수 + * @param block 실행할 gRPC 함수 + * @return gRPC 함수의 실행 결과를 반환하거나, 실패 시 fallback 함수의 결과를 반환 + */ suspend fun executeGrpcCallWithResilience( retry: Retry, circuitBreaker: CircuitBreaker,