使用阻止代码的非阻塞代码来减少使用的线程数

时间:2017-06-18 01:27:42

标签: java spring-mvc asynchttpclient

如果阻止API,调用者希望代码返回一些值。

e.g。 response = blockingAPI.execute()

如果是非阻塞代码,我们通过回调进行通信。

整个请求(Web)管道是阻塞和顺序的。步骤取决于先前步骤的响应。在其中一个步骤中,我们需要调用Web服务。当前设置假定使用阻止API调用此服务。

我们如何以非阻塞方式调用它并仍然像阻塞代码一样返回管道?在此尝试非阻塞调用的主要原因是能够提供更多HTTP每个服务器的请求,每个服务调用不使用新线程。目的是在IO完成时不执行其他任务,因为需要维护序列。其中一种方法是获取Future,循环并检查它是否已完成并在其间进入睡眠状态。这听起来很糟糕。

我们正在使用Spring并且每个请求都有自定义执行管道。我正在考虑使用async-http-client进行服务调用。

示例同步客户端

@Component
public class SyncClient {
    private static final int CONNECTION_TIMEOUT = 1000;
    private static final int CONNECTION_REQUEST_TIMEOUT = 1000;
    private static final int SOCKET_TIMEOUT = 1000;
    private static final String URL = "http://localhost:8080/returnback";
    private static final String RETURN_BACK = "returnBack";

    private final HttpClientBuilder httpClientBuilder;

    public SyncClient() {
        httpClientBuilder = HttpClientBuilder.create();
        httpClientBuilder.useSystemProperties();
        httpClientBuilder.setRedirectStrategy(new LaxRedirectStrategy());
    }

    public Map<String, String> send() throws Exception {
        final CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
        final HttpRequestBase httpRequestBase = new HttpGet(URL);
        httpRequestBase.setConfig(getCustomConfig());

        final String returnBackValue = UUID.randomUUID().toString();
        httpRequestBase.addHeader(new BasicHeader(RETURN_BACK, returnBackValue));

        CloseableHttpResponse response = null;
        final Map<String, String> output = new HashMap<>();
        try {
            response = closeableHttpClient.execute(httpRequestBase);
            if(response == null) {
                throw new RuntimeException("Unexpected null http response");
            }
            output.put(returnBackValue, response.getFirstHeader(RETURN_BACK).getValue());
        }
        finally {
            HttpClientUtils.closeQuietly(response);
        }

        return output;
    }

    private RequestConfig getCustomConfig() {
        return RequestConfig.copy(RequestConfig.DEFAULT)
                .setConnectTimeout(CONNECTION_TIMEOUT)
                .setSocketTimeout(SOCKET_TIMEOUT)
                .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT).build();
    }
}

示例轮询异步客户端

@Component
public class PollingAsyncClient {
    private static final String URL = "http://localhost:8080/returnback";
    private static final String RETURN_BACK = "returnBack";
    private static final int CONNECTION_TIMEOUT = 1000;
    private static final int REQUEST_TIMEOUT = 1000;

    private final AsyncHttpClient asyncHttpClient;

    public PollingAsyncClient() {
        final AsyncHttpClientConfig asyncHttpClientConfig = new DefaultAsyncHttpClientConfig.Builder()
                .setMaxConnections(200)
                .setMaxConnectionsPerHost(20)
                .setConnectTimeout(CONNECTION_TIMEOUT).build();

        this.asyncHttpClient = new DefaultAsyncHttpClient(asyncHttpClientConfig);
    }

    public Map<String, String> send() {
        final RequestBuilder requestBuilder = new RequestBuilder("GET");
        final Request request = requestBuilder.setUrl(URL)
                .setRequestTimeout(REQUEST_TIMEOUT)
                .build();

        final String returnBackValue = UUID.randomUUID().toString();
        requestBuilder.addHeader(RETURN_BACK,returnBackValue);

        ListenableFuture<Response> listenableFuture = null;
        final Map<String, String> output = new HashMap<>();
        try {
            listenableFuture = asyncHttpClient.executeRequest(request);

            while (!listenableFuture.isDone()) {
                Thread.sleep(50);
            }

            Response response = listenableFuture.get();
            output.put(returnBackValue, response.getHeader(RETURN_BACK));

        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        return output;
    }
}

现在,消费者可以将两个客户端称为:

Map<String, String> map = null;

try {
    map = client.send();
} catch (Exception e) {
    e.printStackTrace();
}

因此对消费者而言,他们都是阻挠的。但根据我的理解,非阻塞客户端将消耗更少的资源(线程)。但是这个sleep循环会占用CPU。另一种方法是使用waitnotify。但这会导致每个请求创建大量new个对象。 我会尝试并更新。

编辑(评论): 我说“睡眠循环”会占用CPU,即循环会消耗掉,这就是我的意思。线程将唤醒并重试。

修改

等待异步客户端的示例

public class WaitingAsyncClient {
    private static final String URL = "http://localhost:8080/returnback";
    private static final String RETURN_BACK = "returnBack";
    private static final int CONNECTION_TIMEOUT = 2000;
    private static final int REQUEST_TIMEOUT = 2000;

    private final AsyncHttpClient asyncHttpClient;

    public WaitingAsyncClient() {
        final AsyncHttpClientConfig asyncHttpClientConfig = new DefaultAsyncHttpClientConfig.Builder()
                .setMaxConnections(200)
                .setMaxConnectionsPerHost(20)
                .setConnectTimeout(CONNECTION_TIMEOUT).build();

        this.asyncHttpClient = new DefaultAsyncHttpClient(asyncHttpClientConfig);
    }

    public Map<String, String> send() {
        final Object lock = new Object();
        final RequestBuilder requestBuilder = new RequestBuilder("GET");
        final Request request = requestBuilder.setUrl(URL)
                .setRequestTimeout(REQUEST_TIMEOUT)
                .build();

        final String returnBackValue = UUID.randomUUID().toString();
        requestBuilder.addHeader(RETURN_BACK,returnBackValue);

        ListenableFuture<Response> listenableFuture = null;
        final Map<String, String> output = new HashMap<>();
        try {
            listenableFuture = asyncHttpClient.executeRequest(request, new AsyncHandler<Response>() {

                private final Response.ResponseBuilder builder = new Response.ResponseBuilder();

                @Override
                public void onThrowable(Throwable t) {
                    synchronized (lock) {
                        lock.notify();
                    }
                }

                @Override
                public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
                    builder.accumulate(bodyPart);
                    return State.CONTINUE;
                }

                @Override
                public State onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
                    builder.accumulate(responseStatus);
                    return State.CONTINUE;
                }

                @Override
                public State onHeadersReceived(HttpResponseHeaders headers) throws Exception {
                    builder.accumulate(headers);
                    return State.CONTINUE;
                }

                @Override
                public Response onCompleted() throws Exception {
                    synchronized (lock) {
                        lock.notify();
                    }
                    return builder.build();
                }
            });

            synchronized (lock) {
                lock.wait();
            }

            Response response = listenableFuture.get();
            output.put(returnBackValue, response.getHeader(RETURN_BACK));

        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        return output;
    }
}

现在,我想我需要进行性能检查。

0 个答案:

没有答案