์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- s3 ์ด๋ฏธ์ง ๋ค์ด๋ก๋
- docker-compose kafka
- s3 log ์ ์ฅ
- s3 ์ด๋ฏธ์ง ์ ์ฅ
- ์๋ฒ ํฐ์ง๋ ๋์ปค ์ฌ์คํ
- ์ ํจ์ค ๋น๋ ์ค๋ฅ
- ์คํํ๋ ๋ฏธ์ค
- docker compose
- ํ๋ก๊ทธ๋๋จธ์ค ์ปฌ๋ฌ๋ง๋ถ
- ์ ํจ์ค ์ค์ผ์ค๋ฌ
- prod docker-compose
- ๋ค์ค ์ปจํ ์ด๋
- redis ์กฐํ
- AWS Certified Solutions Architect - Associate
- aws saa ํฉ๊ฒฉ
- ํ๋ก๊ทธ๋๋จธ์ค ํฉ์นํ์์๊ธ
- nGrinder
- Entity
- ํ์ดํผ๋ฐ์ด์
- jvm ๋ฐ๋ฐ๋ฅ๊น์ง ํํค์น๊ธฐ
- aws ์ฟ ํฐ
- ์๋ฐ
- docker ps -a
- Codedeploy ์ค๋ฅ
- redis ํ ์คํธ์ฝ๋
- ํ๋ก๊ทธ๋๋จธ์ค
- Kafka
- JPA
- private subnet ec2 ๋ก์ปฌ ์ ์
- docker
- Today
- Total
๐๐ข๐๐ โ๐๐๐ ๐๐๐ก๐๐ ๐๐๐๐โง
[Spring] Spring Retry ๋ณธ๋ฌธ
[Spring] Spring Retry
๐คRyusun๐ค 2023. 5. 16. 13:16Spring Retry
- Spring์ ์ฌ์๋ ๊ธฐ๋ฅ์ ์คํ๋ง ๋ฐฐ์น์ ํฌํจ๋์ด ์๋ค๊ฐ 2.2.0 ๋ฒ์ ๋ถํฐ ์ ์ธ๋์ด ํ์ฌ๋ Spring Retry ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํฌํจ๋์ด ์๋ค.
- Spring Retry๋ ๋์์ด ์คํจํ๋๋ผ๋ ๋ช ๋ฒ ๋ ์๋ํ๋ฉด ์ฑ๊ณตํ ์ ์๋ ์์ ์ ์๋์ผ๋ก ๋ค์ ์๋ํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
- Spring์์ ์ฌ์๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ธฐ ์ํด์๋ Resilience4j, Spring Retry ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ณดํต ๋ง์ด ์ฌ์ฉํ๋ค.
- Resilience4j๋ ์ฌ์๋(Retry) ๊ธฐ๋ฅ๋ง ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ๊ฑฐ์ ์๊ณ , ๋๋ถ๋ถ ์ํท ๋ธ๋ ์ด์ปค์ ๊ฐ์ด ์ฌ์ฉํ๊ฒ ๋๋ค.
- ์๋ฌ๋ฅผ ๋ค์ ์ฒ๋ฆฌํด์ผํ ๊ฒฝ์ฐ Spring์์ ์ ๊ณตํ๋ Spring Retry๋ฅผ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
- ์ฌ์๋ ๊ธฐ๋ฅ์ ์๋ฐ ์ฝ๋๋ก ๊ตฌํํ์ฌ ์ฌ์ฉํ ์ ์์ง๋ง, ๋น์ง๋์ค ๋ก์ง์ ์ง์ค์ด ๊ฐ๋ฅํ๋๋ก ์คํ๋ง์์ ์ ๊ณตํ๋ Spring Retry ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ ๋ ์ฝ๋๋ฅผ ๊ฐ๊ฒฐํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฝ๋ค๋ ์ฅ์ ์ด ์๋ค.
์ฌ์ฒ๋ฆฌ๋ฅผ ํ ๋ ๊ณ ๋ คํด์ผํ ์
- ์ฌ์๋๋ฅผ ๋ช ๋ฒ ์คํํ ๊ฒ์ธ๊ฐ?
- ์ฌ์๋ ํ๊ธฐ ์ ์ ์ง์ฐ์๊ฐ์ ์ผ๋ง๋ ์ค ๊ฒ์ธ๊ฐ?
- ์ฌ์๋๋ฅผ ๋ชจ๋ ์คํจํ์ ๊ฒฝ์ฐ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ๊ฒ์ธ๊ฐ?
Spring Retry ๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
- Retry Template : ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์
- Retry Annotation : ์ ์ธ์ ๋ฐฉ์
1. RetryTemplate
gradle์ ์๋์ ๊ฐ์ด ์์กด์ฑ์ ์ถ๊ฐํด์ฃผ๊ณ ๊ด๋ จ retryTemplate ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ๋ค.
implementation 'org.springframework.retry:spring-retry'
Spring Retry์๋ ์์
์ ๋ํ ์ฌ์๋๋ฅผ ์๋ํํ๊ธฐ ์ํ ์ธํฐํ์ด์ค์ธ RetryOperations๊ฐ ์๋ค.
์๋๋ RetryOperations์ธํฐํ์ด์ค ์ฝ๋์ด๋ฉฐ, execute() ๋ฉ์๋๊ฐ ์กด์ฌํ๋๋ฐ ๋งค๊ฐ๋ณ์์ธ RetryCallback์ ์คํจ ์ ์ฌ์๋ํด์ผ ํ๋ ๋น์ฆ๋์ค ๋ก์ง ์ฝ์
์ ํ์ฉํ๋ ์ธํฐํ์ด์ค ์ด๋ค.
public interface RetryOperations {
<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E;
...
}
์๋๋ RetryCallback ์ธํฐํ์ด์ค์ด๋ฉฐ, RetryCallback์ doWithRetry๋ผ๋ ๋ฉ์๋๋ฅผ ํ๋ ๊ฐ์ง๊ณ ์๋ ๊ฐ๋จํ ์ธํฐํ์ด์ค์ด๋ค.
doWithRetry ๋ฉ์๋์๋ ์ฌ์๋๋ฅผ ํ ๋น์ฆ๋์ค ๋ก์ง์ด ๋ค์ด๊ฐ๋ค.
public interface RetryCallback<T, E extends Throwable> {
T doWithRetry(RetryContext context) throws E;
}
์ฝ๋ฐฑ์ด ์คํจํ๋ฉด ์ฌ์๋ ์ ์ฑ ์ ๋ฐ๋ผ์ ํน์ ํ์ ํน์ ํน์ ์๊ฐ๋์ ์ฌ์๋๋ฅผ ํ ๊ฒ์ด๋ค.
RetryTemplate์ RetryOperations์ ๊ตฌํ์ฒด์ด๋ค.
@Configuration ํด๋์ค์์ RetryTemplate Bean์ ๊ตฌ์ฑํด์ ์ฌ์ฉํ ๊ฒ์ด๋ค.
1-1) RetryTemplate ์ฌ์ฉ
implementation 'org.springframework.retry:spring-retry'
@Configuration
public class RetryTemplateConfig {
@Bean
public RetryTemplate retryTemplate() {
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(1000L); //์ง์ ํ ์๊ฐ๋งํผ ๋๊ธฐํ ์ฌ์๋ ํ๋ค.
// ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
// backOffPolicy.setInitialInterval(100L); //millisecond
// backOffPolicy.setMultiplier(2); //interval * N ๋งํผ ๋๊ธฐํ ์ฌ์๋ ํ๋ค.
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); // ๊ณ ์ ๋ ํ์๋งํผ ์ฌ ์๋ ํ๋๋ฐ ์ฌ์ฉ
retryPolicy.setMaxAttempts(2); //retry max count
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(backOffPolicy);
retryTemplate.setRetryPolicy(retryPolicy);
return retryTemplate;
}
}
@Service
public class SomeClass {
@Autowired
private RetryTemplate retryTemplate;
public String apply() {
String result = retryTemplate.execute(context -> someFunction());
return result;
}
}
์์ ๊ฐ์ด ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ์ฌ์๋ ์ ์ฑ ๋ฐ ๊ทธ์ธ์ ๊ธฐ๋ฅ์ ๋ํด ์์๋ณด์.
1-1-1) Recovery Callback
์ฌ์๋๊ฐ ์ ๋ถ ์คํจํ๋ฉด, RetryOperations๋ RecoveryCallback์ ํธ์ถํ๋ค.
์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ค๋ฉด execute ๋ฉ์๋๋ฅผ ํธ์ถํ ๋ RecoveryCallback ๊ฐ์ฒด๋ฅผ ์ ๋ฌํด์ฃผ์ด์ผ ํ๋ค.
// ์ต๋ช
ํด๋์ค
String result = retryTemplate.execute(new RetryCallback<String, Throwable>() {
@Override
public String doWithRetry(RetryContext context) throws Throwable {
return "retry logic";
}
}, new RecoveryCallback<String>() {
@Override
public String recover(RetryContext context) throws Exception {
return "recovery logic";
}
});
๋๋ค๋ก ๋ณ๊ฒฝํ๋ฉด ์๋์ ๊ฐ์ด ๋ณ๊ฒฝ์ด ๊ฐ๋ฅํ๋ค.
// ๋๋ค์
String result = retryTemplate.execute(
(RetryCallback<String, Throwable>)
context -> "retry logic",
context -> "recovery logic");
๋ชจ๋ ์ฌ์๋๊ฐ ์คํจํ๊ณ ๋ ์ด์ ์ฌ์๋ํ ์ ์๋ ๊ฒฝ์ฐ, RecoveryCallback ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
RecoveryCallback์ recover ๋ฉ์๋์์๋ ์ฌ์๋๊ฐ ์ ๋ถ ์คํจํ ๊ฒฝ์ฐ์ ๋ํ ๋์ฒด ๋ก์ง์ ์ํํ๋ค.
1-1-2) Backoff Policies
์ค๋ฅ๊ฐ ๋ฐ์ํ์ฌ ์ฌ์๋๋ฅผ ํ ๋ ์ฌ์๋๋ฅผ ํ๊ธฐ์ ์ ์ ๊น ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ด ์ ์ฉํ ๋๊ฐ ๋ง๋ค.
์ผ๋ฐ์ ์ผ๋ก ์ค๋ฅ๋ ์ ๊น ๋์ ๊ธฐ๋ค๋ฆฌ๊ธฐ๋ง ํด๋ ํด๊ฒฐ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
BackOffPolicy๋ ์ฌ์๋ ๊ฐ์ ์ผ์ ์๊ฐ ๊ฐ๊ฒฉ์ ๋๊ณ retry ์ ์ดํ๋๋ฐ ์ฌ์ฉ๋๋ค.
public interface BackOffPolicy {
BackOffContext start(RetryContext context);
void backOff(BackOffContext backOffContext) throws BackOffInterruptedException;
}
BackoffPolicy ์ธํฐํ์ด์ค์ backOff ๋ฉ์๋๋ฅผ ์ํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ๋ฉด ๋๋ค.
๊ณ ์ ๋ ์๊ฐ์ผ๋ก backoff ์ํค๊ณ ์ ํ๋ค๋ฉด FixedBackOffPolicy๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
ํ์ง๋ง, ์์์ ์ธ๊ธํ ๊ฒ์ฒ๋ผ ๋๋ํ๊ฒ ์ฌ์ฒ๋ฆฌ๋ฅผ ํ์ง ์์ผ๋ฉด ์คํ๋ ค ๋คํธ์ํฌ ๋ถํ๋ฅผ ์ค ๊ฐ๋ฅ์ฑ์ด ํฌ๋ค.
๊ทธ๋์ ์กฐ๊ธ ๋ ์ข์ ๋ฐฉ๋ฒ์ผ๋ก ์ ์ง์ ์ผ๋ก ์๊ฐ ๊ฐ๊ฒฉ์ด ๋์ด๋๋ ExponentialBackOffPolicy๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์ด๋ ์ง์์ ๋น๋กํ์ฌ backOff ์๊ฐ์ ์กฐ์ ํ๋ค. ์๋ฅผ ๋ค์ด ์ฒซ๋ฒ์งธ ์ฌ์๋๋ฅผ ์ํ ๋๊ธฐ ์๊ฐ์ 100ms ๋๋ฒ์งธ ์ฌ์๋๋ฅผ ์ํ ๋๊ธฐ์๊ฐ์ 200ms, ์ธ๋ฒ์งธ ์ฌ์๋๋ฅผ ์ํ ๋๊ธฐ ์๊ฐ์ 400ms ์ฒ๋ผ ์ ํด์ง ๋ฐฐ์๋งํผ ๋์ด๋๋ ๋ฐฉ์์ด๋ค.
์๋ ์ฝ๋๋ก ํ์ธํด๋ณด์.
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(100L); // millisecond
backOffPolicy.setMultiplier(2); //interval * N ๋งํผ ๋๊ธฐํ ์ฌ์๋ ํ๋ค.
๊ทธ ์ธ์๋ Jitter๋ผ๋ ๋ฐฉ์์ผ๋ก backOff๋ฅผ ์ง์ ํ ์ ์์ผ๋ฉฐ, AWS์์๋ Retry๋ฅผ Exponential BackOff And Jitter ํจ๊ป ์ฌ์ฉํ๋ค๊ณ ํ๋ค.
1-1-3) Retry Policies
RetryTemplate์์ ์ฌ์๋ ํ ์ง ์ฌ๋ถ๋ RetryPolicy์ ์ํด ๊ฒฐ์ ๋๋ค.
RetryTemplate์ RetryPolicy์ open ๋ฉ์๋๋ฅผ ํตํด์ RetryContext ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค.
๊ทธ๋ฆฌ๊ณ RetryCallback์ doWithRetry ๋ฉ์๋ ์ธ์๋ก ์์ฑ๋ RetryContext ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ๋ค.
RetryTemplate์ ์ฝ๋ฐฑ์ด ์คํจํ ๊ฒฝ์ฐ RetryPolicy์๊ฒ ์ํ๋ฅผ ์
๋ฐ์ดํธ ํ๋๋ก ์์ฒญํ๋ค.
๊ทธ๋ฆฌ๊ณ , RetryPolicy์ canRetry ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ, ์ฌ์๋๊ฐ ๊ฐ๋ฅํ์ง ์ฌ๋ถ๋ฅผ ๋ฌป๋๋ค. ๋ง์ฝ ์ฌ์๋๊ฐ ๋ถ๊ฐ๋ฅํ๊ฒฝ์ฐ RetryTemplate์ ๋ง์ง๋ง ์ฝ๋ฐฑ ์คํ์ ๋ฐ์ํ ์์ธ๋ฅผ ๋์ง๋ค.
๋จ, RecoveryCallback์ด ์๋ ๊ฒฝ์ฐ RecoveryCallback ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
// Set the max attempts including the initial attempt before retrying
// and retry on all exceptions (this is the default):
SimpleRetryPolicy policy = new SimpleRetryPolicy(5, Collections.singletonMap(Exception.class, true));
// Use the policy...
RetryTemplate template = new RetryTemplate();
template.setRetryPolicy(policy);
template.execute(new RetryCallback<Foo>() {
public Foo doWithRetry(RetryContext context) {
// business logic here
}
});
์์ฒ๋ผ, ๋ชจ๋ ์์ธ๋ฅผ ์ฌ์๋ ํ๋ ๊ฒ์ ๋นํจ์จ์ ์ผ ์ ์๋ค. ๋ฐ๋ผ์ ๋ชจ๋ ์์ธ์ ๋ํด ์ฌ์๋ ํ์ง๋ง๊ณ , ์ฌ์๋ ๊ฐ๋ฅํ ๊ฒ ๊ฐ์ ์์ธ์ ๋ํด์๋ง ์ฌ์๋ ํ ์ ์๋ค.
์ฌ์๋ ํด๋ ๋ ๋ค์ ์์ธ๊ฐ ๋ฐ์ํ ๊ฒ์ด ํ์คํ ๊ฒฝ์ฐ์ ์ฌ์๋๋ฅผ ํ๋ ๊ฒ์ ๋นํจ์จ์ ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
ExceptionClassifierRetryPolicy๋ผ๋ ๋ณด๋ค ์ ์ฐํ RetryPolicy๋ ์๋ค.
์ด๋ ์์ธ ์ ํ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ์ฌ์๋ํ ์ ์๋๋ก ํด์ค๋ค. ExceptionClassifierRetryPolicy๋ ์์ธ ์ ํ์ ๋ฐ๋ผ RetryPolicy๋ฅผ ๊ฒฐ์ ํ๋ค.
์ฆ, ์ฝ๋ฐฑ ๋ฉ์๋์์ ๋ฐ์ํ๋ ์์ธ ์ ํ์ ๋ฐ๋ผ RetryPolicy๋ฅผ ๋ค๋ฅด๊ฒ ํ๊ณ ์ถ์ ๋ ์ ์ฉํ๋ค.
2. Retry Annotation
@EnableRetry ์ด๋ ธํ ์ด์ ์ ์ถ๊ฐํด์ Spring Retry๋ฅผ ํ์ฑํํ ์๋ ์๋ค.
@EnableRetry
@Configuration
public class RetryTemplateConfig {
//...
}
๊ทธ ํ @Retryable ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ๋ฉ์๋์ ์ฌ์๋ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ฉด ๋๋ค.
@Service
@RequiredArgsConstructor
public class KakaoAddressSearchService {
@Retryable(value = {RuntimeException.class})
public KakaoApiResponseDto requestAddressSearch(String address) {
// ...
}
value์ ํตํด exception ์ค์ ์ด ๊ฐ๋ฅํ๋ฉฐ RuntimeException์ด ๋ฐ์ํ๋ฉด ์ฌ์๋๋ฅผ ํ๊ฒ ๋๋ค.ํ์ฌ๋ ์ค์ ์ default๋ก ์ฃผ์๊ธฐ ๋๋ฌธ์ ์ฌ์๋๋ ์ต๋ 3๋ฒ, ์ฌ์๋ ๋๋ ์ด๋ 1์ด์ด๋ค.
maxAttempts(์ฌ์๋ ์ต๋ ํ์) ๋ฐ backoff (๋๋ ์ด ์ด) ๋งค๊ฐ๋ณ์๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์๋ ๋์์ ์ปค์คํ ๋ง์ด์ง ํ ์ ์๋ค.
@Service
@RequiredArgsConstructor
public class KakaoAddressSearchService {
@Retryable(
value = {RuntimeException.class}, // ์ฌ๋ฌ exception ์ ํ ๊ฐ๋ฅ
maxAttempts = 2,
backoff = @Backoff(delay = 3000)
)
public KakaoApiResponseDto requestAddressSearch(String address) {
// ...
}
์์ ์ฝ๋๋ ์ต๋ 2ํ ์ฌ์๋(์ฒซ๋ฒ์งธ ์๋ ํฌํจ)๋ฅผ ํ๊ณ ์ฌ์๋ ์ 3์ด ์ง์ฐ์ ์ฃผ์๋ค.
์ฌ์๋ ์ฒ๋ฆฌ๊ฐ ๋ชจ๋ ์คํจํ ์ ์ด๋ป๊ฒ ํ ์ง ์ ์ํ๋ FallBack ๊ธฐ๋ฅ์ ์ฌ์ฉํ ๋ ค๋ฉด, @Recover ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ๋ฉด ๋๋ค.
@Recover
public KakaoApiResponseDto recover(RuntimeException e, String address) {
log.error("All the retries failed. address: {}, error : {}", address, e.getMessage());
return null;
}
์ฆ, ์ต๋ 2๋ฒ ์ฌ์๋(์ฒซ๋ฒ ์งธ ์๋ ํฌํจ)๋ฅผ ํ๊ณ , ๋ชจ๋ ์คํจํ๊ฒ ๋๋ค๋ฉด recover ๋ฉ์๋๊ฐ ์คํ๋๋ค.
์ฌ๊ธฐ์ ์ฃผ์ํ ์ ์ Recover ๋ฉ์๋์ ๋ฐํ ํ์ ์ ๋ฐ๋์ ๋ง์ถฐ์ผ ํ๋๋ฐ, requestAddressSearch ๋ฉ์๋์ ๋ฐํํ์ ์ ๋ง์ถฐ์ค๋ค.
์ถ์ฒ
[Spring] Spring Retry - SW Developer (wonyong-jang.github.io)
'๐ฃ๐ฟ๐ผ๐ด๐ฟ๐ฎ๐บ๐บ๐ถ๐ป๐ด๐ป > ๐๐ฉ๐ซ๐ข๐ง๐ ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring] RestTemplate (0) | 2023.05.19 |
---|---|
[Spring] MockWebServer (0) | 2023.05.17 |
[Spring] Spock (0) | 2023.05.15 |
[Spring] Docker (0) | 2023.05.14 |
[Spring] Jpa๋ฅผ ์ด์ฉํ ์กฐํ์ ์ฆ๊ฐ ๊ธฐ๋ฅ ๊ตฌํ (0) | 2023.01.27 |