@ -23,7 +23,7 @@ import java.lang.reflect.Proxy;
import java.util.Arrays ;
import java.util.List ;
import java.util.Map ;
import java.util.concurrent.Executor ;
import java.util.concurrent.Executor Service ;
import java.util.concurrent.TimeUnit ;
import java.util.concurrent.atomic.AtomicReference ;
@ -31,17 +31,18 @@ import org.redisson.api.RBatch;
import org.redisson.api.RBlockingQueue ;
import org.redisson.api.RBlockingQueueAsync ;
import org.redisson.api.RRemoteService ;
import org.redisson.api.RScript ;
import org.redisson.api.RScript.Mode ;
import org.redisson.api.RemoteInvocationOptions ;
import org.redisson.client.codec.Codec ;
import org.redisson.client.codec.LongCodec ;
import org.redisson.client.protocol.RedisCommands ;
import org.redisson.command.CommandExecutor ;
import org.redisson.executor.RemotePromise ;
import org.redisson.remote.RRemoteAsync ;
import org.redisson.remote.RRemoteServiceResponse ;
import org.redisson.remote.RemoteServiceAck ;
import org.redisson.remote.RemoteServiceAckTimeoutException ;
import org.redisson.remote.RemoteServiceCancelRequest ;
import org.redisson.remote.RemoteServiceCancelResponse ;
import org.redisson.remote.RemoteServiceKey ;
import org.redisson.remote.RemoteServiceMethod ;
import org.redisson.remote.RemoteServiceRequest ;
@ -65,25 +66,25 @@ import io.netty.util.internal.ThreadLocalRandom;
* /
public class RedissonRemoteService implements RRemoteService {
private static final Logger log = LoggerFactory . getLogger ( RedissonRemoteService . class ) ;
private static final Logger log = LoggerFactory . getLogger ( RedissonRemoteService . class ) ;
private final Map < RemoteServiceKey , RemoteServiceMethod > beans = PlatformDependent . newConcurrentHashMap ( ) ;
protected final Codec codec ;
protected final Redisson redisson ;
protected final String name ;
protected final CommandExecutor commandExecutor ;
public RedissonRemoteService ( Redisson redisson , CommandExecutor commandExecutor ) {
this ( redisson , "redisson_r emote_ service ", commandExecutor ) ;
this ( redisson , "redisson_r s", commandExecutor ) ;
}
public RedissonRemoteService ( Redisson redisson , String name , CommandExecutor commandExecutor ) {
this ( null , redisson , name , commandExecutor ) ;
}
public RedissonRemoteService ( Codec codec , Redisson redisson , CommandExecutor commandExecutor ) {
this ( codec , redisson , "redisson_r emote_ service ", commandExecutor ) ;
this ( codec , redisson , "redisson_r s", commandExecutor ) ;
}
public RedissonRemoteService ( Codec codec , Redisson redisson , String name , CommandExecutor commandExecutor ) {
@ -97,13 +98,14 @@ public class RedissonRemoteService implements RRemoteService {
public < T > void register ( Class < T > remoteInterface , T object ) {
register ( remoteInterface , object , 1 ) ;
}
@Override
public < T > void register ( Class < T > remoteInterface , T object , int workersAmount ) {
register ( remoteInterface , object , workersAmount , null ) ;
}
public < T > void register ( Class < T > remoteInterface , T object , int workersAmount , Executor executor ) {
@Override
public < T > void register ( Class < T > remoteInterface , T object , int workersAmount , ExecutorService executor ) {
if ( workersAmount < 1 ) {
throw new IllegalArgumentException ( "executorsAmount can't be lower than 1" ) ;
}
@ -114,7 +116,7 @@ public class RedissonRemoteService implements RRemoteService {
return ;
}
}
for ( int i = 0 ; i < workersAmount ; i + + ) {
String requestQueueName = getRequestQueueName ( remoteInterface ) ;
RBlockingQueue < RemoteServiceRequest > requestQueue = redisson . getBlockingQueue ( requestQueueName , getCodec ( ) ) ;
@ -122,6 +124,10 @@ public class RedissonRemoteService implements RRemoteService {
}
}
private String getCancelRequestQueueName ( Class < ? > remoteInterface , String requestId ) {
return "{" + name + ":" + remoteInterface . getName ( ) + "}:" + requestId + ":cancel" ;
}
private String getAckName ( Class < ? > remoteInterface , String requestId ) {
return "{" + name + ":" + remoteInterface . getName ( ) + "}:" + requestId + ":ack" ;
}
@ -133,7 +139,7 @@ public class RedissonRemoteService implements RRemoteService {
private String getRequestQueueName ( Class < ? > remoteInterface ) {
return "{" + name + ":" + remoteInterface . getName ( ) + "}" ;
}
private Codec getCodec ( ) {
if ( codec ! = null ) {
return codec ;
@ -149,7 +155,8 @@ public class RedissonRemoteService implements RRemoteService {
}
}
private < T > void subscribe ( final Class < T > remoteInterface , final RBlockingQueue < RemoteServiceRequest > requestQueue , final Executor executor ) {
private < T > void subscribe ( final Class < T > remoteInterface , final RBlockingQueue < RemoteServiceRequest > requestQueue ,
final ExecutorService executor ) {
Future < RemoteServiceRequest > take = requestQueue . takeAsync ( ) ;
take . addListener ( new FutureListener < RemoteServiceRequest > ( ) {
@Override
@ -163,35 +170,39 @@ public class RedissonRemoteService implements RRemoteService {
return ;
}
// do not subscribe now, see https://github.com/mrniko/redisson/issues/493
// 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 ( ) ) {
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 RemoteServiceMethod method = beans . get ( new RemoteServiceKey ( remoteInterface , request . getMethodName ( ) ) ) ;
final RemoteServiceMethod method = beans
. get ( new RemoteServiceKey ( remoteInterface , request . getMethodName ( ) ) ) ;
final String responseName = getResponseQueueName ( remoteInterface , request . getRequestId ( ) ) ;
// send the ack only if expected
if ( request . getOptions ( ) . isAckExpected ( ) ) {
String ackName = getAckName ( remoteInterface , request . getRequestId ( ) ) ;
Future < Boolean > ackClientsFuture = redisson . getScript ( ) . evalAsync ( responseName , Mode . READ_WRITE , LongCodec . INSTANCE ,
"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;" , RScript . ReturnType . BOOLEAN , Arrays . < Object > asList ( ackName , responseName ) ,
encode ( new RemoteServiceAck ( ) ) ,
request . getOptions ( ) . getAckTimeoutInMillis ( ) ) ;
Future < 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 {
@ -210,61 +221,90 @@ public class RedissonRemoteService implements RRemoteService {
return ;
}
if ( executor ! = null ) {
executor . execute ( new Runnable ( ) {
@Override
public void run ( ) {
invokeMethod ( remoteInterface , requestQueue , request , method , responseName , executor ) ;
}
} ) ;
} else {
invokeMethod ( remoteInterface , requestQueue , request , method , responseName , executor ) ;
}
runMethod ( remoteInterface , requestQueue , executor , request , method , responseName ) ;
}
} ) ;
} else {
if ( executor ! = null ) {
executor . execute ( new Runnable ( ) {
@Override
public void run ( ) {
invokeMethod ( remoteInterface , requestQueue , request , method , responseName , executor ) ;
}
} ) ;
} else {
invokeMethod ( remoteInterface , requestQueue , request , method , responseName , executor ) ;
}
runMethod ( remoteInterface , requestQueue , executor , request , method , responseName ) ;
}
}
} ) ;
}
private < T > void invokeMethod ( final Class < T > remoteInterface , final RBlockingQueue < RemoteServiceRequest > requestQueue ,
final RemoteServiceRequest request , RemoteServiceMethod method , String responseName , final Executor executor ) {
final AtomicReference < RemoteServiceResponse > responseHolder = new AtomicReference < RemoteServiceResponse > ( ) ;
private < T > void runMethod ( final Class < T > remoteInterface , final RBlockingQueue < RemoteServiceRequest > requestQueue ,
final ExecutorService executor , final RemoteServiceRequest request , final RemoteServiceMethod method ,
final String responseName ) {
if ( executor ! = null ) {
RBlockingQueue < RemoteServiceCancelRequest > cancelRequestQueue =
redisson . getBlockingQueue ( getCancelRequestQueueName ( remoteInterface , request . getRequestId ( ) ) , getCodec ( ) ) ;
final Future < RemoteServiceCancelRequest > cancelRequestFuture = cancelRequestQueue . takeAsync ( ) ;
final AtomicReference < RRemoteServiceResponse > responseHolder = new AtomicReference < RRemoteServiceResponse > ( ) ;
final java . util . concurrent . Future < ? > submitFuture = executor . submit ( new Runnable ( ) {
@Override
public void run ( ) {
invokeMethod ( remoteInterface , requestQueue , request , method , responseName , executor ,
cancelRequestFuture , responseHolder ) ;
}
} ) ;
cancelRequestFuture . addListener ( new FutureListener < RemoteServiceCancelRequest > ( ) {
@Override
public void operationComplete ( Future < RemoteServiceCancelRequest > future ) throws Exception {
if ( ! future . isSuccess ( ) ) {
return ;
}
boolean res = submitFuture . cancel ( future . getNow ( ) . isMayInterruptIfRunning ( ) ) ;
if ( res ) {
responseHolder . compareAndSet ( null , new RemoteServiceCancelResponse ( ) ) ;
}
}
} ) ;
} else {
final AtomicReference < RRemoteServiceResponse > responseHolder = new AtomicReference < RRemoteServiceResponse > ( ) ;
invokeMethod ( remoteInterface , requestQueue , request , method , responseName , executor , null , responseHolder ) ;
}
}
private < T > void invokeMethod ( final Class < T > remoteInterface ,
final RBlockingQueue < RemoteServiceRequest > requestQueue , final RemoteServiceRequest request ,
RemoteServiceMethod method , String responseName , final ExecutorService executor ,
Future < RemoteServiceCancelRequest > cancelRequestFuture , AtomicReference < RRemoteServiceResponse > responseHolder ) {
try {
Object result = method . getMethod ( ) . invoke ( method . getBean ( ) , request . getArgs ( ) ) ;
RemoteServiceResponse response = new RemoteServiceResponse ( result ) ;
responseHolder . set ( response ) ;
responseHolder . compareAndSet( null , response ) ;
} catch ( Exception e ) {
RemoteServiceResponse response = new RemoteServiceResponse ( e . getCause ( ) ) ;
responseHolder . set ( response ) ;
responseHolder . compareAndSet( null , response ) ;
log . error ( "Can't execute: " + request , e ) ;
}
if ( cancelRequestFuture ! = null ) {
cancelRequestFuture . cancel ( false ) ;
}
// send the response only if expected
if ( request . getOptions ( ) . isResultExpected ( ) ) {
Future < List < ? > > clientsFuture = send ( request . getOptions ( ) . getExecutionTimeoutInMillis ( ) , responseName , responseHolder . get ( ) ) ;
Future < List < ? > > clientsFuture = send ( request . getOptions ( ) . getExecutionTimeoutInMillis ( ) , responseName ,
responseHolder . get ( ) ) ;
clientsFuture . addListener ( new FutureListener < List < ? > > ( ) {
@Override
public void operationComplete ( Future < List < ? > > future ) throws Exception {
if ( ! future . isSuccess ( ) ) {
log . error ( "Can't send response: " + responseHolder . get ( ) + " for request: " + request , future . cause ( ) ) ;
log . error ( "Can't send response: " + responseHolder . get ( ) + " for request: " + request ,
future . cause ( ) ) ;
if ( future . cause ( ) instanceof RedissonShutdownException ) {
return ;
}
}
// re-subscribe anyways (fail or success) after the send (response)
// re-subscribe anyways (fail or success) after the send
// (response)
subscribe ( remoteInterface , requestQueue , executor ) ;
}
} ) ;
@ -281,15 +321,14 @@ public class RedissonRemoteService implements RRemoteService {
@Override
public < T > T get ( Class < T > remoteInterface , long executionTimeout , TimeUnit executionTimeUnit ) {
return get ( remoteInterface , RemoteInvocationOptions . defaults ( )
. expectResultWithin ( executionTimeout , executionTimeUnit ) ) ;
return get ( remoteInterface ,
RemoteInvocationOptions . defaults ( ) . expectResultWithin ( executionTimeout , executionTimeUnit ) ) ;
}
@Override
public < T > T get ( Class < T > remoteInterface , long executionTimeout , TimeUnit executionTimeUnit ,
long ackTimeout , TimeUnit ackTimeUnit ) {
return get ( remoteInterface , RemoteInvocationOptions . defaults ( )
. expectAckWithin ( ackTimeout , ackTimeUnit )
public < T > T get ( Class < T > remoteInterface , long executionTimeout , TimeUnit executionTimeUnit , long ackTimeout ,
TimeUnit ackTimeUnit ) {
return get ( remoteInterface , RemoteInvocationOptions . defaults ( ) . expectAckWithin ( ackTimeout , ackTimeUnit )
. expectResultWithin ( executionTimeout , executionTimeUnit ) ) ;
}
@ -297,31 +336,34 @@ public class RedissonRemoteService implements RRemoteService {
public < T > T get ( Class < T > remoteInterface , RemoteInvocationOptions options ) {
for ( Annotation annotation : remoteInterface . getAnnotations ( ) ) {
if ( annotation . annotationType ( ) = = RRemoteAsync . class ) {
Class < T > syncInterface = ( Class < T > ) ( ( RRemoteAsync ) annotation ) . value ( ) ;
Class < T > syncInterface = ( Class < T > ) ( ( RRemoteAsync ) annotation ) . value ( ) ;
for ( Method m : remoteInterface . getMethods ( ) ) {
try {
syncInterface . getMethod ( m . getName ( ) , m . getParameterTypes ( ) ) ;
} catch ( NoSuchMethodException e ) {
throw new IllegalArgumentException ( "Method '" + m . getName ( ) + "' with params '" + Arrays . toString ( m . getParameterTypes ( ) )
+ "' isn't defined in " + syncInterface ) ;
throw new IllegalArgumentException ( "Method '" + m . getName ( ) + "' with params '"
+ Arrays . toString ( m . getParameterTypes ( ) ) + "' isn't defined in " + syncInterface ) ;
} catch ( SecurityException e ) {
throw new IllegalArgumentException ( e ) ;
}
if ( ! m . getReturnType ( ) . getClass ( ) . isInstance ( Future . class ) ) {
throw new IllegalArgumentException ( m . getReturnType ( ) . getClass ( ) + " isn't allowed as return type" ) ;
throw new IllegalArgumentException (
m . getReturnType ( ) . getClass ( ) + " isn't allowed as return type" ) ;
}
}
return async ( remoteInterface , options , syncInterface ) ;
}
}
return sync ( remoteInterface , options ) ;
}
private < T > T async ( final Class < T > remoteInterface , final RemoteInvocationOptions options , final Class < ? > syncInterface ) {
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 ( ) ;
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 {
@ -333,10 +375,8 @@ public class RedissonRemoteService implements RRemoteService {
return toString . hashCode ( ) ;
}
if ( ! optionsCopy . isResultExpected ( )
& & ! ( method . getReturnType ( ) . equals ( Void . class )
| | method . getReturnType ( ) . equals ( Void . TYPE )
| | method . getReturnType ( ) . equals ( Future . class ) ) ) {
if ( ! optionsCopy . isResultExpected ( ) & & ! ( method . getReturnType ( ) . equals ( Void . class )
| | method . getReturnType ( ) . equals ( Void . TYPE ) | | method . getReturnType ( ) . equals ( Future . class ) ) ) {
throw new IllegalArgumentException ( "The noResult option only supports void return value" ) ;
}
@ -345,33 +385,64 @@ public class RedissonRemoteService implements RRemoteService {
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 ( ) , args , optionsCopy , System . currentTimeMillis ( ) ) ;
final RemotePromise < Object > result = new RemotePromise < Object > ( ImmediateEventExecutor . INSTANCE . newPromise ( ) ) {
final RBlockingQueue < RemoteServiceRequest > requestQueue = redisson . getBlockingQueue ( requestQueueName ,
getCodec ( ) ) ;
final RemoteServiceRequest request = new RemoteServiceRequest ( requestId , method . getName ( ) , 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 ( ) ) {
Future < Boolean > future = redisson . getScript ( ) . evalAsync ( responseName , Mode . READ_WRITE , LongCodec . INSTANCE ,
Future < 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;" , RScript . ReturnType . BOOLEAN , Arrays . < Object > asList ( ackName , responseName , requestQueueName ) ,
encode ( request ) ,
request . getOptions ( ) . getAckTimeoutInMillis ( ) ) ;
return commandExecutor . get ( future ) ;
+ "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 ) {
return true ;
}
return cancel ( syncInterface , requestId , request , mayInterruptIfRunning ) ;
}
return requestQueue . remove ( request ) ;
boolean removed = requestQueue . remove ( request ) ;
if ( removed ) {
return true ;
}
return cancel ( syncInterface , requestId , request , mayInterruptIfRunning ) ;
}
private boolean cancel ( Class < ? > remoteInterface , String requestId , RemoteServiceRequest request ,
boolean mayInterruptIfRunning ) {
RBlockingQueueAsync < RemoteServiceCancelRequest > cancelRequestQueue = redisson . getBlockingQueue ( getCancelRequestQueueName ( remoteInterface , requestId ) , getCodec ( ) ) ;
cancelRequestQueue . putAsync ( new RemoteServiceCancelRequest ( mayInterruptIfRunning ) ) ;
cancelRequestQueue . expireAsync ( 60 , TimeUnit . SECONDS ) ;
awaitUninterruptibly ( ) ;
return isCancelled ( ) ;
}
} ;
Future < Boolean > addFuture = addAsync ( requestQueue , request , result ) ;
addFuture . addListener ( new FutureListener < Boolean > ( ) {
@ -381,16 +452,10 @@ public class RedissonRemoteService implements RRemoteService {
result . tryFailure ( future . cause ( ) ) ;
return ;
}
final RBlockingQueue < ? extends RRemoteServiceResponse > responseQueue ;
if ( optionsCopy . isAckExpected ( ) | | optionsCopy . isResultExpected ( ) ) {
responseQueue = redisson . getBlockingQueue ( responseName , getCodec ( ) ) ;
} else {
responseQueue = null ;
}
if ( optionsCopy . isAckExpected ( ) ) {
Future < RemoteServiceAck > ackFuture = ( Future < RemoteServiceAck > ) responseQueue . pollAsync ( optionsCopy . getAckTimeoutInMillis ( ) , TimeUnit . MILLISECONDS ) ;
RBlockingQueue < RemoteServiceAck > responseQueue = redisson . getBlockingQueue ( responseName , getCodec ( ) ) ;
Future < RemoteServiceAck > ackFuture = responseQueue . pollAsync ( optionsCopy . getAckTimeoutInMillis ( ) , TimeUnit . MILLISECONDS ) ;
ackFuture . addListener ( new FutureListener < RemoteServiceAck > ( ) {
@Override
public void operationComplete ( Future < RemoteServiceAck > future ) throws Exception {
@ -398,51 +463,54 @@ public class RedissonRemoteService implements RRemoteService {
result . tryFailure ( future . cause ( ) ) ;
return ;
}
RemoteServiceAck ack = future . getNow ( ) ;
if ( ack = = null ) {
Future < RemoteServiceAck > ackFutureAttempt = tryPollAckAgainAsync ( optionsCopy , responseQueue , ackName ) ;
Future < RemoteServiceAck > ackFutureAttempt =
tryPollAckAgainAsync ( optionsCopy , responseQueue , ackName ) ;
ackFutureAttempt . addListener ( new FutureListener < RemoteServiceAck > ( ) {
@Override
public void operationComplete ( Future < RemoteServiceAck > future ) throws Exception {
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 ) ;
Exception ex = new RemoteServiceAckTimeoutException (
"No ACK response after "
+ optionsCopy . getAckTimeoutInMillis ( )
+ "ms for request: " + request ) ;
result . tryFailure ( ex ) ;
return ;
}
invokeAsync ( optionsCopy , result , request , response Queu e, ackName ) ;
invokeAsync ( optionsCopy , result , request , response Nam e, ackName ) ;
}
} ) ;
} else {
invokeAsync ( optionsCopy , result , request , response Queu e) ;
invokeAsync ( optionsCopy , result , request , response Nam e) ;
}
}
} ) ;
} else {
invokeAsync ( optionsCopy , result , request , responseQueue ) ;
}
invokeAsync ( optionsCopy , result , request , responseName ) ;
}
}
} ) ;
return result ;
}
} ;
return ( T ) Proxy . newProxyInstance ( remoteInterface . getClassLoader ( ) , new Class [ ] { remoteInterface } , handler ) ;
return ( T ) Proxy . newProxyInstance ( remoteInterface . getClassLoader ( ) , new Class [ ] { remoteInterface } , handler ) ;
}
private void invokeAsync ( final RemoteInvocationOptions optionsCopy ,
final Promise < Object > result , final RemoteServiceRequest request ,
final RBlockingQueue < ? extends RRemoteServiceResponse > responseQueue ,
final String ackName ) {
private void invokeAsync ( final RemoteInvocationOptions optionsCopy , final RemotePromise < Object > result ,
final RemoteServiceRequest request , final String responseName , final String ackName ) {
Future < Boolean > deleteFuture = redisson . getBucket ( ackName ) . deleteAsync ( ) ;
deleteFuture . addListener ( new FutureListener < Boolean > ( ) {
@Override
@ -451,50 +519,57 @@ public class RedissonRemoteService implements RRemoteService {
result . tryFailure ( future . cause ( ) ) ;
return ;
}
invokeAsync ( optionsCopy , result , request , response Queu e) ;
invokeAsync ( optionsCopy , result , request , response Nam e) ;
}
} ) ;
}
private void invokeAsync ( final RemoteInvocationOptions optionsCopy , final Promise < Object > result ,
final RemoteServiceRequest request ,
final RBlockingQueue < ? extends RRemoteServiceResponse > responseQueue ) {
private void invokeAsync ( final RemoteInvocationOptions optionsCopy , final RemotePromise < Object > result ,
final RemoteServiceRequest request , final String responseName ) {
// poll for the response only if expected
if ( optionsCopy . isResultExpected ( ) ) {
Future < RemoteServiceResponse > responseFuture = ( Future < RemoteServiceResponse > ) responseQueue . pollAsync ( optionsCopy . getExecutionTimeoutInMillis ( ) , TimeUnit . MILLISECONDS ) ;
responseFuture . addListener ( new FutureListener < RemoteServiceResponse > ( ) {
RBlockingQueue < RRemoteServiceResponse > responseQueue = redisson . getBlockingQueue ( responseName , getCodec ( ) ) ;
Future < RRemoteServiceResponse > responseFuture = responseQueue
. pollAsync ( optionsCopy . getExecutionTimeoutInMillis ( ) , TimeUnit . MILLISECONDS ) ;
responseFuture . addListener ( new FutureListener < RRemoteServiceResponse > ( ) {
@Override
public void operationComplete ( Future < RemoteServiceResponse > future )
throws Exception {
public void operationComplete ( Future < RRemoteServiceResponse > future ) throws Exception {
if ( ! future . isSuccess ( ) ) {
result . tryFailure ( future . cause ( ) ) ;
return ;
}
if ( future . getNow ( ) = = null ) {
RemoteServiceTimeoutException e = new RemoteServiceTimeoutException ( "No response after " + optionsCopy . getExecutionTimeoutInMillis ( ) + "ms for request: " + request ) ;
RemoteServiceTimeoutException e = new RemoteServiceTimeoutException ( "No response after "
+ optionsCopy . getExecutionTimeoutInMillis ( ) + "ms for request: " + request ) ;
result . tryFailure ( e ) ;
return ;
}
if ( future . getNow ( ) . getError ( ) ! = null ) {
result . tryFailure( future . getNow ( ) . getError ( ) ) ;
if ( future . getNow ( ) instanceof RemoteServiceCancelResponse ) {
result . doCancel( ) ;
return ;
}
result . trySuccess ( future . getNow ( ) . getResult ( ) ) ;
RemoteServiceResponse response = ( RemoteServiceResponse ) future . getNow ( ) ;
if ( response . getError ( ) ! = null ) {
result . tryFailure ( response . getError ( ) ) ;
return ;
}
result . trySuccess ( response . getResult ( ) ) ;
}
} ) ;
}
}
private < T > T sync ( final Class < T > remoteInterface , final RemoteInvocationOptions options ) {
final String interfaceName = remoteInterface . getName ( ) ;
// local copy of the options, to prevent mutation
final RemoteInvocationOptions optionsCopy = new RemoteInvocationOptions ( options ) ;
final String toString = getClass ( ) . getSimpleName ( ) + "-" + remoteInterface . getSimpleName ( ) + "-proxy-" + generateRequestId ( ) ;
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 {
@ -506,15 +581,17 @@ public class RedissonRemoteService implements RRemoteService {
return toString . hashCode ( ) ;
}
if ( ! optionsCopy . isResultExpected ( ) & & ! ( method . getReturnType ( ) . equals ( Void . class ) | | method . getReturnType ( ) . equals ( Void . TYPE ) ) )
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 ( ) , args , optionsCopy , System . currentTimeMillis ( ) ) ;
RBlockingQueue < RemoteServiceRequest > requestQueue = redisson . getBlockingQueue ( requestQueueName ,
getCodec ( ) ) ;
RemoteServiceRequest request = new RemoteServiceRequest ( requestId , method . getName ( ) , args , optionsCopy ,
System . currentTimeMillis ( ) ) ;
requestQueue . add ( request ) ;
RBlockingQueue < RRemoteServiceResponse > responseQueue = null ;
@ -526,11 +603,13 @@ public class RedissonRemoteService implements RRemoteService {
// poll for the ack only if expected
if ( optionsCopy . isAckExpected ( ) ) {
String ackName = getAckName ( remoteInterface , requestId ) ;
RemoteServiceAck ack = ( RemoteServiceAck ) responseQueue . poll ( optionsCopy . getAckTimeoutInMillis ( ) , TimeUnit . MILLISECONDS ) ;
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 ) ;
throw new RemoteServiceAckTimeoutException ( "No ACK response after "
+ optionsCopy . getAckTimeoutInMillis ( ) + "ms for request: " + request ) ;
}
}
redisson . getBucket ( ackName ) . delete ( ) ;
@ -538,9 +617,11 @@ public class RedissonRemoteService implements RRemoteService {
// poll for the response only if expected
if ( optionsCopy . isResultExpected ( ) ) {
RemoteServiceResponse response = ( RemoteServiceResponse ) responseQueue . poll ( optionsCopy . getExecutionTimeoutInMillis ( ) , TimeUnit . MILLISECONDS ) ;
RemoteServiceResponse response = ( RemoteServiceResponse ) responseQueue
. poll ( optionsCopy . getExecutionTimeoutInMillis ( ) , TimeUnit . MILLISECONDS ) ;
if ( response = = null ) {
throw new RemoteServiceTimeoutException ( "No response after " + optionsCopy . getExecutionTimeoutInMillis ( ) + "ms for request: " + request ) ;
throw new RemoteServiceTimeoutException ( "No response1 after "
+ optionsCopy . getExecutionTimeoutInMillis ( ) + "ms for request: " + request ) ;
}
if ( response . getError ( ) ! = null ) {
throw response . getError ( ) ;
@ -552,39 +633,40 @@ public class RedissonRemoteService implements RRemoteService {
}
} ;
return ( T ) Proxy . newProxyInstance ( remoteInterface . getClassLoader ( ) , new Class [ ] { remoteInterface } , handler ) ;
return ( T ) Proxy . newProxyInstance ( remoteInterface . getClassLoader ( ) , new Class [ ] { remoteInterface } , handler ) ;
}
private RemoteServiceAck tryPollAckAgain ( RemoteInvocationOptions optionsCopy ,
RBlockingQueue < ? extends RRemoteServiceResponse > responseQueue , String ackName ) throws InterruptedException {
Future < Boolean > ackClientsFuture = redisson . getScript ( ) . evalAsync ( ackName , Mode . READ_WRITE , LongCodec . INSTANCE ,
"if redis.call('setnx', KEYS[1], 1) == 1 then "
+ "redis.call('pexpire', KEYS[1], ARGV[1]);"
+ "return 0;"
+ "end;"
+ "redis.call('del', KEYS[1]);"
+ "return 1;" ,
RScript . ReturnType . BOOLEAN , Arrays . < Object > asList ( ackName ) , optionsCopy . getAckTimeoutInMillis ( ) ) ;
RBlockingQueue < ? extends RRemoteServiceResponse > responseQueue , String ackName )
throws InterruptedException {
Future < Boolean > ackClientsFuture = commandExecutor . evalWriteAsync ( ackName , LongCodec . INSTANCE , RedisCommands . EVAL_BOOLEAN ,
"if redis.call('setnx', KEYS[1], 1) == 1 then "
+ "redis.call('pexpire', KEYS[1], ARGV[1]);"
+ "return 0;"
+ "end;"
+ "redis.call('del', KEYS[1]);"
+ "return 1;" ,
Arrays . < Object > asList ( ackName ) , optionsCopy . getAckTimeoutInMillis ( ) ) ;
ackClientsFuture . sync ( ) ;
if ( ackClientsFuture . getNow ( ) ) {
return ( RemoteServiceAck ) responseQueue . poll ( ) ;
}
return null ;
}
private Future < RemoteServiceAck > tryPollAckAgainAsync ( RemoteInvocationOptions optionsCopy ,
final RBlockingQueue < ? extends RRemoteServiceResponse > responseQueue , String ackName )
final RBlockingQueue < RemoteServiceAck > responseQueue , String ackName )
throws InterruptedException {
final Promise < RemoteServiceAck > promise = ImmediateEventExecutor . INSTANCE . newPromise ( ) ;
Future < Boolean > ackClientsFuture = redisson. getScript ( ) . evalAsync ( ackName , Mode . READ_WRITE , LongCodec . INSTANCE ,
"if redis.call('setnx', KEYS[1], 1) == 1 then "
Future < Boolean > ackClientsFuture = commandExecutor. evalWriteAsync ( ackName , LongCodec . INSTANCE , RedisCommands . EVAL_BOOLEAN ,
"if redis.call('setnx', KEYS[1], 1) == 1 then "
+ "redis.call('pexpire', KEYS[1], ARGV[1]);"
+ "return 0;"
+ "end;"
+ "redis.call('del', KEYS[1]);"
+ "return 1;" ,
RScript. ReturnType . BOOLEAN , Arrays. < Object > asList ( ackName ) , optionsCopy . getAckTimeoutInMillis ( ) ) ;
+ "end;"
+ "redis.call('del', KEYS[1]);"
+ "return 1;" ,
Arrays. < Object > asList ( ackName ) , optionsCopy . getAckTimeoutInMillis ( ) ) ;
ackClientsFuture . addListener ( new FutureListener < Boolean > ( ) {
@Override
public void operationComplete ( Future < Boolean > future ) throws Exception {
@ -592,9 +674,9 @@ public class RedissonRemoteService implements RRemoteService {
promise . setFailure ( future . cause ( ) ) ;
return ;
}
if ( future . getNow ( ) ) {
Future < RemoteServiceAck > pollFuture = ( Future < RemoteServiceAck > ) responseQueue . pollAsync ( ) ;
Future < RemoteServiceAck > pollFuture = responseQueue . pollAsync ( ) ;
pollFuture . addListener ( new FutureListener < RemoteServiceAck > ( ) {
@Override
public void operationComplete ( Future < RemoteServiceAck > future ) throws Exception {
@ -602,7 +684,7 @@ public class RedissonRemoteService implements RRemoteService {
promise . setFailure ( future . cause ( ) ) ;
return ;
}
promise . setSuccess ( future . getNow ( ) ) ;
}
} ) ;
@ -629,9 +711,11 @@ public class RedissonRemoteService implements RRemoteService {
return batch . executeAsync ( ) ;
}
protected Future < Boolean > addAsync ( RBlockingQueue < RemoteServiceRequest > requestQueue , RemoteServiceRequest request , RemotePromise < Object > result ) {
protected Future < Boolean > addAsync ( RBlockingQueue < RemoteServiceRequest > requestQueue , RemoteServiceRequest request ,
RemotePromise < Object > result ) {
Future < Boolean > future = requestQueue . addAsync ( request ) ;
result . setAddFuture ( future ) ;
return future ;
}
}