Search in sources :

Example 1 with RemoteServiceAck

use of org.redisson.remote.RemoteServiceAck 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 RemoteServiceAck

use of org.redisson.remote.RemoteServiceAck in project redisson by redisson.

the class RedissonRemoteService method subscribe.

private <T> void subscribe(final Class<T> remoteInterface, final RBlockingQueue<RemoteServiceRequest> requestQueue, final ExecutorService executor) {
    RFuture<RemoteServiceRequest> take = requestQueue.takeAsync();
    take.addListener(new FutureListener<RemoteServiceRequest>() {

        @Override
        public void operationComplete(Future<RemoteServiceRequest> future) throws Exception {
            if (!future.isSuccess()) {
                if (future.cause() instanceof RedissonShutdownException) {
                    return;
                }
                log.error("Can't process the remote service request.", future.cause());
                // re-subscribe after a failed takeAsync
                subscribe(remoteInterface, requestQueue, executor);
                return;
            }
            // do not subscribe now, see
            // https://github.com/mrniko/redisson/issues/493
            // subscribe(remoteInterface, requestQueue);
            final RemoteServiceRequest request = future.getNow();
            // check the ack only if expected
            if (request.getOptions().isAckExpected() && System.currentTimeMillis() - request.getDate() > request.getOptions().getAckTimeoutInMillis()) {
                log.debug("request: {} has been skipped due to ackTimeout");
                // re-subscribe after a skipped ackTimeout
                subscribe(remoteInterface, requestQueue, executor);
                return;
            }
            final String responseName = getResponseQueueName(remoteInterface, request.getRequestId());
            // send the ack only if expected
            if (request.getOptions().isAckExpected()) {
                String ackName = getAckName(remoteInterface, request.getRequestId());
                RFuture<Boolean> ackClientsFuture = 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('rpush', KEYS[2], ARGV[1]);" + "redis.call('pexpire', KEYS[2], ARGV[2]);" + "return 1;" + "end;" + "return 0;", Arrays.<Object>asList(ackName, responseName), encode(new RemoteServiceAck()), request.getOptions().getAckTimeoutInMillis());
                ackClientsFuture.addListener(new FutureListener<Boolean>() {

                    @Override
                    public void operationComplete(Future<Boolean> future) throws Exception {
                        if (!future.isSuccess()) {
                            log.error("Can't send ack for request: " + request, future.cause());
                            if (future.cause() instanceof RedissonShutdownException) {
                                return;
                            }
                            // re-subscribe after a failed send (ack)
                            subscribe(remoteInterface, requestQueue, executor);
                            return;
                        }
                        if (!future.getNow()) {
                            subscribe(remoteInterface, requestQueue, executor);
                            return;
                        }
                        executeMethod(remoteInterface, requestQueue, executor, request);
                    }
                });
            } else {
                executeMethod(remoteInterface, requestQueue, executor, request);
            }
        }
    });
}
Also used : FutureListener(io.netty.util.concurrent.FutureListener) RFuture(org.redisson.api.RFuture) RemoteServiceRequest(org.redisson.remote.RemoteServiceRequest) RFuture(org.redisson.api.RFuture) Future(io.netty.util.concurrent.Future) RemoteServiceAck(org.redisson.remote.RemoteServiceAck)

Example 3 with RemoteServiceAck

use of org.redisson.remote.RemoteServiceAck in project redisson by redisson.

the class BaseRemoteService method sync.

private <T> T sync(final Class<T> remoteInterface, final RemoteInvocationOptions options) {
    // 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)))
                throw new IllegalArgumentException("The noResult option only supports void return value");
            String requestId = generateRequestId();
            String requestQueueName = getRequestQueueName(remoteInterface);
            RBlockingQueue<RemoteServiceRequest> requestQueue = redisson.getBlockingQueue(requestQueueName, getCodec());
            RemoteServiceRequest request = new RemoteServiceRequest(requestId, method.getName(), getMethodSignatures(method), args, optionsCopy, System.currentTimeMillis());
            requestQueue.add(request);
            RBlockingQueue<RRemoteServiceResponse> responseQueue = null;
            if (optionsCopy.isAckExpected() || optionsCopy.isResultExpected()) {
                String responseName = getResponseQueueName(remoteInterface, requestId);
                responseQueue = redisson.getBlockingQueue(responseName, getCodec());
            }
            // poll for the ack only if expected
            if (optionsCopy.isAckExpected()) {
                String ackName = getAckName(remoteInterface, requestId);
                RemoteServiceAck ack = (RemoteServiceAck) responseQueue.poll(optionsCopy.getAckTimeoutInMillis(), TimeUnit.MILLISECONDS);
                if (ack == null) {
                    ack = tryPollAckAgain(optionsCopy, responseQueue, ackName);
                    if (ack == null) {
                        throw new RemoteServiceAckTimeoutException("No ACK response after " + optionsCopy.getAckTimeoutInMillis() + "ms for request: " + request);
                    }
                }
                redisson.getBucket(ackName).delete();
            }
            // poll for the response only if expected
            if (optionsCopy.isResultExpected()) {
                RemoteServiceResponse response = (RemoteServiceResponse) responseQueue.poll(optionsCopy.getExecutionTimeoutInMillis(), TimeUnit.MILLISECONDS);
                if (response == null) {
                    throw new RemoteServiceTimeoutException("No response1 after " + optionsCopy.getExecutionTimeoutInMillis() + "ms for request: " + request);
                }
                if (response.getError() != null) {
                    throw response.getError();
                }
                return response.getResult();
            }
            return null;
        }
    };
    return (T) Proxy.newProxyInstance(remoteInterface.getClassLoader(), new Class[] { remoteInterface }, handler);
}
Also used : RemoteServiceTimeoutException(org.redisson.remote.RemoteServiceTimeoutException) Method(java.lang.reflect.Method) InvocationHandler(java.lang.reflect.InvocationHandler) RemoteInvocationOptions(org.redisson.api.RemoteInvocationOptions) RemoteServiceRequest(org.redisson.remote.RemoteServiceRequest) RRemoteServiceResponse(org.redisson.remote.RRemoteServiceResponse) RemoteServiceAckTimeoutException(org.redisson.remote.RemoteServiceAckTimeoutException) RemoteServiceResponse(org.redisson.remote.RemoteServiceResponse) RRemoteServiceResponse(org.redisson.remote.RRemoteServiceResponse) RemoteServiceAck(org.redisson.remote.RemoteServiceAck)

Aggregations

RemoteServiceAck (org.redisson.remote.RemoteServiceAck)3 RemoteServiceRequest (org.redisson.remote.RemoteServiceRequest)3 Future (io.netty.util.concurrent.Future)2 FutureListener (io.netty.util.concurrent.FutureListener)2 InvocationHandler (java.lang.reflect.InvocationHandler)2 Method (java.lang.reflect.Method)2 RFuture (org.redisson.api.RFuture)2 RemoteInvocationOptions (org.redisson.api.RemoteInvocationOptions)2 RemoteServiceAckTimeoutException (org.redisson.remote.RemoteServiceAckTimeoutException)2 RemoteServiceTimeoutException (org.redisson.remote.RemoteServiceTimeoutException)2 IOException (java.io.IOException)1 RBlockingQueue (org.redisson.api.RBlockingQueue)1 RemotePromise (org.redisson.executor.RemotePromise)1 RRemoteServiceResponse (org.redisson.remote.RRemoteServiceResponse)1 RemoteServiceResponse (org.redisson.remote.RemoteServiceResponse)1