Web/Spring 2019. 6. 13. 22:30

 

오늘 포스팅할 내용은 Spring의 RestTemplate입니다. 우선 RestTemplate란 Spring 3.0부터 지원하는 Back End 단에서 Http 통신에 유용하게 쓰이는 템플릿 객체이며, 복잡한 HttpClient 사용을 한번 추상화하여 Http 통신사용을 단순화한 객체입니다. 즉, HttpClient의 사용에 있어 기계적이고 반복적인 코드들을 한번 랩핑해서 손쉽게 사용할 수 있게 해줍니다. 또한 json,xml 포멧의 데이터를 RestTemplate이 직접 객체에 컨버팅해주기도 합니다.

 

이렇게 사용하기 편한 RestTemplate에서도 하나 짚고 넘어가야할 점이 있습니다. RestTemplate 같은 경우에는 Connection Pooling을 직접적으로 지원하지 않기 때문에 매번 RestTemplate를 호출할때마다, 로컬에서 임시 TCP 소켓을 개방하여 사용합니다. 또한 이렇게 사용된 TCP 소켓은 TIME_WAIT 상태가 되는데, 요청량이 엄청 나게 많아진다면 이러한 상태의 소켓들은 재사용 될 수 없기 때문에 응답이 지연이 될것입니다. 하지만 이러한 RestTemplate도 Connection Pooling을 이용할 수 있는데 이것은 바로 RestTemplate 내부 구성에 의해 가능합니다. 바로 내부적으로 사용되는 HttpClient를 이용하는 것입니다. 바로 예제 코드로 들어가겠습니다.

 

우선 Connection pool을 적용하기 위한 HttpClientBuilder를 사용하기 위해서는 dependency 라이브러리가 필요하다.

 

compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.6'

 

각자 필요한 버전을 명시해서 의존성을 추가해준다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    /*
     * Connection Pooling을 적용한 RestTemplate
     */
    @Bean(name="restTemplateClient")
    public RestTemplate restClient() {
        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        /*
         * 타임아웃 설정
         */
        //httpRequestFactory.setConnectTimeout(timeout);
        //httpRequestFactory.setReadTimeout(timeout);
        //httpRequestFactory.setConnectionRequestTimeout(connectionRequestTimeout);
        HttpClient httpClient = HttpClientBuilder.create()
                                                 .setMaxConnTotal(150)
                                                 .setMaxConnPerRoute(50)
                                                 .build();
        httpRequestFactory.setHttpClient(httpClient);
        
        return new RestTemplate(httpRequestFactory);
    }
cs

 

위에 코드를 보면 최대 커넥션 수(MaxConnTotal)를 제한하고 IP,포트 1쌍 당 동시 수행할 연결 수(MaxConnPerRoute)를 제한하는 설정이 포함되어있습니다. 이런식으로 최대 커넥션 수를 150개로 제한하여 150개의 자원내에서 모든 일을 수행하게 되는 것입니다. 마치 DB의 Connection Pool 과 비슷한 역할이라고 보시면 됩니다.(물론 같지는 않음.) 그리고 RestTemplate은 Multi Thread 환경에서 safety 하기 때문에 빈으로 등록하여 가져다 쓰도록 하였습니다. 

 

마지막으로 구글링을 하던 도중에 Keep-alive 활성화가 되야지만 HttpClient의 Connection Pooling 지원이 가능하다고 나와있습니다. 기본적으로 HTTP1.1은 Keep-alive가 활성화되어 있지만 이부분은 더 깊게 알아봐야할 점인것 같습니다. 만약 해당 부분에 대해 아시는 분은 꼭 댓글에 코멘트 부탁드리겠습니다.

posted by 여성게
:

Java - ConnectionTimeout,ReadTimeout,SocketTimeout 차이점?


사실 지금까지 웹개발을 해오면서 ConnectionTimeout,ReadTimeout,SocketTimeout에 대해 대략적으로만 알고있었지 

사실 정확히 설명해봐라 혹은 차이점을 설명해봐라하면 대답하기 힘든 부분이 있었다. 이번 포스팅으로 

정확한 타임아웃 개념을 잡아보려고한다.

 

 

 

ConnectionTimeout이란?

ConnectionTimeout이라는 개념을 설명하기 전에 URL로 HTTP호출을 할 때 어떤 방식으로 수행되는지 이해가 필요하다.웹 브라우저가 네이버 서버에 접속하기 위해서 서버와 연결된 상태가 되어야한다. 보통 연결을 구성하기위해TCP Connection과 동일하게 3-way-HandShake 방식으로 수행하게 된다. 3-way HandShake가 정상적으로
수행하게 되면 웹 브라우저와 네이버 서버는 연결된 상태가 되는데, 이때까지 소요된 시간을 Connection에 소요된 시간이라고 할 수 있다. 

