坏蛋Dan
知乎@坏蛋Dan
发布时间:2023.12.29

前言

这一大段时间都没写文档,一方面是之前提到的在参与一个项目重构,全是坑搞得焦头烂额。另一方面是有准备跑路的打算,还有一方面是我这边在实现之前博客平台的前端部分,所以没写文章。等过段时间完成了会把实现的内容以文章的方式分享出来。

docker 安装redis

如果你还不知道怎么安装docker,你可以看我之前的文章:坏蛋Dan:docker简单入门

直接上代码,我们这里准备使用的是redisJSON,也就是使用json格式存储。但是redisJSONdocker包已经废弃了,所以我们这里使用redis/redis-stack [1]

  • --requirepass:这个是用来设置密码的,千万别忘了这一点,不然你就等着被注入病毒吧。。。。(别问我怎么知道的。。。)

没有镜像会自动下载镜像。

这个时候你可以用docker ps -a或者docker logs [container-name]的方式看下是否正常安装。


rust 接入 redis

安装必要crate

这里我们需要接入两个包

另外还需要

虽然是json格式,但是传输还是得序列化。。。

下载完这仨


业务接入

然后我们来接入,咱这里是基于之前我们实现的博客平台来接入,如果你对这个项目还不了解,可以看下这个索引:坏蛋Dan:Rust + Rocket + PostgreSQL简单实现CRUD的restfulAPI后台——索引

不过GitHub上的代码好久没更新了,里面还有些问题和bug,但大体设计还是差不多的。

首先,我们的这个redis需要放到全局,这样就不需要每次都去创建。

而在rocket中,如果我们需要将某个东西放置全局,那么我们就需要用到State

回到我们之前的state文件夹中。

  • ROCKET_REDIS:这个是环境变量,我们需要手动在环境中添加,添加方式和我们数据库ROCKET_DB变量一样的。格式为:redis://:[password]@[host]:[port]/。比如你在powerShell中运行我们的服务器,那么你就需要设置环境变量:$ENV:ROCKET_REDIS = 'redis://:[password]@[host]:[port]/';

这里我们是创建一个redis池子,然后将它放置到全局。

接着我们去到main.rs中引入这个state

那么这里就接入了,我们接着找一处地方使用下api看能否正常连上。

这里就直接去到article/route.rs文件中找到获取文章列表的route

这里代码我就不详细讲了,就是分页查询。

然后我们来接入redis测试下能不能用。

首先我们在type文件夹下创建一个cache_type.rs的文件用于存储缓存的类型。

这里我们创建了一个CacheArticles tuple struct,用来存放我们缓存文章列表。然后我们需要给它实现FromRedisValue这个trait,为什么要这么做呢?因为我们需要告知redis我们需要它返回的数据类型(当然,还是我们手动实现的,没办法自动判断)。

这里我们还得给Article也实现FromRedisValue,因为外层可以实现的原则是内部一定都实现了~

  • from_redis_value:这是个function,用来简化对redis::Value的处理。我们可以稍微看下相关的源码

FromRedisValue

macro

这里有两个宏,用来自动实现FromRedisValue,但是很不巧,它没有被暴露出来,我们不能使用它。

from_redis_value则是FromRedisValue::from_redis_value的简写,能快速返回一个处理好的Value

而我们还需要将数据从字符串转换回我们的struct类型,所以我们需要手动去实现。即:

当然,也是需要申明类型的。

然后回到我们的route里。

我们先来实现cache部分。

这里我们在返回数据之前,将数据放到了redis里。

使用json_set的方法,这个方法会自动序列化,另外trait bound需要我们实现SerializeDeserialize这俩trait,我们前面定义的时候已经通过派生宏的方式实现了。

这个方法会自动将我们的struct序列化。

这里它实际上是调用的redis::cmd("JSON.SET")的方式来实现的,这种方案也可以,不过繁琐一些。

不过你现在应该很疑惑,为啥你的代码提示没有提示这个api,因为你没有引入几个重要的trait

这里我们需要手动引入AsyncCommands, JsonAsyncCommands, JsonCommands这仨,不然就没办法使用json相关的method

接着我们可以运行下代码看是否正常set

可以直接看下log,或者我们也可以通过redis-cli去查看是否set进去了。

回到我们的docker

然后我们使用AUTH [your password]的方式验证权限。

接着我们可以使用KEYS *的方式查看

然后如果需要看具体的,redis提供了六种类型。

我这里就不演示了(其实我遇到了问题,老是说类型有误。。。但是我存储的确实是字符串)

然后回到我们的代码中,我们接着来使用get来看下缓存的数据。

这里使用的是json_getmethod,注意这里需要声明类型CacheArticles

然后我们重新运行下我们的代码

可以看到正常拿到并且转换成我们的enum struct类型了。


总结

rust中接入redis的学习成本还是较高的。。。 我这边还是在摸索中,要完全接入还得思考很多问题。。

补充

实现FromRedisValue的注意事项

注意:实现FromRedisValue的目的是为了转换成我们json_get时需要的类型,所以这里不需要和json_set是同一个类型。

比如我们有这么一个struct People { name: String; age: u8 }。它将作为json_set的类型,即你存储到redis里的是{ name: 'xxx', age: xxx }

而在某个场景中,你只需要name这个字段,这个时候你就不能使用People这个类型了,而是应该使用String作为它返回的类型,比如这样

而非这样

FromRedisValue是用来处理返回数据的,所以如果你get的数据结构和set的不同,此时如果还使用同一个类型,就会导致报错。


如何获取某个path下的数组切片

由于一次性从缓存中将整个数组拿出来会临时占用大量的内存,所以最好的方式是拿必要的切片,这个时候我们也是可以使用json_get来实现的,比如:

注意这里的$.list[start:end:steps]$不可少,不然只会返回索引为start的元素。

不过这里还有个问题,那就是这里会导致不走我们的CacheArticleSlicefrom_redis_value,而是走的Article::from_redis_value,问题的原因可能是我们这里使用的是json_getmethod,所以它会认为只有一个。

所以这个时候我们需要修改下代码,避免涉及到Article类型

这里实现FromRedisValue的时候没有涉及Article,则不会走Article::from_redis_value

参考

  1. ^redis/redis-stack https://hub.docker.com/r/redis/redis-stack

发布于 2023-10-17 16:48・IP 属地广东