Java 8 RestService: эффективный способ установить время ожидания ответа в Spring bean - PullRequest
0 голосов
/ 27 мая 2020

Я пишу пользовательский RestService (оболочка Apache Http Client 4.5.10) и RestServiceAsyn c (оболочка Apache httpasyncclient 4.1.4) в приложении Spring MVC monolithi c.

Ниже мой IRestService

public interface IRestService {

    public <T> T request(HttpUriRequest request, final ResponseParser<T> responseParser);

    public <T> Optional<T> request(final HttpMethod method);

    public <T> T call(HttpUriRequest request, ResponseHandler<T> responseHandler);

    default <T> Optional<T> get() {
        return request(HttpMethod.GET);
    }

    public default <T> Optional<T> get(String URL) {
        return get(URL, Collections.EMPTY_MAP, ResponseParsers.STRING);
    }

    public default <T> Optional<T> get(String URL, boolean isAsync) {
        return get(URL, Collections.EMPTY_MAP, ResponseParsers.STRING);
    }

    public default <T> Optional<T> get(String URL, Map params) {
        return get(URL, params, ResponseParsers.STRING);
    }

    public default <T> Optional<T> get(String url, Map<String, String> params, ResponseParser<T> responseParser) {
        return request(url, params, HttpMethod.GET, responseParser);
    }

    public default <T> Optional<T> get(String URL, ResponseParser<T> responseParser) {
        return request(URL, Collections.EMPTY_MAP, HttpMethod.GET, responseParser);
    }

    public default <T> Optional<T> request(String url, Map params, HttpMethod httpMethod, ResponseParser<T> responseParser) {
        return request(url, null, params, httpMethod, responseParser);
    }

    public  <T> Optional<T> request(String url, HttpEntity entity, Map params, HttpMethod httpMethod, ResponseParser<T> responseParser);

    HttpResponse execute(HttpUriRequest request, Closeable httpClient) ;
}

Ниже мой RestServiceBase

public abstract class RestServiceBase implements IRestService{

    @Override
    public <T> Optional<T> request(HttpMethod method) {
        return Optional.empty();
    }

    @Override
    public <T> Optional<T> request(String url, HttpEntity entity, Map params, HttpMethod httpMethod, ResponseParser<T> responseParser) {
        T respose = null;
        try {
            final HttpUriRequest httpUriRequest = buildHttpUriRequest(httpMethod, url, entity, params);

            respose = (T) request(httpUriRequest, responseParser);
        } catch (HttpException | URISyntaxException e) {
            e.printStackTrace();
        }
        return Optional.ofNullable(respose);
    }

    public <T> T request(HttpUriRequest request, final ResponseParser<T> responseParser) {
        return call(request, new BaseResponseHandler<>(entity -> {
            // Gets the content as a string
            String content = entity != null ? EntityUtils.toString(entity, "UTF-8") : null;
            // Parses the response
            return responseParser.parse(content);
        }));
    }


    private HttpUriRequest buildHttpUriRequest(HttpMethod httpMethod, String url, HttpEntity entity,
                                               Map<String, String> params) throws HttpException, URISyntaxException {
        URI uri = buildURI(url, params);
        final RequestBuilder requestBuilder = resolveRequestBuilder(httpMethod,uri);
        Optional.ofNullable(entity).ifPresent(requestBuilder::setEntity);
        return requestBuilder.build();
    }

    private URI buildURI(String url, Map<String, String> params) throws URISyntaxException {
        URIBuilder uriBuilder = new URIBuilder(url);
        Optional.ofNullable(params.entrySet())
                .ifPresent(map -> map.forEach(e -> uriBuilder.setParameter(e.getKey(), e.getValue())));

        return uriBuilder.build();
    }


    private RequestBuilder resolveRequestBuilder(HttpMethod method, URI uri) throws HttpException {
        switch (method) {
            case GET:
                return RequestBuilder.get().setUri(uri);

            case PUT:
                return RequestBuilder.put().setUri(uri);

            case POST:
                return RequestBuilder.post().setUri(uri);

            case DELETE:
                return RequestBuilder.delete().setUri(uri);
            default:
                throw new HttpException("Unsupported HttpMethod " + method);
        }

    }
}

Ниже мой RestService (который использует Синхронная версия Apache http-клиента)

@Service
public class RestService extends RestServiceBase{

