Jedis and Lettuce async abilities

cykopath picture cykopath · Sep 30, 2015 · Viewed 19.2k times · Source

I am using redis with Akka so I need no blocking calls. Lettuce has async-future call built into it. But Jedis is the recommended client by Redis. Can someone tell me if I am using both of them the right way. If so which one is better.

JEDIS I am using a static Jedis connection pool to get con and using Akka future callback to process the result. My concern here is when I use another thread (callable) to get the result that thread is eventually going to block for the result. While Lettuce might have some more efficient way of doing this.

 private final class OnSuccessExtension extends OnSuccess<String> {
            private final ActorRef senderActorRef;
            private final Object message;
            @Override
            public void onSuccess(String valueRedis) throws Throwable {
                log.info(getContext().dispatcher().toString());
                senderActorRef.tell((String) message, ActorRef.noSender());
            }
             public OnSuccessExtension(ActorRef senderActorRef,Object message) {
                    this.senderActorRef = senderActorRef;
                    this.message=message;
                }
        }
        ActorRef senderActorRef = getSender(); //never close over a future
            if (message instanceof String) {
        Future<String> f =akka.dispatch.Futures.future(new Callable<String>() {
                    public String call() {
                        String result;
                        try(Jedis jedis=JedisWrapper.redisPool.getResource()) {
                            result = jedis.get("name");
                        }
                        return result;
                    }
                }, ex);
                f.onSuccess(new OnSuccessExtension(senderActorRef,message), ex);
    }

LETTUCE

ExecutorService executorService = Executors.newFixedThreadPool(10);
public void onReceive(Object message) throws Exception {
        ActorRef senderActorRef = getSender(); //never close over a future
        if (message instanceof String) {

            final RedisFuture<String> future = lettuce.connection.get("name");
            future.addListener(new Runnable() {
                final ActorRef sender = senderActorRef;
                final String msg =(String) message;
                @Override
                public void run() {
                    try {
                        String value = future.get();
                        log.info(value);
                        sender.tell(message, ActorRef.noSender());
                    } catch (Exception e) {
                    }
                }
            }, executorService);

If lettuce is a better option for Async calls. Then what type of executor should I go with in production environment. If possible can I use a Akka dispatcher as an execution context for Letture future call.

Answer

mp911de picture mp911de · Sep 30, 2015

There is no one answer to your question because it depends.

Jedis and lettuce are both mature clients. To complete the list of Java clients, there is also Redisson, which adds another layer of abstraction (Collection/Queue/Lock/... interfaces instead of raw Redis commands).

It pretty much depends on how you're working with the clients. In general, Jedis (java based client to connect to redis) is single-threaded in terms of data access, so the only benefit you gain by concurrency is offloading the protocol and I/O work to different threads. That is not fully true to lettuce and Redisson since they use netty under the hood (netty binds one socket channel to a particular event loop thread).

With Jedis, you can use only one connection only with one thread at a time. That correlates nicely with the Akka actor model because one actor instance is occupied only by one thread at a time.

On the other side, you need as much Jedis connections as threads that deal with a particular actor. If you start sharing Jedis connections across different actors, you either go for connection pooling, or you need to have a dedicated Jedis connection per actor instance. Please keep in mind that you need to take care of the reconnection (once a Redis connection is broken) by yourself.

With Redisson and lettuce, you get transparent reconnection if you wish to do so (That's the default value to lettuce, not sure about Redisson).

By using lettuce and Redisson you can share one connection amongst all actors because they are thread-safe. You cannot share one lettuce connection in two cases:

  1. Blocking operations (since you would block all other users of the connection)
  2. Transactions (MULTI/EXEC, since you would mix different operations with the transactions and that is certainly a thing you do not want to do so)

Jedis has no async interface, so you're required to do this by yourself. That's feasible, and I did something similar with MongoDB, offloading/decoupling the I/O part to other actors. You can use the approach from your code, but you're not required to provide an own executor service because you do non-blocking operations in the runnable listener.

With lettuce 4.0 you'll get Java 8 support (which is way better in terms of the async API because of the CompletionStage interface), and you can even use RxJava (reactive programming) to approach concurrency.

Lettuce is not opinionated on your concurrency model. It allows you to use it according to you needs, except the plain Future/ListenableFuture API of Java 6/7 and Guava is not very nice to use.

HTH, Mark