"즉,ConnectionTimeout이란 3-way HandShake가 정상적으로 수행되어 서버에 연결되기까지 소요된시간이다."

 

SocketTimeout이란?

클라이언트와 서버가 연결된 상태 이후에 서버는 페이지를 브라우저에 랜더링하기 위해 데이터를 전송한다. 이때 하나의 패킷으로데이터를 전송하는 것이 아니라 여러 개의 패킷으로 나눠서 전송하게 된다. 여러개의 패킷이 전송될 때, 각 패킷 간의 시간 Gap이생길 수 있는데 이 시간의 임계치를 SocketTimeout이라고 한다.

"즉, SocketTimeout이란 데이터를 여러개의 패킷으로 나누어 보낼때 각 패킷간의 시간 Gap을 이야기한다."

 


위의 두개를 그림으로 표현해보면,

 

결국 위의 두개의 시간설정은 URL을 호출 할때 꼭 필요한 설정이다. 만약 두 시간이 설정되지 않는다면?URL 접속 무한 대기가 발생될 수 있다.

 

ReadTimeout이란?

"Connection 맺은 후 Response(응답)을 받기까지 소요될 시간의 임계치이다."

 

사소하게 넘어 갈 수 있는 것들이지만 네트워크 상에 돌아가는 무엇인가를 개발한다면 꼭 숙지해야 될 것들중 하나인 것 같다.

 

posted by 여성게
:
Web/Spring 2018. 10. 11. 22:14

요즘의 소프트웨어는 대부분 서버와 Json 형태의 데이터를 주고 받습니다. Json으로 주고 받게 되면서 이종소프트웨어 간의 통신도 자유로워 질수 있었습니다. 하지만 요즘은 클라이언트 단과 서버단의 Json 통신 이외에도 서버와 서버끼리도 Restful 한 통신을 하는 경우가 많아졌습니다.


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



출처: https://sjh836.tistory.com/141 [빨간색코딩]




Restful(서버-서버) RestTemplate


dependency


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
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
    <groupId>com.fasterxml.jackson.core/groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.9.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.7</version>
</dependency>
 
cs


RestTemplate도 결국 내부는 httpClient를 사용합니다. (기본 설정으로 사용한다면 상관없지만 세부 설정을 위해 dependency를 추가해줍니다.)





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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.spring.javaconfig.controller;
 
import java.net.URI;
 
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
 
import com.spring.javaconfig.bean.RestDTO;
 
@RestController
public class RestApiController {
    
    @RequestMapping(value="/rest")
    public Object getMethod() {
        String botCode="D9UD878EAJ";
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory=
                new HttpComponentsClientHttpRequestFactory();
        clientHttpRequestFactory.setReadTimeout(5000);
        clientHttpRequestFactory.setConnectionRequestTimeout(5000);
        clientHttpRequestFactory.setConnectTimeout(5000);
        HttpClient httpClient=HttpClientBuilder.create()
                .setMaxConnTotal(100)
                .setMaxConnPerRoute(5)
                .build();
        clientHttpRequestFactory.setHttpClient(httpClient);
        RestTemplate restTemplate=new RestTemplate(clientHttpRequestFactory);
        /*UriComponentsBuilder.newInstance()
            .scheme("http")
            .host("127.0.0.1")
            .port("8983")
            .path("/solr/{botCode}/textanalysis")
            .queryParam("q", "faq테스트")
            .queryParam("rows", 1)
            .queryParam("knn.cinfo", true)
            .queryParam("knn.k", 1)
            .queryParam("intent", true)
            .queryParam("ner", false)
            .queryParam("log", false)
            .build().expand(botCode)
            .encode()
            .toUri();
            
            queryParams 로 map객체를 이용해 한번에 쿼리스트링을 넣을수도 있다.
            */
        return restTemplate.getForObject(UriComponentsBuilder.newInstance()
                .scheme("http")
                .host("localhost")
                .port("8983")
                .path("/solr/{botCode}/select")
                .queryParam("q""faq테스트")
                .build().expand(botCode)
                .encode()
                .toUri(), Object.class);
    }
}
 
cs



RestTemplate은 커낵션을 열고 사용을 마친 후에 커넥션을 닫게 되면 close Wait상태가 된다고 합니다. 즉, db의 커넥션 풀과 같은 설정을 추가하여 불필요한 리소스, 성능이슈를 적절히 해결할 필요가 있습니다. 이 예제는 검색엔진인 solr를 이용하여 간단히 쿼리를 날려 결과를 받아 보는 요청입니다. 각각 메소드의 기능은 구글링해보시길...

posted by 여성게
: