복붙노트

[SPRING] RestTemplate을 사용하여 요청 당 RequestConfiguration을 설정하는 방법

SPRING

RestTemplate을 사용하여 요청 당 RequestConfiguration을 설정하는 방법

나는 고객에 의해 사용되고있는 라이브러리를 가지고 있으며 그것들은 userid, timeout과 다른 필드를 가진 DataRequest 객체를 전달하고있다. 이제이 DataRequest 객체를 사용하여 URL을 만든 다음 RestTemplate을 사용하여 HTTP 호출을 만들고 서비스에서 JSON 응답을 반환합니다.이 JSON 응답을 사용하여 DataResponse 객체를 만들고이 DataResponse 객체를 반환합니다.

아래는 DataRequest 객체를 전달하여 고객이 사용하는 DataClient 클래스입니다. getSyncData 메서드에서 너무 많은 시간이 걸리면 요청을 시간 초과하기 위해 DataRequest에서 고객이 전달한 제한 시간 값을 사용하고 있습니다.

public class DataClient implements Client {

    private final RestTemplate restTemplate = new RestTemplate();
    private final ExecutorService service = Executors.newFixedThreadPool(10);

    // this constructor will be called only once through my factory
    // so initializing here
    public DataClient() {
        try {
          restTemplate.setRequestFactory(clientHttpRequestFactory());
        } catch (Exception ex) {
          // log exception
        }
    }           

    @Override
    public DataResponse getSyncData(DataRequest key) {
        DataResponse response = null;
        Future<DataResponse> responseFuture = null;

        try {
            responseFuture = getAsyncData(key);
            response = responseFuture.get(key.getTimeout(), key.getTimeoutUnit());
        } catch (TimeoutException ex) {
            response = new DataResponse(DataErrorEnum.CLIENT_TIMEOUT, DataStatusEnum.ERROR);
            responseFuture.cancel(true);
            // logging exception here               
        }

        return response;
    }   

    @Override
    public Future<DataResponse> getAsyncData(DataRequest key) {
        DataFetcherTask task = new DataFetcherTask(key, restTemplate);
        Future<DataResponse> future = service.submit(task);

        return future;
    }

    // how to set socket timeout value by using `key.getSocketTimeout()` instead of using hard coded 400
    private ClientHttpRequestFactory clientHttpRequestFactory() {
        HttpComponentsClientHttpRequestFactory requestFactory =
            new HttpComponentsClientHttpRequestFactory();
        RequestConfig requestConfig =
            RequestConfig.custom().setConnectionRequestTimeout(400).setConnectTimeout(400)
                .setSocketTimeout(400).setStaleConnectionCheckEnabled(false).build();
        SocketConfig socketConfig =
            SocketConfig.custom().setSoKeepAlive(true).setTcpNoDelay(true).build();

        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager =
            new PoolingHttpClientConnectionManager();
        poolingHttpClientConnectionManager.setMaxTotal(300);
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(200);

        CloseableHttpClient httpClientBuilder =
            HttpClientBuilder.create().setConnectionManager(poolingHttpClientConnectionManager)
                .setDefaultRequestConfig(requestConfig).setDefaultSocketConfig(socketConfig).build();

        requestFactory.setHttpClient(httpClientBuilder);
        return requestFactory;
    }       
}

DataFetcherTask 클래스 :

public class DataFetcherTask implements Callable<DataResponse> {

    private final DataRequest key;
    private final RestTemplate restTemplate;

    public DataFetcherTask(DataRequest key, RestTemplate restTemplate) {
        this.key = key;
        this.restTemplate = restTemplate;
    }

    @Override
    public DataResponse call() throws Exception {
        // In a nutshell below is what I am doing here. 
        // 1. Make an url using DataRequest key.
        // 2. And then execute the url RestTemplate.
        // 3. Make a DataResponse object and return it.
    }
}

회사 내부의 고객은 코드 기반에서 내 공장을 사용하여 아래와 같이 내 라이브러리를 사용합니다.

// if they are calling `getSyncData()` method
DataResponse response = DataClientFactory.getInstance().getSyncData(key);

// and if they want to call `getAsyncData()` method
Future<DataResponse> response = DataClientFactory.getInstance().getAsyncData(key);

비동기 + 대기로 동기화 호출을 구현하고 있습니다. 스레드 수를 줄이기 위해 제어를하지 않고도 서비스를 포격 할 수 있기 때문입니다.

문제 설명:-

내 DataRequest 클래스에 socket timeout이라는 또 다른 timeout 변수를 추가 할 것이고, 하드 코딩 된 400 값을 사용하는 대신 clientHttpRequestFactory () 메소드에서 해당 변수 값 (key.getSocketTimeout ())을 사용하려고합니다. 그렇게하기위한 최선의 효율적인 방법은 무엇입니까?

지금은 Inversion of Control을 사용하고 RestTemplate을 생성자에서 전달하여 모든 Task 객체간에 RestTemplate을 공유합니다. 내 clientHttpRequestFactory () 메소드에서 key.getSocketTimeout () 값을 사용하는 방법을 혼동합니다. 이 질문은 대부분 내 ClientHttpRequestFactory () 메서드에서 key.getSocketTimeout () 값을 사용할 수 있도록 RestTemplate을 효율적으로 사용하는 방법에 대한 디자인 문제라고 생각합니다.

내가 코드를 단순화하여 아이디어가 명확 해지고 Java 7을 사용하게되었습니다. ThreadLocal을 사용하면 여기있는 유일한 옵션이거나 더 좋고 최적화 된 방법이 있습니까?

해결법

  1. ==============================

    1.Peter가 설명 하듯이 ThreadLocal을 사용하는 것은 좋은 생각이 아닙니다. 그러나 "메서드 호출 체인을 통해 값을 전달하는"방법을 찾을 수도 없습니다.

    Peter가 설명 하듯이 ThreadLocal을 사용하는 것은 좋은 생각이 아닙니다. 그러나 "메서드 호출 체인을 통해 값을 전달하는"방법을 찾을 수도 없습니다.

    일반 "Apache HttpClient"를 사용하는 경우 HttpGet / Put / etc를 만들 수 있습니다. 간단히 전화 해. httpRequest.setConfig (myRequestConfig). 즉, 요청마다 요청 구성 설정 (아무 것도 요청에 설정되어 있지 않으면 요청을 실행하는 HttpClient의 요청 구성이 사용됩니다).

    대조적으로, RestTemplate createRequest (URI, HttpMethod) 호출 (HttpAccessor에 정의 됨) ClientHttpRequestFactory를 사용합니다. 즉, 요청마다 요청 구성을 설정하는 옵션이 없습니다. 왜 Spring이이 옵션을 남겨 놓았는지 확신 할 수 없지만, 합리적인 기능 요구 사항 (또는 어쩌면 여전히 뭔가를 놓치고있는 것 같습니다)이 보인다.

    "그들은 아무런 통제없이 우리의 서비스를 폭격 할 수있다"

  2. ==============================

    2.ThreadLocal은 메서드 속성을 통해 일반적으로 전달할 동적 값을 전달하는 방법이지만 변경할 수 없거나 변경할 수없는 API를 사용하고 있습니다.

    ThreadLocal은 메서드 속성을 통해 일반적으로 전달할 동적 값을 전달하는 방법이지만 변경할 수 없거나 변경할 수없는 API를 사용하고 있습니다.

    스레드 스택의 특정 레벨에서 ThreadLocal (여러 값을 포함 할 수있는 가능한 데이터 구조)을 설정하고 스택 위로 더 사용할 수 있습니다.

    이것이 최선의 접근 방법입니까? 아니, 당신은 정말로 메서드 호출의 체인을 통해 값을 전달해야하지만 때로는 이것이 실용적이지 않습니다.

    너는으로 시작할지도 모른다.

    static final ThreadLocal<Long> SOCKET_TIMEOUT = new ThreadLocal<>();
    

    당신이 할 수있는 그것을 설정하려면

    SOCKET_TIMEOUT .set(key.getSocketTimeout());
    

    당신이 할 수있는 가치를 얻으려면

    long socketTimeout = SOCKET_TIMEOUT.get();
    
  3. from https://stackoverflow.com/questions/37125694/how-to-set-requestconfiguration-per-request-using-resttemplate by cc-by-sa and MIT license