     @Autowired
    HttpClientFactory           httpClientFactory;

    public HttpResponse execute(HttpUriRequest request, Closeable httpClient) {
        if(! (httpClient instanceof CloseableHttpClient))
            throw new RuntimeException("UnSupported HttpClient Exception");
        try {
            CloseableHttpClient closeableHttpClient = (CloseableHttpClient) httpClient;
            return closeableHttpClient.execute(request);
        } catch (IOException e) {
            throw new RuntimeException();
        }
    }

    private CloseableHttpClient getHttpClient() {
        ApacheHttpClient apacheHttpClient = (ApacheHttpClient) httpClientFactory.getApacheHttpClient();
        return (CloseableHttpClient) apacheHttpClient.getHttpClient();
    }

    public <T> T call(HttpUriRequest request, ResponseHandler<T> responseHandler) {
        try {
            try (CloseableHttpClient httpClient = getHttpClient()) {
                HttpResponse response = execute(request, httpClient);
                // Entity response
                HttpEntity entity = response.getEntity();
                try {
                    return responseHandler.handleResponse(request, response, entity);
                } finally {
                    EntityUtils.consume(entity);
                }
            }
        } catch (IOException e) {
            throw new ClientGeneralException(request, e);
        } finally {
            ((HttpRequestBase) request).releaseConnection();
        }
    }

}

Ниже мой RestService (который использует Асинхронный версию Apache http-клиента)

@Service
public class RestServiceAsync extends RestServiceBase{

     @Autowired
    HttpClientFactory           httpClientFactory;

    public HttpResponse execute(HttpUriRequest request, Closeable httpClient) {
        if(! (httpClient instanceof CloseableHttpAsyncClient))
            throw new RuntimeException("UnSupported HttpClient Exception");
        try {
            CloseableHttpAsyncClient closeableHttpClient = (CloseableHttpAsyncClient) httpClient;
            Future<HttpResponse> future = closeableHttpClient.execute(request, null);
            HttpResponse response = future.get();
            return response;
            //return (HttpResponse) closeableHttpClient.execute(request,null);
        } catch (InterruptedException | ExecutionException e) {
            request.abort();
            throw new RuntimeException();
        }
    }

    private CloseableHttpAsyncClient getHttpClient() {
        ApacheAsyncClient apacheAsyncClient = (ApacheAsyncClient) httpClientFactory.getApacheAsyncHttpClient();
        return (CloseableHttpClient) apacheAsyncClient.getHttpClient();
    }

    public <T> T call(HttpUriRequest request, ResponseHandler<T> responseHandler) {
        try {
            try (CloseableHttpAsyncClient httpClient = getHttpClient()) {
                HttpResponse response = execute(request, httpClient);
                // Entity response
                HttpEntity entity = response.getEntity();
                try {
                    return responseHandler.handleResponse(request, response, entity);
                } finally {
                    EntityUtils.consume(entity);
                }
            }
        } catch (IOException e) {
            throw new ClientGeneralException(request, e);
        } finally {
            ((HttpRequestBase) request).releaseConnection();
        }
    }
}

Ниже мой пример использования RestServiceAsync

@Service
ReportsService(){

    @Autowired
    @Qualifier("restServiceAsync")
    RestService restService;

    public getReport(){
            restService.get("http://localhost/reports");
    }
}

Я хочу представить чтение ответа timeout, чтобы основной поток, выполняющий метод RestServiceAsync.execute, не ждал больше, чем желаемое количество времени для различных вариантов использования. Ниже были мои подходы к этому

1 . Введите частную переменную timeout, используйте метод установки в RestServiceAsync и выполните HttpResponse response = future.get(timeout, TimeUnit.Seconds);. Этот подход нежизнеспособен, так как RestServiceAsync - это настройка одноэлементного bean-компонента timeout переменная будет отражать для всех пользователей приложения

2 . Отправьте timeout в качестве параметра из вызова метода как restService.get("http://localhost/reports",30); это сработает, но мне нужно обновить все соответствующие методы в IRestService.

Мне интересно, есть ли альтернативный эффективный подход для введения timeout без необходимости касаться всех методов в IRestService, но внесения изменений только в RestServiceAsync

PS: ApacheAsyncClient использует PoolingNHttpClientConnectionManager и ApacheHttpClient использует PoolingHttpClientConnectionManager

...