빨간색코딩

RestTemplate (정의, 특징, URLConnection, HttpClient, 동작원리, 사용법, connection pool 적용) 본문

Spring

RestTemplate (정의, 특징, URLConnection, HttpClient, 동작원리, 사용법, connection pool 적용)

빨간색소년 2018. 2. 26. 21:45

참조문서 : https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html

1. RestTemplate이란?

spring 3.0 부터 지원한다. 스프링에서 제공하는 http 통신에 유용하게 쓸 수 있는 템플릿이며, HTTP 서버와의 통신을 단순화하고 RESTful 원칙을 지킨다. jdbcTemplate 처럼 RestTemplate 도 기계적이고 반복적인 코드들을 깔끔하게 정리해준다. 요청보내고 요청받는데 몇줄 안될 정도..

특징

  • 기계적이고 반복적인 코드를 최대한 줄여줌
  • RESTful형식에 맞춤
  • json, xml 를 쉽게 응답받음

2. HTTP 서버와의 다양한 통신방법

2-1. URLConnection

jdk 1.2 부터 내장되어 있으며, java.net 패키지에 있다. URL의 내용을 읽어오거나, URL 주소에 GET, POST로 데이터를 전달 할 때 사용한다. 또한 http 프로토콜 이외에도 가능하다.(file 등) 보통 아래와 같이 사용한다.

  1. new URL("http:// ....")
  2. openConnection()
  3. URLConnection
  4. getInputStream, getOutputStream
  5. InputStream, OutputStream 처리

2-1-1. 문제점

  • 응답코드가 4xx 거나 5xx 면 IOException 이 터진다.
  • 타임아웃을 설정할 수 없다.
  • 쿠키 제어가 불가

2-2. HttpClient

3.x일 땐 apache commons 의 프로젝트였다가 승급해서, 아파치 탑 프로젝트가 되었다. 4.x부터는 Apache HttpComponents 로 불린다. maven dependency 를 설정하거나, http://hc.apache.org/downloads.cgi 에서 다운로드할 수 있다. org.apache.http 패키지에 있다.보통 아래처럼 사용한다.

  1. CloseableHttpClient httpclient = HttpClients.createDefault();
  2. 메소드에 따라 new HttpGet("http:// ....");
  3. CloseableHttpResponse response = httpclient.execute(httpget);
  4. HttpEntity entity = response.getEntity();
  5. Stream으로 entity.getContent() 처리 등

2-2-1. URLConnection 와 비교하였을 때 장점

  • 모든 응답코드를 읽을 수 있다. httpResponse.getStatusLine().getStatusCode()
  • 타임아웃 설정 가능
  • 쿠키 제어가 가능

2-2-2. 문제점

  • URLConnection 을 이용한 방식보다 코드가 간결해졌지만, 여전히 반복적이고 코드들이 길다.
  • 스트림 처리 로직을 별도로 짜야한다. (EntityUtils 를 쓰면 되는거 같긴하지만)
  • 응답의 컨텐츠타입에 따라 별도 로직이 필요하다. (RestTemplate 가 이때 유용!!)

3. RestTemplate 의 동작원리

org.springframework.http.client 패키지에 있다. HttpClient는 HTTP를 사용하여 통신하는 범용 라이브러리이고, RestTemplate은 HttpClient 를 추상화(HttpEntity의 json, xml 등)해서 제공해준다. 따라서 내부 통신(HTTP 커넥션)에 있어서는 Apache HttpComponents 를 사용한다. 만약 RestTemplate 가 없었다면, 직접 json, xml 라이브러리를 사용해서 변환해야 했을 것이다.

  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. 어플리케이션에 반환된다.

4. RestTemplate 를 써보자

