我需要创建一个库,我将在其中具有同步和异步功能。
executeSynchronous()
- 等到我有结果,返回结果。executeAsynchronous()
- 立即返回一个Future,如果需要,可以在其他事情完成后处理。我图书馆的核心逻辑
客户将使用我们的库,他们将通过传递DataKey
构建器对象来调用它。然后,我们将使用该DataKey
对象构造一个URL,并通过执行它来对该URL进行HTTP客户端调用,然后在我们将响应作为JSON字符串返回之后,我们将该JSON字符串发送回给我们的客户。它是通过创建DataResponse
对象。有些客户会调用executeSynchronous()
,有些可能会调用executeAsynchronous()
方法,因此我需要在库中单独提供两种方法。
接口:
public interface Client {
// for synchronous
public DataResponse executeSynchronous(DataKey key);
// for asynchronous
public Future<DataResponse> executeAsynchronous(DataKey key);
}
然后我的DataClient
实现了上面的Client
接口:
public class DataClient implements Client {
private RestTemplate restTemplate = new RestTemplate();
private ExecutorService executor = Executors.newFixedThreadPool(10);
// for synchronous call
@Override
public DataResponse executeSynchronous(DataKey key) {
DataResponse dataResponse = null;
Future<DataResponse> future = null;
try {
future = executeAsynchronous(key);
dataResponse = future.get(key.getTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {
PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, key);
dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
//for asynchronous call
@Override
public Future<DataResponse> executeAsynchronous(DataKey key) {
Future<DataResponse> future = null;
try {
Task task = new Task(key, restTemplate);
future = executor.submit(task);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
}
return future;
}
}
将执行实际任务的简单类:
public class Task implements Callable<DataResponse> {
private DataKey key;
private 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);
// it is a successful response
dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS);
} catch (RestClientException ex) {
PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, key);
dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
// create a URL by using key object
private String createURL() {
String url = somecode;
return url;
}
}
我们公司内的客户将使用我的库,如下所示,在我们的代码库中使用我的工厂 -
// if they are calling `executeSynchronous()` method
DataResponse response = DataClientFactory.getInstance().executeSynchronous(dataKey);
// and if they want to call `executeAsynchronous()` method
Future<DataResponse> response = DataClientFactory.getInstance().executeAsynchronous(dataKey);
为我的库实现同步和异步方法的最佳方法是什么?实施sync call as async + waiting
是不是一个坏主意?因为使用我的currrent设置,每次调用它会从线程池中消耗一个线程吗?如果是,那么任何人都可以解释为什么这是一个坏主意,它会有任何性能问题吗?
根据上述标准,您将如何实现同步和异步方法?做这个的最好方式是什么?这个库将在非常繁重的负载下使用,并且必须很快,这意味着无论我的服务器采取什么响应都需要时间。
我应该在我的代码库中使用AsyncRestTemplate
哪个是非同步非阻塞架构?
答案 0 :(得分:4)
对于synchronous
调用,在单独的线程中执行绝对不是一个好主意。
在这种情况下,您需要为线程带来额外的成本和资源以及线程上下文切换的成本。
如果有很多synchronous
个调用,那么你将不必要地阻塞asynchronous
调用的线程,因为你的执行者是固定大小的线程。在这种情况下,系统的总吞吐量会更少。
例如:
如果有10个客户端调用synchronous
和asynchronous
调用中的每一个,则在您的实现中,只有线程实际上正在工作。但是,如果您还要使用客户端线程而不是synchronous
调用asynchronous
并等待,那么所有20个调用将同时处理。
答案 1 :(得分:2)
如果您正在创建新线程,即使在同步操作的情况下(实际上不需要),也会导致性能下降。你基本上是创建新的线程(读作浪费资源),甚至没有任何好处。 话虽如此,我认为更好的方法是将HTTP部分包装在不同的类中。这样,您就可以在同步和异步情况下重复使用HTTP访问代码。
class HTTPAccess{
private RestTemplate restTemplate;
private DataKey key;
public HTTPAccess(DataKey key,RestTemplate restTemplate){
this.key = key;
this.restTemplate = restTemplate;
}
public DataResponse performRequest() {
DataResponse dataResponse = null;
try {
String url = createURL();
response = restTemplate.getForObject(url, String.class);
// it is a successful response
dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS);
} catch (RestClientException ex) {
PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, key);
dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
// create a URL by using key object
private String createURL() {
String url = somecode;
return url;
}
}
现在,对于客户端实现,只需使用此类。
public class DataClient implements Client {
private ExecutorService executor = Executors.newFixedThreadPool(10);
private RestTemplate restTemplate;
private void initRestClient(DataKey key){
if(restTemplate == null)
restTemplate = new RestTemplate(clientHttpRequestFactory(key));
}
private ClientHttpRequestFactory clientHttpRequestFactory(DataKey key) {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(key.getTimeout());
factory.setConnectTimeout(key.getTimeout());
//if you need to set otherparams this is the place we can do it extracting from DataKey obj
return factory;
}
// for synchronous call
@Override
public DataResponse executeSynchronous(DataKey key) {
initRestClient(key);
DataResponse dataResponse = new HTTPAccess(key).performRequest();
return dataResponse;
}
//for asynchronous call
@Override
public Future<DataResponse> executeAsynchronous(final DataKey key) {
return executor.submit(new Callable<DataResponse>() {
@Override
public DataResponse call() throws Exception {
return executeSynchronous(key);
}
});
}
}
这样,您的HTTP实现是完全独立的,将来如果您需要更改接收 DataResponse 的方式(可能来自数据库调用),那么您必须仅更改HTTPAccess类和其他部分不会受到影响。
答案 2 :(得分:1)
我认为这更好:
@Override
public DataResponse executeSynchronous(DataKey key) {
Task task = new Task(key, restTemplate);
return task.call();
}
它执行相同的工作,清晰,更短,并且没有开销。
请注意,他还会清除您当前拥有的重复异常处理。
如果超时是必须,则选项是使用RestTemplate
类的基础超时,如Spring RestTemplate timeout
然后超时将导致您或库客户端可以处理的RestClientException
。
答案 3 :(得分:1)
我不打扰那个Task类。只需让你的同步方法完成所有工作,并从异步方法中异步调用它。
public class DataClient implements Client {
private RestTemplate restTemplate = new RestTemplate();
private ExecutorService executor = Executors.newFixedThreadPool(10);
// for synchronous call
@Override
public DataResponse executeSynchronous(DataKey key) {
DataResponse dataResponse = null;
String response = null;
try {
String url = createURL();
response = restTemplate.getForObject(url, String.class);
// it is a successful response
dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS);
} catch (RestClientException ex) {
PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, key);
dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
//for asynchronous call
@Override
public Future<DataResponse> executeAsynchronous(final DataKey key) {
return executor.submit(new Callable<DataResponse>() {
@Override
public DataResponse call() throws Exception {
return executeSynchronous(key);
}
});
}
}
答案 4 :(得分:1)
上述通过异步执行同步任务的代码与将所有内容都设置为异步相同。如果这是要求,那么我建议你使用google guava的ListenableFuture。我不是一个拥护者,但它有管理任务超时的方法,编写好的回调来处理onSuccess,onFailure场景。 https://code.google.com/p/guava-libraries/wiki/ListenableFutureExplained