在 Quarkus 2.11 中,我们引入了一个新的 API 来与 Redis 交互。Redis 数据源 API 旨在更简单、更广泛和类型安全。在底层,它使用了一个高性能的非阻塞客户端(如果您更喜欢低级 API,也可以使用它)。
在这篇文章中,我们将介绍这个新的 API 并使用它来构建缓存,这是 Redis 的主要用例之一。
什么是 Redis?
Redis是一种开源的内存数据存储,可用作数据库、缓存、流引擎和消息代理。Redis 通常用作实时数据存储、缓存后端、地理空间实体的数据存储等。要与 Redis 交互,您需要发出命令并接收响应。这些命令以键为目标并操作相关数据。有许多命令分为组,包括:
-
用于操作位向量的 BitMap 组
-
通用组来操作键
-
地理空间组来操作地理项目
-
哈希组来操作field -> item对集合(如Map在 Java 中)
-
List、Set 和 Sorted Set 组用于存储项目的列表、集合和排序集
-
Pub/Sub 组在通道上发出消息并接收它们
-
用于操作值的字符串组(在 Redis 中,字符串表示值,包括二进制、数字……)
-
执行事务的事务组
您可以在Redis 命令页面上找到完整的命令列表。
新的 Quarkus Redis API
新 Quarkus Redis API 的入口点是两个数据源接口:
-
io.quarkus.redis.datasource.RedisDataSource- 命令式(阻塞)API
-
io.quarkus.redis.datasource.ReactiveRedisDataSource- 反应式 API
如上所述,这些 API 是在较低级别的客户端之上实现的:
数据源 API 遵循命令组结构。对于每个组,您检索一个专门用于发出该组命令的对象。在这方面,这个新的 API 不是 Redis 的抽象。你仍然需要知道你需要的命令。
例如,要操作 a Set
record Person(String firstName, String lastName) {} @ApplicationScoped class PersonService { private final SetCommands<String, Person> commands; public PersonService(RedisDataSource ds) { // Retrieve the `set` group commands = ds.set(Person.class); } public void add(Person person) { // Emit the `sadd` command commands.sadd("key", person); } }
复制
API 为您管理序列化和反序列化。目前,它使用 JSON 作为对象(使用 Jackson),但很快 API 将提供更高级的功能。
此示例使用命令式 API,但反应式 API 是对称的。
实现 Redis 缓存
是时候编写更具凝聚力的代码了。让我们想象以下应用程序:
我们有一个数据库存储Heroes,其中很多。您需要根据他们的等级返回3个最强大的英雄。当然,你可以使用你的 SQL 忍者技能,但是让我们假设这段代码是很久以前编码的,无法更改,而且非常耗时:
// Dumb approach, don't do this return new Ranking(Hero.<Hero>listAll() .stream() .sorted((o1, o2) -> Integer.compare(o2.level, o1.level)) .peek(h -> { // do something very long... nap(); }) .limit(3) .collect(Collectors.toList()));
复制
因此,避免每次调用都重新计算这组英雄的一种解决方案是将结果缓存一段时间,比如 10 秒。让我们考虑在这种情况下返回可能过时的结果集是可以接受的。
要使用新的 Redis API,我们需要使用redis-client扩展。对于来自先前 API 的用户,它是相同的扩展。以前的 API 仍然可用,但已被弃用,我们计划在某个时候将其删除。
现在我们可以使用 了,我们可以按如下RedisDataSource方式实现MyRedisCache该类:
package me.escoffier.quarkus.supes; import io.quarkus.redis.datasource.RedisDataSource; import io.quarkus.redis.datasource.string.SetArgs; import io.quarkus.redis.datasource.string.StringCommands; import javax.enterprise.context.ApplicationScoped; import java.time.Duration; import java.util.function.Supplier; @ApplicationScoped public class MyRedisCache { private final StringCommands<String, Ranking> commands; public MyRedisCache(RedisDataSource ds) { this.commands = ds.string(Ranking.class); } public Ranking get(String key) { return commands.get(key); } public void set(String key, Ranking result) { commands.set(key, result, new SetArgs().ex(Duration.ofSeconds(10))); } public void evict(String key) { commands.getdel(key); } public Ranking getOrSetIfAbsent(String key, Supplier<Ranking> computation) { var cached = get(key); if (cached != null) { return cached; } else { var result = computation.get(); set(key, result); return result; } } }
复制
请注意,它是一个没有任何花哨功能的简单缓存。Redis 提供了更高级的命令来实现更复杂的策略。
构造函数接收RedisDataSource并获取一个对象来操作 Redis值。在我们的例子中,Ranking(前 3 名英雄)
该get方法发出 Redisget命令以检索已存储的Ranking(null`(如果没有)。
该set方法发出 Redisset命令并将 a 存储Ranking到传递的密钥中。该命令还配置过期时间。因此,10 秒后,该值被 Redis 删除。如上所述,Ranking 实例被序列化为 JSON 文档。
该evict方法允许删除存储的值。多个命令可以做到这一点,例如delor getdel(它也返回存储的值)。
对于我们的应用程序,我们需要一些更花哨的东西。我们想检查我们在 Redis 中是否有值。如果是,则使用该值,如果不是,则计算该值并存储它。这是在getOrSetIfAbsent.
现在,我们可以使用这个缓存来避免每次调用的繁重计算(查看HeroService类以查看完整代码):
@Inject MyRedisCache cache; public Ranking getTopHeroes() { return cache.getOrSetIfAbsent("top", () -> { // Dumb approach, don't do this return new Ranking(Hero.<Hero>listAll() .stream() .sorted((o1, o2) -> Integer.compare(o2.level, o1.level)) .peek(h -> { // do something very long... nap(); }) .limit(3) .collect(Collectors.toList())); }); }
复制
要运行应用程序,只需启动mvn quarkus:dev并打开浏览器访问http://localhost:8080:
要查看缓存的运行情况,请检查页面上显示的时间并刷新页面。不要忘记缓存的值仅在 10 秒内有效(在 中设置MyRedisCache)。因此,如果您等待 10 秒,它将重新计算结果。
Quarkus 附带一个 Redis 开发服务,它会自动在您的机器上启动一个 Redis 实例并配置应用程序。请注意,您需要能够在本地运行容器才能使用此功能。
解决方案
这篇文章简要介绍了新的 Redis API,并通过一个缓存实现示例演示了它的用法。完整代码可从此GitHub 存储库获得。
API 支持更多功能,例如地理空间数据、发布/订阅和事务,可用于改进getOrSetIfAbsent方法。我们将在以后的文章中介绍更高级的用例。
您可以在以下位置找到有关新 API 的更多详细信息:
-
Quarkus 入门指南
-
Quarkus Redis 参考指南
此外,Quarkus 团队正致力于将Redis 集成为缓存实现。因此,最终,您只需要使用@CacheResult,并让魔法发生。
原文标题:Introducing the new Redis API - How to cache with Redis?
原文作者:Clement Escoffier
原文地址:https://cn.quarkus.io/blog/redis-api-intro/