Search in sources :

Example 1 with RemotePromise

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);
}
Also used : RBlockingQueue(org.redisson.api.RBlockingQueue) RemoteServiceAck(org.redisson.remote.RemoteServiceAck) FutureListener(io.netty.util.concurrent.FutureListener) Method(java.lang.reflect.Method) RFuture(org.redisson.api.RFuture) InvocationHandler(java.lang.reflect.InvocationHandler) RemoteServiceTimeoutException(org.redisson.remote.RemoteServiceTimeoutException) IOException(java.io.IOException) RemoteServiceAckTimeoutException(org.redisson.remote.RemoteServiceAckTimeoutException) RemoteInvocationOptions(org.redisson.api.RemoteInvocationOptions) RemoteServiceRequest(org.redisson.remote.RemoteServiceRequest) RFuture(org.redisson.api.RFuture) Future(io.netty.util.concurrent.Future) RemoteServiceAckTimeoutException(org.redisson.remote.RemoteServiceAckTimeoutException) RemotePromise(org.redisson.executor.RemotePromise)

Example 2 with RemotePromise

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);
}
Also used : RedisException(org.redisson.client.RedisException) Method(java.lang.reflect.Method) InvocationHandler(java.lang.reflect.InvocationHandler) RedisException(org.redisson.client.RedisException) RemoteInvocationOptions(org.redisson.api.RemoteInvocationOptions) RemotePromise(org.redisson.executor.RemotePromise)

Example 3 with RemotePromise

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;
}
Also used : RPromise(org.redisson.misc.RPromise) Timeout(io.netty.util.Timeout) CommandAsyncExecutor(org.redisson.command.CommandAsyncExecutor) java.util(java.util) Codec(org.redisson.client.codec.Codec) RemoteServiceRequest(org.redisson.remote.RemoteServiceRequest) java.util.concurrent(java.util.concurrent) RedissonPromise(org.redisson.misc.RedissonPromise) ObjectListReplayDecoder(org.redisson.client.protocol.decoder.ObjectListReplayDecoder) RedissonListIterator(org.redisson.iterator.RedissonListIterator) RRemoteService(org.redisson.api.RRemoteService) RRemoteAsync(org.redisson.api.annotation.RRemoteAsync) Convertor(org.redisson.client.protocol.convertor.Convertor) ListDrainToDecoder(org.redisson.connection.decoder.ListDrainToDecoder) RemoteInvocationOptions(org.redisson.api.RemoteInvocationOptions) RFuture(org.redisson.api.RFuture) Consumer(java.util.function.Consumer) CompletableFutureWrapper(org.redisson.misc.CompletableFutureWrapper) RemotePromise(org.redisson.executor.RemotePromise) ByteBuf(io.netty.buffer.ByteBuf) ImmediateEventExecutor(io.netty.util.concurrent.ImmediateEventExecutor) RedisStrictCommand(org.redisson.client.protocol.RedisStrictCommand) RedisCommand(org.redisson.client.protocol.RedisCommand) RTransferQueue(org.redisson.api.RTransferQueue) RedissonPromise(org.redisson.misc.RedissonPromise) Timeout(io.netty.util.Timeout) RemotePromise(org.redisson.executor.RemotePromise)

Aggregations

RemoteInvocationOptions (org.redisson.api.RemoteInvocationOptions)3 RemotePromise (org.redisson.executor.RemotePromise)3 InvocationHandler (java.lang.reflect.InvocationHandler)2 Method (java.lang.reflect.Method)2 RFuture (org.redisson.api.RFuture)2 RemoteServiceRequest (org.redisson.remote.RemoteServiceRequest)2 ByteBuf (io.netty.buffer.ByteBuf)1 Timeout (io.netty.util.Timeout)1 Future (io.netty.util.concurrent.Future)1 FutureListener (io.netty.util.concurrent.FutureListener)1 ImmediateEventExecutor (io.netty.util.concurrent.ImmediateEventExecutor)1 IOException (java.io.IOException)1 java.util (java.util)1 java.util.concurrent (java.util.concurrent)1 Consumer (java.util.function.Consumer)1 RBlockingQueue (org.redisson.api.RBlockingQueue)1 RRemoteService (org.redisson.api.RRemoteService)1 RTransferQueue (org.redisson.api.RTransferQueue)1 RRemoteAsync (org.redisson.api.annotation.RRemoteAsync)1 RedisException (org.redisson.client.RedisException)1