4-1. 생성하기

  • 기본 생성 : RestTemplate restTemplate = getRestTempalte();

  • 설정 생성 :

      HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
      factory.setReadTimeout(5000);  // 읽기시간초과, ms
      factory.setConnectTimeout(3000); // 연결시간초과, ms
      HttpClient httpClient = HttpClientBuilder.create()
          .setMaxConnTotal(100) // connection pool 적용
          .setMaxConnPerRoute(5) // connection pool 적용
          .build();
      factory.setHttpClient(httpClient); // 동기실행에 사용될 HttpClient 세팅 
      RestTemplate restTemplate = new RestTemplate(factory);
    

connection pool 적용

참조문서 : https://stackoverflow.com/questions/31869193/using-spring-rest-template-either-creating-too-many-connections-or-slow/

언젠가 만날 것 같아서 기록.. RestTemplate 은 기본적으로 connection pool 을 사용하지 않는다. 따라서 연결할 때 마다, 로컬 포트를 열고 tcp connection 을 맺는다. 이때 문제는 close() 이후에 사용된 소켓은 TIME_WAIT 상태가 되는데, 요청량이 많다면 이런 소켓들을 재사용하지 못하고 소켓이 오링나서 응답이 지연될 것이다.

이런 경우 connection pool 을 사용해서 해결할 수 있는데, DBCP마냥 소켓의 갯수를 정해서 재사용하는 것이다. RestTemplate 에서 connection pool 을 적용하려면, 위와 같이 HttpClient 를 만들고 setHttpClient() 를 해야한다.

  • setMaxConnPerRoute : IP,포트 1쌍에 대해 수행 할 연결 수를 제한한다.
  • setMaxConnTotal : 최대 오픈되는 커넥션 수를 제한한다.

3-2. 사용하기

주요 메소드

RestTemplate MethodHTTP Method설명
executeAny
exchangeAny헤더세팅해서 HTTP Method로 요청보내고 ResponseEntity로 반환받음
getForObjectGETget 요청을 보내고 java object로 매핑받아서 반환받음
getForEntityGETget 요청을 보내고 ResponseEntity로 반환받음
postForLocationPOSTpost 요청을 보내고 java.net.URI 로 반환받음
postForObjectPOSTpost 요청을 보내고 ResponseEntity로 반환받음
putPUT
deleteDELETE
headForHeadersHEAD
optionsForAllowOPTIONS

요청할 URL

다양한 방법이 있다.

  • UriComponentsBuilder 로 파라미터를 붙이거나 String.format 로 붙이거나 등등
  • (/user/{id}, ... , "redboy") 처럼 rest하게 넘길 수도 있다.
  • map 을 이용해서 더 깔끔하게 할 수도 있다.

Object 로 받기

ForObject 를 사용할때, 응답 xml이나 json 에 맞는 java object(Class responseType)가 필요하다. @XmlElement 를 사용하거나 @JsonProperty 등을 사용하여 매핑해줘야한다.

에러 처리

DefaultResponseErrorHandler를 사용하여 HTTP Error 를 제어한다. restTemplate.setErrorHandler 를 통해 커스텀 핸들러를 등록할 수 있다.

비동기 처리

RestTemplate 는 동기처리에 사용된다. 비동기 처리는 org.springframework.web.client.AsyncRestTemplate 를 사용해야 한다. 언젠가 쓸 일이 오겠지..

5. 예제

import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

public class RestTemplateEx {
    public static void main(String[] args) {
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setReadTimeout(5000);  // 읽기시간초과, ms
        factory.setConnectTimeout(3000); // 연결시간초과, ms
        HttpClient httpClient = HttpClientBuilder.create()
            .setMaxConnTotal(100) // connection pool 적용
            .setMaxConnPerRoute(5) // connection pool 적용
            .build();
        factory.setHttpClient(httpClient); // 동기실행에 사용될 HttpClient 세팅
        RestTemplate restTemplate = new RestTemplate(factory);

        String url = "http://testapi.com/search?text=1234"; // 예제니까 애초에 때려박음..

        Object obj = restTemplate.getForObject("요청할 URI 주소", "응답내용과 자동으로 매핑시킬 java object");
System.out.println(obj); } }


Comments