diff --git a/client/external/mathrank-school/build.gradle b/client/external/mathrank-school/build.gradle index bd5fbb70..f33d29e9 100644 --- a/client/external/mathrank-school/build.gradle +++ b/client/external/mathrank-school/build.gradle @@ -1,4 +1,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'io.github.resilience4j:resilience4j-spring-boot3:2.2.0' + implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'org.springframework.boot:spring-boot-starter-validation' } diff --git a/client/external/mathrank-school/src/main/java/kr/co/mathrank/client/external/school/SchoolClient.java b/client/external/mathrank-school/src/main/java/kr/co/mathrank/client/external/school/SchoolClient.java index f8599c37..573771eb 100644 --- a/client/external/mathrank-school/src/main/java/kr/co/mathrank/client/external/school/SchoolClient.java +++ b/client/external/mathrank-school/src/main/java/kr/co/mathrank/client/external/school/SchoolClient.java @@ -11,6 +11,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.client.RestClient; +import io.github.resilience4j.ratelimiter.annotation.RateLimiter; import jakarta.validation.constraints.NotNull; import kr.co.mathrank.client.config.RestClientResponseDecorator; import kr.co.mathrank.client.config.TimeoutConfiguredClient; @@ -19,7 +20,9 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Component public class SchoolClient extends TimeoutConfiguredClient { @Value("${neice.school.key:}") @@ -39,6 +42,7 @@ public SchoolClient(final SchoolClientProperties schoolClientProperties) { } @Deprecated + @RateLimiter(name = "neiceApi", fallbackMethod = "fallBackSchoolInfo") public Optional getSchool(final String type, final String schoolCode) { if (schoolCode == null || schoolCode.isBlank()) { return Optional.empty(); @@ -58,11 +62,18 @@ public Optional getSchool(final String type, final String schoolCode return response.getSchoolInfo().isEmpty() ? Optional.empty() : Optional.of(response.getSchoolInfo().getFirst()); } + // fall back - #getSchool + private Optional fallBackSchoolInfo(final String type, final String schoolCode, final Throwable t) { + log.warn("[SchoolClient.fallBackSchoolInfo] fallback called - type: {}, schoolCode: {}", type, schoolCode, t); + return Optional.empty(); + } + public ClientResponse> getSchoolResponse(final String type, final String schoolCode) { return responseDecorator.wrap(() -> getSchool(type, schoolCode)); } @Deprecated + @RateLimiter(name = "neiceApi", fallbackMethod = "fallBackGetSchools") public SchoolResponse getSchools(String type, Integer pageIndex, Integer pageSize, String schoolName) { return restClient.get() .uri(uriBuilder -> uriBuilder.path("/hub/schoolInfo") @@ -75,6 +86,11 @@ public SchoolResponse getSchools(String type, Integer pageIndex, Integer pageSiz .body(SchoolResponse.class); } + private SchoolResponse fallBackGetSchools(String type, Integer pageIndex, Integer pageSize, String schoolName, Throwable t) { + log.warn("[SchoolClient.fallBackGetSchools] fallback called - type: {}, pageIndex: {}, pageSize: {}, schoolName: {}", type, pageIndex, pageSize, schoolName, t); + return new SchoolResponse(null); + } + public ClientResponse getSchoolsResponse(String type, Integer pageIndex, Integer pageSize, String schoolName) { return responseDecorator.wrap(() -> this.getSchools(type, pageIndex, pageSize, schoolName)); } @@ -88,6 +104,7 @@ public ClientResponse getSchoolsResponse(String type, Integer pa * @return 해당 도시의 학교 정보가 담긴 응답 객체 */ @Deprecated + @RateLimiter(name = "neiceApi", fallbackMethod = "fallBackGetSchoolsByCityName") public SchoolResponse getSchoolsByCityName(String type, String cityName) { return restClient.get() .uri(uriBuilder -> uriBuilder.path("/hub/schoolInfo") @@ -101,6 +118,11 @@ public SchoolResponse getSchoolsByCityName(String type, String cityName) { .body(SchoolResponse.class); } + private SchoolResponse fallBackGetSchoolsByCityName(String type, String cityName, Throwable t) { + log.warn("[SchoolClient.fallBackGetSchoolsByCityName] fallback called - type: {}, cityName: {}", type, cityName, t); + return new SchoolResponse(null); + } + public ClientResponse getSchoolsByCityNameResponse(String type, String cityName) { return responseDecorator.wrap(() -> this.getSchoolsByCityName(type, cityName)); } diff --git a/client/external/mathrank-school/src/test/java/kr/co/mathrank/client/external/school/SchoolClientTest.java b/client/external/mathrank-school/src/test/java/kr/co/mathrank/client/external/school/SchoolClientTest.java index bc05771f..ec4690a4 100644 --- a/client/external/mathrank-school/src/test/java/kr/co/mathrank/client/external/school/SchoolClientTest.java +++ b/client/external/mathrank-school/src/test/java/kr/co/mathrank/client/external/school/SchoolClientTest.java @@ -13,6 +13,9 @@ @SpringBootTest(properties = """ client.school.read-timeout-seconds=10 client.school.connection-timeout-seconds=10 +resilience4j.ratelimiter.instances.neiceApi.limit-refresh-period=1s +resilience4j.ratelimiter.instances.neiceApi.limit-for-period=100 +resilience4j.ratelimiter.instances.neiceApi.timeout-duration=1s """) class SchoolClientTest { @Autowired