在 spring-data-redis-reactive 中链接 redis 操作的正确方法是什么?
What is the correct way to chain redis operations in spring-data-redis-reactive?
在spring-data-redis-reactive
中,写入操作returns redis 执行结果,这使得链接运算符非常困难。以 Redis In Action 第 1 章中的 reddit 为例。我尝试像这样重新实现:
@Service
public class ArticleService {
private final ReactiveStringRedisTemplate redisTemplate;
private final long voteScore = 432L;
public ArticleService(ReactiveStringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public Mono<Article> createArticle(Article article) {
long now = System.currentTimeMillis();
Map<String, String> newArticle = new HashMap<>();
newArticle.put("title", article.getTitle());
newArticle.put("link", article.getLink());
newArticle.put("poster", article.getPoster());
newArticle.put("time", String.valueOf(now));
newArticle.put("votes", "1");
return redisTemplate.opsForValue()
.increment("article:")
.doOnNext(id -> redisTemplate.opsForSet().add("voted:" + id.toString(), article.getPoster()).subscribe())
.doOnNext(id -> redisTemplate.expire("votes:" + id.toString(), Duration.ofDays(7)).subscribe())
.doOnNext(id -> redisTemplate.opsForHash().putAll("article:" + id.toString(), newArticle).subscribe())
.doOnNext(id -> redisTemplate.opsForZSet().add("score:", "article:" + id.toString(), now + voteScore).subscribe())
.doOnNext(id -> redisTemplate.opsForZSet().add("time:", "article:" + id.toString(), now).subscribe())
.map(id -> {
article.setId(id);
article.setVotes("1");
return article;
});
}
}
如您所见,我使用doOnNext
来避免丢失increment
返回的id值,并且每个doOnNext
中都有一个subscribe()
以确保每个redis操作都被执行了。但我认为这不是推荐的方式。我认为应用程序应该尽量避免 subscribe()
并主要关注链接流。
在spring-data-redis-reactive
中做很多redis写操作的正确方法是什么?
避免在两者之间进行订阅,如果您从 WEB 界面调用它,Spring 将在最后订阅流。这是一个如何实现的例子
return redisTemplate.opsForValue()
.increment("article:")
.flatMap(id -> // Mono.zip will execute concurrently all the modifications below
Mono.zip(redisTemplate.opsForSet().add("voted:" + id.toString(), article.getPoster()),
redisTemplate.expire("votes:" + id.toString(), Duration.ofDays(7)),
redisTemplate.opsForHash().putAll("article:" + id.toString(), newArticle),
redisTemplate.opsForZSet().add("score:", "article:" + id.toString(), now + voteScore),
redisTemplate.opsForZSet().add("time:", "article:" + id.toString(), now)
)
.map(tuple -> id))
.map(id -> {
article.setId(id);
article.setVotes("1");
return article;
});
但是您应该考虑将修改执行到 MULTI
Redis 命令中,以确保自动发生 https://redis.io/commands/multi. Because you don't have any validation and/or restriction the EVAL is not necessary https://redis.io/commands/eval
在spring-data-redis-reactive
中,写入操作returns redis 执行结果,这使得链接运算符非常困难。以 Redis In Action 第 1 章中的 reddit 为例。我尝试像这样重新实现:
@Service
public class ArticleService {
private final ReactiveStringRedisTemplate redisTemplate;
private final long voteScore = 432L;
public ArticleService(ReactiveStringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public Mono<Article> createArticle(Article article) {
long now = System.currentTimeMillis();
Map<String, String> newArticle = new HashMap<>();
newArticle.put("title", article.getTitle());
newArticle.put("link", article.getLink());
newArticle.put("poster", article.getPoster());
newArticle.put("time", String.valueOf(now));
newArticle.put("votes", "1");
return redisTemplate.opsForValue()
.increment("article:")
.doOnNext(id -> redisTemplate.opsForSet().add("voted:" + id.toString(), article.getPoster()).subscribe())
.doOnNext(id -> redisTemplate.expire("votes:" + id.toString(), Duration.ofDays(7)).subscribe())
.doOnNext(id -> redisTemplate.opsForHash().putAll("article:" + id.toString(), newArticle).subscribe())
.doOnNext(id -> redisTemplate.opsForZSet().add("score:", "article:" + id.toString(), now + voteScore).subscribe())
.doOnNext(id -> redisTemplate.opsForZSet().add("time:", "article:" + id.toString(), now).subscribe())
.map(id -> {
article.setId(id);
article.setVotes("1");
return article;
});
}
}
如您所见,我使用doOnNext
来避免丢失increment
返回的id值,并且每个doOnNext
中都有一个subscribe()
以确保每个redis操作都被执行了。但我认为这不是推荐的方式。我认为应用程序应该尽量避免 subscribe()
并主要关注链接流。
在spring-data-redis-reactive
中做很多redis写操作的正确方法是什么?
避免在两者之间进行订阅,如果您从 WEB 界面调用它,Spring 将在最后订阅流。这是一个如何实现的例子
return redisTemplate.opsForValue()
.increment("article:")
.flatMap(id -> // Mono.zip will execute concurrently all the modifications below
Mono.zip(redisTemplate.opsForSet().add("voted:" + id.toString(), article.getPoster()),
redisTemplate.expire("votes:" + id.toString(), Duration.ofDays(7)),
redisTemplate.opsForHash().putAll("article:" + id.toString(), newArticle),
redisTemplate.opsForZSet().add("score:", "article:" + id.toString(), now + voteScore),
redisTemplate.opsForZSet().add("time:", "article:" + id.toString(), now)
)
.map(tuple -> id))
.map(id -> {
article.setId(id);
article.setVotes("1");
return article;
});
但是您应该考虑将修改执行到 MULTI
Redis 命令中,以确保自动发生 https://redis.io/commands/multi. Because you don't have any validation and/or restriction the EVAL is not necessary https://redis.io/commands/eval