> ## Content Index
> Fetch the complete content index at: https://www.popit.kr/llms.txt
> Use this file to discover other available public pages before exploring further.

# REST 기반의 간단한 분산 트랜잭션 구현 - 4편 REST Retry
- URL: https://www.popit.kr/rest-distributed-transaction-4-retry/
- Published: 2018-09-05T23:39:28.000Z
- Updated: 2018-09-05T23:39:28.000Z
- Author: 유 영모
- Tags: #소프트웨어, 마이크로서비스, Spring Boot, REST, Spring, 트랜잭션, TCC, 분산트랜잭션, Retry, Spring Retry

이 글은 읽기 전에 이전 글까지 모두 읽을 필요는 없습니다. 다만 맥락을 이해하기 위해서 [1편](https://www.popit.kr/rest-distributed-transaction-1/)은 미리 읽는 편이 좋습니다.

---

- [REST 기반의 간단한 분산 트랜잭션 구현 -1편 TCC 개관](https://www.popit.kr/rest-distributed-transaction-1/)
- [REST 기반의 간단한 분산 트랜잭션 구현 - 2편 TCC Cancel, Timeout](https://www.popit.kr/rest-distributed-transaction-2-tcc-cancel-timeout/)
- [REST 기반의 간단한 분산 트랜잭션 구현 - 3편 TCC Confirm(Eventual Consistency)](https://www.popit.kr/rest-distributed-transaction-3-tcc-confirm/)
- **REST 기반의 간단한 분산 트랜잭션 구현 - 4편 REST Retry**

---

지난 글까지는 REST 기반의 분산 트랜잭션 구현 방법 중 하나인 TCCTry-Confirm/Cancel를 다루었다. 분산 환경에서는 네트워크 오류나 일시적인 서비스 중지 등으로 인해 일시적으로 REST 요청이 실패할 수 있다. 이러한 문제는 REST 요청을 재시도Retry하여 보안할 수 있다.

![그림. TCC 시나리오](https://www.popit.kr/content/images/wp/2018/09/image2018-8-30_9-20-36.png)

그림. TCC 시나리오

이번 글은 [스프링 Retry](https://github.com/spring-projects/spring-retry?ref=popit.kr) 라이브러리를 사용하여 위의 TCC 시나리오에서 REST 요청 실패 시 재시도 하도록 구현한다.

# [스프링 Retry](https://github.com/spring-projects/spring-retry?ref=popit.kr)

스프링 Retry는 스프링 애플리케이션에서 작업이 실패하였을 때 자동으로 재 시도하는 메커니즘을 제공한다. 최초에 [스프링 배치Spring Batch](https://spring.io/projects/spring-batch?ref=popit.kr)에서 시작되었으며, 현재는 분리되어 스프링 배치와 상관없이 독립적으로 사용할 수 있게 되었는데, 주로 템플릿/콜백 패턴Template/Callback Pattern\[1\]을 구현한 [RetryTemplate](https://docs.spring.io/spring-retry/docs/1.1.2.RELEASE/apidocs/org/springframework/retry/support/RetryTemplate.html?ref=popit.kr)을 제공하여 프로그래밍할 수 있게 했다.

```java
// 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
    }
});
```

RetryTemplate 이외에도 Java 어노테이션 방식도 지원한다.

```
@Service
class Service {
    @Retryable(RemoteAccessException.class)
    public void service(String str1, String str2) {
        // ... do something
    }
    @Recover
    public void recover(RemoteAccessException e, String str1, String str2) {
       // ... error handling making use of original args if required
    }
}
```

이 글에서는 RetryTemplate를 사용한다.

# OrderService

REST 호출 주체가 OrderService이기 때문에 스프링 Retry를 OrderService에 적용한다. 먼저 스프링 Retry를 사용하기 위해 [Maven POMProject Object Model](https://maven.apache.org/pom.html?ref=popit.kr#What%5Fis%5Fthe%5FPOM)에 의존성을 추가한다.\[2\]

```xhtml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    // ...
    <dependencies>
        // ...
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
    </dependencies>
</project>
```

그리고 RetryTemplate을 Spring Bean으로 등록한다.

```java
@Configuration
public class AppConfig {
 
    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
 
        // 재시도 간격을 2초로 설정
        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        fixedBackOffPolicy.setBackOffPeriod(2000l);
        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
 
        // 재시도 최대 횟수를 5번으로 설정
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(5);
        retryTemplate.setRetryPolicy(retryPolicy);
 
        return retryTemplate;
    }
}
```

OrderService에서 REST 연결을 책임지는 객체는 TccRestAdapterImpl이다. TccRestAdapterImpl은 스프링 [RestTemplate](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html?ref=popit.kr)을 사용하여 REST를 호출하고 있다. REST 호출하는 부분을 RetryTemplate로 감싸서 예외 발생 시 재시도 하도록 작성한다.

```java
@Component
public class TccRestAdapterImpl implements TccRestAdapter {
    // ...
    @Autowired
    private RetryTemplate retryTemplate;
 
    // ...
    @Override
    public void confirmAll(List<ParticipantLink> participantLinks) {
        participantLinks.forEach(participantLink -> {
            try {
                retryTemplate.execute((RetryCallback<Void, RestClientException>) context -> {
                    restTemplate.put(participantLink.getUri(), null);
                    return null;
                });
 
            } catch (RestClientException e) {
                log.error(String.format("TCC - Confirm Error[URI : %s]", participantLink.getUri().toString()), e);
            }
        });
    }
}
```

위의 코드에서는 REST PUT 호출(restTemplate.put()) 시 RestClientException이 발생하면 2초 간격으로 재 시도하며 최대 5번 시도한다.

# 마치며

[이전 글](https://www.popit.kr/rest-distributed-transaction-3-tcc-confirm/)에서 결과적 일관성을 [아파치 카프카Apache Kafka](https://kafka.apache.org/?ref=popit.kr)를 사용하여 구현하였는데, 이 경우 메시지 중복이 문제가 될 수 있다고 언급하였다. 다음 글은 이를 보안하는 멱등성Idempotence에 대해 다룬다.

# GitHub

전체 코드는 필자의 [GitHub 저장소](https://github.com/YooYoungmo/article-tcc?ref=popit.kr)에서 확인할 수 있다.

# 주석

\[1\] [토비의 스프링 3.1](http://www.yes24.com/24/Goods/7516721?Acode=101&ref=popit.kr) \- 3.5 템플릿과 콜백

> **템플릿**  
>  
> 템플릿(template)은 어떤 목적을 위해 미리 만들어둔 모양이 있는 틀을 가르킨다. 학생들이 도형을 그릴 때 사용하는 도형자 또는 모양자가 바로 템플릿이다. 프로그래밍에서는 고정된 틀 안에 바꿀 수 있는 부분을 넣어서 사용하는 경우 템플릿이라고 부른다.  
>  
> \- 이하 중략

> **콜백**  
>  
> 콜백(callback)은 실행되는 것을 목적으로 다른 오브젝트의 메소드의 전달되는 오브젝트를 말한다. 파라미터로 전달되지만 값을 참조하기 위한 것이 아니라 특정 로직을 담은 메소드를 실행 시키기 위해 사용한다.- 이하 중략

\[2\] OrderService는 스프링 부트를 사용하고 있다. 스프링 부트 부모 POM에서 이미 스프링 Retry 버전을 관리하고 있기 때문에 별도로 버전을 기술할 필요는 없다.

# 참고 자료

- [https://www.baeldung.com/spring-retry](https://www.baeldung.com/spring-retry?ref=popit.kr)
- [클라우드 네이티브 자바](http://www.yes24.com/24/Goods/61788283?Acode=101&ref=popit.kr) \- Isolating Failures and Graceful Degradation