使用JetCache的异步API访问Redis缓存

Jedis一直是Java中使用最广泛的Redis client,现在我们又有了一个新的选择:lettuce
lettuce由Pivotal(也就是目前维护Spring的公司)的Mark Paluch发起,支持异步API和Reactive API,连接可以复用,近期开发也非常活跃,成为Redis客户端中的一个新锐。

JetCache提供的统一API也支持异步操作方式,当前,只有使用lettuce访问Redis能实现异步。
当下层使用的驱动不支持异步,比如访问Tair或者使用Jedis访问Redis时,会自动退化为同步堵塞的方式。所以从API上说,是完全兼容和一致的。

JetCache提供了简单易用的常规API(正常方法命名)和具有完整返回值的大写方法名API,异步API基于这些大写方法名的方法。
关于JetCache API的介绍请参考这里:https://github.com/alibaba/jetcache/wiki/CacheAPI_CN

为什么要异步访问缓存呢?虽然大部分情况下Redis缓存访问很快,但毕竟是一个网络IO操作,同步访问缓存多少也会增加一点RT,特别是在循环中操作缓存时,就积少成多了。
比如下面的例子从jdbc结果集读取了数据,然后写缓存:

//......获取数据库连接,执行SQL,得到ResultSet,省略
while(resultSet.next()){
    User user = new User();
    user.setId(resultSet.getLong("user_id"));
    //.....省略一部分设置用户属性的代码
    
    cache.put(user.getId(), user);
}

仔细分析一下就会发现,其实这个put方法完全没有必要同步执行,因为我们并不关心它的返回值(很多类似的场景下即使操作缓存出现了错误,我们也不能干什么,只能忽略错误继续处理)。
使用JetCache和lettuce的时候,我们什么都不用做,上面的代码的put操作就是异步的,它会马上返回,后续自动异步更新缓存,是不是很酷?

如果要显式的使用异步API,以GET方法为例:

CacheGetResult<UserDO> r = cache.GET(userId);

如果使用底层驱动不支持异步,那么GET方法就会堵塞直到缓存操作完成。

如果支持异步,这一行代码执行完以后,缓存操作可能还没有完成,此时调用r.isSuccess()或者r.getValue()或者r.getMessage()将会堵塞直到缓存操作完成。
如果不想被堵塞,并且需要在缓存操作完成以后执行后续操作,可以这样做:

CompletionStage<ResultData> future = r.future();
future.thenRun(() -> {
    if(r.isSuccess()){
        System.out.println(r.getValue());
    }
});

以上代码将会在缓存操作异步完成后,在完成异步操作的线程中调用thenRun中指定的回调。
CompletionStage是Java8新增的功能,如果对此不太熟悉可以先查阅相关的文档。
需要注意的是,既然已经选择了异步的开发方式,在回调中不能调用堵塞方法,以免堵塞其他的线程(回调方法很可能是在event loop线程中执行的)。

部分常规api(方法名小写字母开头)由于没有返回值,因此不需要任何修改,它们直接就是异步的,比如put和removeAll方法;而get方法由于需要取返回值,所以仍然会堵塞。

使用lettuce作为客户端访问Redis还有另一个好处,就是不用配置连接池。
lettuce的连接是线程安全的可以复用,所以一个服务器维持一个Redis连接就好了。
对于一些云上的Redis,连接数可能是受限的,要支持更多连接就要付更多钱,这个时候lettuce就非常有优势了。这方面可以参考lettuce的文档获得更多信息:
https://github.com/lettuce-io/lettuce-core/wiki/Connection-Pooling

上一篇:mysql 获取最近6个月的日期的 sql 实例


下一篇:Kotlin 全栈开发之 前端 JavaScript: Getting Started with Kotlin and JavaScript with Gradle