如果阻止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。另一种方法是使用wait
和notify
。但这会导致每个请求创建大量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;
}
}
现在,我想我需要进行性能检查。