java - 如何在多线程应用程序中高效使用 RestTemplate?

标签 java multithreading spring apache-httpclient-4.x resttemplate

我在我的一个库中使用 RestTemplate 作为我的 HttpClient。我不确定我是否在多线程环境中正确使用它,因为我的库将在多线程环境中以非常重的负载使用,因此它必须非常快。

下面是我的 DataClient 类:

public class DataClient implements Client {

    private RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
    private ExecutorService executor = Executors.newFixedThreadPool(10);

    // for synchronous call
    @Override
    public DataResponse executeSync(DataKey key) {
        DataResponse dataResponse = null;
        Future<DataResponse> future = null;

        try {
            future = executeAsync(key);
            dataResponse = future.get(key.getTimeout(), TimeUnit.MILLISECONDS);
        } catch (TimeoutException ex) {
            dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT, DataStatusEnum.ERROR);
            future.cancel(true);
        } catch (Exception ex) {
            dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
        }

        return dataResponse;
    }

    //for asynchronous call
    @Override
    public Future<DataResponse> executeAsync(DataKey key) {
        Future<DataResponse> future = null;
        Task task = new Task(key, restTemplate);
        future = executor.submit(task);

        return future;
    }

    // does this looks right?
    private ClientHttpRequestFactory clientHttpRequestFactory() {
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        // setting 2000 ms as the default timeout for each Http Request
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(2000).setConnectTimeout(2000)
                .setSocketTimeout(2000).setStaleConnectionCheckEnabled(false).build();
        SocketConfig socketConfig = SocketConfig.custom().setSoKeepAlive(true).setTcpNoDelay(true).build();

        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
        poolingHttpClientConnectionManager.setMaxTotal(800);
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(700);

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

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

将执行实际任务的简单类:

public class Task implements Callable<DataResponse> {

    private final DataKey key;
    private final RestTemplate restTemplate;

    public Task(DataKey key, RestTemplate restTemplate) {
        this.key = key;
        this.restTemplate = restTemplate;
    }

    @Override
    public DataResponse call() {
        DataResponse dataResponse = null;
        String response = null;

        try {
            String url = createURL();
            response = restTemplate.getForObject(url, String.class);

            dataResponse = new DataResponse(response, DataErrorEnum.OK, DataStatusEnum.SUCCESS);
        } catch (RestClientException ex) {
            dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
        } catch (Exception ex) {
            dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
        }

        return dataResponse;
    }
}

下面是我的工厂,我用它来创建 DataClient 的单个实例,这意味着它也将有 RestTemplate 的单个实例。

public class DataClientFactory {

    private DataClientFactory() {}

    private static class ClientHolder {
        private static final DataClient INSTANCE = new DataClient();
    }

    public static Client getInstance() {
        return ClientHolder.INSTANCE;
    }
}

这就是我调用以获取数据的方式:

DataResponse response = DataClientFactory.getInstance().executeSync(dataKey);

现在我的问题是 - 我不确定我是否将 RestTemplateHttpComponentsClientHttpRequestFactory 一起正确使用。我是否需要 PoolingHttpClientConnectionManager 以及 RestTemplate

我的主要目标是在多线程环境中高效地使用 RestTemplate。由于我的库将在非常重的负载下使用,因此它必须非常快。在重负载下,我看到很多 TIME_WAIT 连接,所以我添加了 clientHttpRequestFactory() 方法以与 RestTemplate 一起使用。

最佳答案

RestTemplatethread safe在 Spring 。因此,您可能想要做的是在您的应用程序中只创建一个 RestTemplate 实例,并在多个线程之间共享它。这当然是假设您将对所有人使用相同的 HTTP 属性(如超时、设置 Activity 等)。如果您需要更改连接属性,您可以在 RestTemplate 对象的应用启动时创建一个池,并使用它将 RestTemplate 实例注入(inject)调用者类。

关于java - 如何在多线程应用程序中高效使用 RestTemplate?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31151990/

相关文章:

java - 我可以使用 Collection.size() 来替换这段代码中的计数器吗?

java - 将 Spring @Value 传递给静态 @BeforeAll Junit5 方法

java - MySQL 插入数据时出现错误 "Duplicate entry ' ' for key "

java - 如何更改 json 对象名称(键)?

JavaFX 卡住问题

java - BigDecimal,setScale 问题

c# - 使用异步和事件访问 UI 线程上的 UI 控件

java - 如何获取 Java JAR 文件中资源的路径

c# - .NET 中的生产者/消费者模式

java - Spring Boot应用程序属性暂存和生产