카샤의 만개시기

RestTemplate 본문

Java/Spring

RestTemplate

SKaSha 2019. 7. 7. 02:31

HttpClient는 HTTP를 사용하여 통신하는 범용 라이브러리이고, RestTemplate은 HttpClient 를 추상화(HttpEntity의 json, xml 등)해서 제공해준다. 따라서 내부 통신(HTTP 커넥션)에 있어서는 Apache HttpComponents 를 사용한다.

작동원리

RestTemplate

  1. 어플리케이션이 RestTemplate를 생성하고, URI, HTTP메소드 등의 헤더를 담아 요청한다.
  2. RestTemplate 는 HttpMessageConverter 를 사용하여 requestEntity 를 요청메세지로 변환한다.
  3. RestTemplate 는 ClientHttpRequestFactory 로 부터 ClientHttpRequest 를 가져와서 요청을 보낸다.
  4. ClientHttpRequest 는 요청메세지를 만들어 HTTP 프로토콜을 통해 서버와 통신한다.
  5. RestTemplate 는 ResponseErrorHandler 로 오류를 확인하고 있다면 처리로직을 태운다.
  6. ResponseErrorHandler 는 오류가 있다면 ClientHttpResponse 에서 응답데이터를 가져와서 처리한다.
  7. RestTemplate 는 HttpMessageConverter 를 이용해서 응답메세지를 java object(Class responseType) 로 변환한다.
  8. 어플리케이션에 반환된다.

사용법

RestTemplate은 디폴트 생성자 외에 ClientHttpRequestFactory 인터페이스를 받는 생성자가있다. JavaDoc을 참조하면 여러 구현체가 있는데 그 중 SimpleClientHttpRequestFactoryHttpComponentsClientHttpRequestFactory가 대표적인 구현체이다.

RestTemplate restTemplate = new RestTemplate();

별도의 인자 없이 RestTemplate를 생성하게 되면 SimpleClientHttpRequestFactory 구현체를 사용하게 되고 이는 HttpUrlConnection을 이용한다.
또한 다양한 속성을 지원해주지 않으며, connection pool 역시 지원되지 않는다.

HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); 
factory.setReadTimeout(5000); // 읽기시간초과
factory.setConnectTimeout(3000); // 서버에 연결을 맺을 때의 타임아웃 
HttpClient httpClient = HttpClientBuilder.create() 
        .setMaxConnTotal(100) // 최대 커넥션 갯수
        .setMaxConnPerRoute(5) // IP/domain name당 최대 커넥션 갯수 
        .build(); 
factory.setHttpClient(httpClient); // 동기실행에 사용될 HttpClient 세팅 RestTemplate restTemplate = new RestTemplate(factory);

connection pool을 적용하기 위해서는 HttpComponentsClientHttpRequestFactor 구현체를 이용하면 되는데 이는 HttpClient 라이브러리를 이용하여 구현되어 있다. HttpClient는 HttpUrlConnection에 비해 모든 응답코드를 읽을 수 있으며, 타임아웃 설정과 쿠키 제어가 가능하다.

Keep-Alive

커넥션 풀은 HTTP에서 미리 연결을 유지하고 있으면서 필요할때 연결되어 있는 커넥션을 이용하여 빠르게 처리하는데 목적이 있다.
하지만 타겟 서버에서 Keep-Alive를 지원하지 않는다면 커넥션 풀은 작동하지 않아, 요청을 할때마다 새로운 커넥션이 연결되어 매번 핸드쉐이크가 발생될것이다.
커넥션 풀을 이용할때는 반드시 타겟 서버에서 Keep-Alive를 지원하는지 확인해야한다.

예외 처리

기본적으로 RestTemplate 은 HTTP 오류가 발생하면 RestClientResponseException의 확장인 다음 예외 중 하나를 throw합니다.

  • HttpClientErrorException : HTTP 상태 4xx의 경우
  • HttpServerErrorException : HTTP 상태 5xx의 경우
  • UnknownHttpStatusCodeException : 알려지지 않은 HTTP 상태의 경우

ResponseErrorHandler를 구현하여 적용하면 재사용 가능한 오류 처리기를 구현할수 있습니다.

@Component
public class RestTemplateResponseErrorHandler 
  implements ResponseErrorHandler {

    @Override
    public boolean hasError(ClientHttpResponse httpResponse) 
      throws IOException {

        return (
          httpResponse.getStatusCode().series() == CLIENT_ERROR 
          || httpResponse.getStatusCode().series() == SERVER_ERROR);
    }

    @Override
    public void handleError(ClientHttpResponse httpResponse) 
      throws IOException {

        if (httpResponse.getStatusCode()
          .series() == HttpStatus.Series.SERVER_ERROR) {
            // handle SERVER_ERROR
        } else if (httpResponse.getStatusCode()
          .series() == HttpStatus.Series.CLIENT_ERROR) {
            // handle CLIENT_ERROR
            if (httpResponse.getStatusCode() == HttpStatus.NOT_FOUND) {
                throw new NotFoundException();
            }
        }
    }
}

RestTemplateBuilder를 사용하여 템플릿을 작성하고 response flow에서 DefaultResponseErrorHandler를 대체한다.

@Service
public class BarConsumerService {

    private RestTemplate restTemplate;

    @Autowired
    public BarConsumerService(RestTemplateBuilder restTemplateBuilder) {
        RestTemplate restTemplate = restTemplateBuilder
          .errorHandler(new RestTemplateResponseErrorHandler())
          .build();
    }

    public Bar fetchBarById(String barId) {
        return restTemplate.getForObject("/bars/4242", Bar.class);
    }

}

비동기 처리

RestTemplate 는 동기처리에 사용되며 비동기 처리는 org.springframework.web.client.AsyncRestTemplate 를 사용해야 한다.

추가 참조

https://medium.com/@circlee7/resttemplate-host-header-e30596ab04dd

참조

https://sjh836.tistory.com/141

'Java > Spring' 카테고리의 다른 글

JPA에서 Optimistic Lock과 Pessimistic Lock  (0) 2019.07.08
Spring AOP, Proxy  (0) 2019.07.04
Comments