use of org.redisson.executor.RemotePromise in project redisson by redisson.
the class BaseRemoteService method async.
private <T> T async(final Class<T> remoteInterface, final RemoteInvocationOptions options, final Class<?> syncInterface) {
// local copy of the options, to prevent mutation
final RemoteInvocationOptions optionsCopy = new RemoteInvocationOptions(options);
final String toString = getClass().getSimpleName() + "-" + remoteInterface.getSimpleName() + "-proxy-" + generateRequestId();
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("toString")) {
return toString;
} else if (method.getName().equals("equals")) {
return proxy == args[0];
} else if (method.getName().equals("hashCode")) {
return toString.hashCode();
}
if (!optionsCopy.isResultExpected() && !(method.getReturnType().equals(Void.class) || method.getReturnType().equals(Void.TYPE) || method.getReturnType().equals(RFuture.class))) {
throw new IllegalArgumentException("The noResult option only supports void return value");
}
final String requestId = generateRequestId();
final String requestQueueName = getRequestQueueName(syncInterface);
final String responseName = getResponseQueueName(syncInterface, requestId);
final String ackName = getAckName(syncInterface, requestId);
final RBlockingQueue<RemoteServiceRequest> requestQueue = redisson.getBlockingQueue(requestQueueName, getCodec());
final RemoteServiceRequest request = new RemoteServiceRequest(requestId, method.getName(), getMethodSignatures(method), args, optionsCopy, System.currentTimeMillis());
final RemotePromise<Object> result = new RemotePromise<Object>(commandExecutor.getConnectionManager().newPromise()) {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (isCancelled()) {
return true;
}
if (isDone()) {
return false;
}
if (optionsCopy.isAckExpected()) {
RFuture<Boolean> future = commandExecutor.evalWriteAsync(responseName, LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "if redis.call('setnx', KEYS[1], 1) == 1 then " + "redis.call('pexpire', KEYS[1], ARGV[2]);" + "redis.call('lrem', KEYS[3], 1, ARGV[1]);" + "redis.call('pexpire', KEYS[2], ARGV[2]);" + "return 1;" + "end;" + "return 0;", Arrays.<Object>asList(ackName, responseName, requestQueueName), encode(request), request.getOptions().getAckTimeoutInMillis());
boolean ackNotSent = commandExecutor.get(future);
if (ackNotSent) {
super.cancel(mayInterruptIfRunning);
return true;
}
return cancel(syncInterface, requestId, request, mayInterruptIfRunning);
}
boolean removed = remove(requestQueue, request);
if (removed) {
super.cancel(mayInterruptIfRunning);
return true;
}
return cancel(syncInterface, requestId, request, mayInterruptIfRunning);
}
private boolean cancel(Class<?> remoteInterface, String requestId, RemoteServiceRequest request, boolean mayInterruptIfRunning) {
if (isCancelled()) {
return true;
}
if (isDone()) {
return false;
}
String canceRequestName = getCancelRequestQueueName(remoteInterface, requestId);
cancelExecution(optionsCopy, responseName, request, mayInterruptIfRunning, canceRequestName, this);
awaitUninterruptibly(60, TimeUnit.SECONDS);
return isCancelled();
}
};
result.setRequestId(requestId);
RFuture<Boolean> addFuture = addAsync(requestQueue, request, result);
addFuture.addListener(new FutureListener<Boolean>() {
@Override
public void operationComplete(Future<Boolean> future) throws Exception {
if (!future.isSuccess()) {
result.tryFailure(future.cause());
return;
}
if (optionsCopy.isAckExpected()) {
final RBlockingQueue<RemoteServiceAck> responseQueue = redisson.getBlockingQueue(responseName, getCodec());
RFuture<RemoteServiceAck> ackFuture = responseQueue.pollAsync(optionsCopy.getAckTimeoutInMillis(), TimeUnit.MILLISECONDS);
ackFuture.addListener(new FutureListener<RemoteServiceAck>() {
@Override
public void operationComplete(Future<RemoteServiceAck> future) throws Exception {
if (!future.isSuccess()) {
result.tryFailure(future.cause());
return;
}
RemoteServiceAck ack = future.getNow();
if (ack == null) {
RFuture<RemoteServiceAck> ackFutureAttempt = tryPollAckAgainAsync(optionsCopy, responseQueue, ackName);
ackFutureAttempt.addListener(new FutureListener<RemoteServiceAck>() {
@Override
public void operationComplete(Future<RemoteServiceAck> future) throws Exception {
if (!future.isSuccess()) {
result.tryFailure(future.cause());
return;
}
if (future.getNow() == null) {
Exception ex = new RemoteServiceAckTimeoutException("No ACK response after " + optionsCopy.getAckTimeoutInMillis() + "ms for request: " + request);
result.tryFailure(ex);
return;
}
awaitResultAsync(optionsCopy, result, request, responseName, ackName);
}
});
} else {
awaitResultAsync(optionsCopy, result, request, responseName);
}
}
});
} else {
awaitResultAsync(optionsCopy, result, request, responseName);
}
}
});
return result;
}
};
return (T) Proxy.newProxyInstance(remoteInterface.getClassLoader(), new Class[] { remoteInterface }, handler);
}
use of org.redisson.executor.RemotePromise in project redisson by redisson.
the class SyncRemoteProxy method create.
public <T> T create(Class<T> remoteInterface, RemoteInvocationOptions options) {
// local copy of the options, to prevent mutation
RemoteInvocationOptions optionsCopy = new RemoteInvocationOptions(options);
String toString = getClass().getSimpleName() + "-" + remoteInterface.getSimpleName() + "-proxy-" + remoteService.generateRequestId();
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("toString")) {
return toString;
} else if (method.getName().equals("equals")) {
return proxy == args[0];
} else if (method.getName().equals("hashCode")) {
return toString.hashCode();
}
if (!optionsCopy.isResultExpected() && !(method.getReturnType().equals(Void.class) || method.getReturnType().equals(Void.TYPE)))
throw new IllegalArgumentException("The noResult option only supports void return value");
RequestId requestId = remoteService.generateRequestId();
String requestQueueName = getRequestQueueName(remoteInterface);
RemoteServiceRequest request = new RemoteServiceRequest(executorId, requestId.toString(), method.getName(), remoteService.getMethodSignature(method), args, optionsCopy, System.currentTimeMillis());
CompletableFuture<RemoteServiceAck> ackFuture;
if (optionsCopy.isAckExpected()) {
ackFuture = pollResponse(optionsCopy.getAckTimeoutInMillis(), requestId, false);
} else {
ackFuture = null;
}
CompletableFuture<RRemoteServiceResponse> responseFuture;
if (optionsCopy.isResultExpected()) {
long timeout = remoteService.getTimeout(optionsCopy.getExecutionTimeoutInMillis(), request);
responseFuture = pollResponse(timeout, requestId, false);
} else {
responseFuture = null;
}
RemotePromise<Object> addPromise = new RemotePromise<Object>(requestId);
CompletableFuture<Boolean> futureAdd = remoteService.addAsync(requestQueueName, request, addPromise);
Boolean res;
try {
res = futureAdd.join();
} catch (Exception e) {
if (responseFuture != null) {
responseFuture.cancel(false);
}
if (ackFuture != null) {
ackFuture.cancel(false);
}
throw e.getCause();
}
if (!res) {
if (responseFuture != null) {
responseFuture.cancel(false);
}
if (ackFuture != null) {
ackFuture.cancel(false);
}
throw new RedisException("Task hasn't been added");
}
// poll for the ack only if expected
if (ackFuture != null) {
String ackName = remoteService.getAckName(requestId);
RemoteServiceAck ack = null;
try {
ack = ackFuture.toCompletableFuture().get(optionsCopy.getAckTimeoutInMillis(), TimeUnit.MILLISECONDS);
} catch (ExecutionException | TimeoutException e) {
// skip
}
if (ack == null) {
CompletionStage<RemoteServiceAck> ackFutureAttempt = tryPollAckAgainAsync(optionsCopy, ackName, requestId);
try {
ack = ackFutureAttempt.toCompletableFuture().get(optionsCopy.getAckTimeoutInMillis(), TimeUnit.MILLISECONDS);
} catch (ExecutionException | TimeoutException e) {
// skip
}
if (ack == null) {
throw new RemoteServiceAckTimeoutException("No ACK response after " + optionsCopy.getAckTimeoutInMillis() + "ms for request: " + request);
}
}
new RedissonBucket<>(commandExecutor, ackName).delete();
}
// poll for the response only if expected
if (responseFuture != null) {
RemoteServiceResponse response = null;
try {
response = (RemoteServiceResponse) responseFuture.toCompletableFuture().join();
} catch (Exception e) {
// skip
}
if (response == null) {
throw new RemoteServiceTimeoutException("No response after " + optionsCopy.getExecutionTimeoutInMillis() + "ms for request: " + request);
}
if (response.getError() != null) {
throw response.getError();
}
if (method.getReturnType().equals(Optional.class)) {
if (response.getResult() == null) {
return Optional.empty();
}
return Optional.of(response.getResult());
}
return response.getResult();
}
return null;
}
};
return (T) Proxy.newProxyInstance(remoteInterface.getClassLoader(), new Class[] { remoteInterface }, handler);
}
use of org.redisson.executor.RemotePromise in project redisson by redisson.
the class RedissonTransferQueue method tryTransferAsync.
public RFuture<Boolean> tryTransferAsync(V v, long timeout, TimeUnit unit) {
RPromise<Boolean> result = new RedissonPromise<>();
result.setUncancellable();
RemotePromise<Void> future = (RemotePromise<Void>) service.invoke(v).toCompletableFuture();
long remainTime = unit.toMillis(timeout);
long startTime = System.currentTimeMillis();
Timeout timeoutFuture = commandExecutor.getConnectionManager().newTimeout(tt -> {
if (!future.getAddFuture().cancel(false)) {
future.cancelAsync(false);
}
}, remainTime, TimeUnit.MILLISECONDS);
future.whenComplete((res, exc) -> {
if (future.isCancelled()) {
result.trySuccess(false);
return;
}
timeoutFuture.cancel();
if (exc != null) {
result.tryFailure(exc);
return;
}
result.trySuccess(true);
});
future.getAddFuture().whenComplete((added, e) -> {
if (future.getAddFuture().isCancelled()) {
result.trySuccess(false);
return;
}
if (e != null) {
timeoutFuture.cancel();
result.tryFailure(e);
return;
}
if (!added) {
timeoutFuture.cancel();
result.trySuccess(false);
return;
}
Runnable task = () -> {
future.cancelAsync(false).whenComplete((canceled, ex) -> {
if (ex != null) {
timeoutFuture.cancel();
result.tryFailure(ex);
return;
}
if (canceled) {
timeoutFuture.cancel();
result.trySuccess(false);
}
});
};
long time = remainTime - (System.currentTimeMillis() - startTime);
if (time > 0) {
commandExecutor.getConnectionManager().newTimeout(tt -> {
task.run();
}, time, TimeUnit.MILLISECONDS);
} else {
task.run();
}
});
return result;
}
Aggregations