【苍穹外卖】项目实战Day05
? 本文由 程序喵正在路上 原创,CSDN首发!
? 系列专栏:苍穹外卖项目实战
? 首发时间:2024年5月5日
? 欢迎关注?点赞?收藏?留言?
目录
- Redis入门
- Redis简介
- Redis下载与安装
- Redis服务启动与停止
- Redis数据类型
- 5种常用数据类型介绍
- 各种数据类型的特点
- Redis常用命令
- 字符串操作命令
- 哈希操作命令
- 列表操作命令
- 集合操作命令
- 有序集合操作命令
- 通用命令
- 在Java中操作Redis
- Redis的Java客户端
- Spring Data Redis使用方式
- 操作 String 类型的数据
- 操作 Hash 类型的数据
- 操作 List 类型的数据
- 操作 Set 类型的数据
- 操作 ZSet 类型的数据
- 通用命令操作
- 店铺营业状态设置
- 需求分析和设计
- 代码开发
- 功能测试
Redis入门
Redis简介
Redis 是一个基于内存的 key-value 结构数据库。
- 基于内存存储,读写性能高
- 适合存储热点数据(热点商品、资讯、新闻)
- 企业应用广泛
- 官网:https://redis.io
- 中文网:https://www.redis.net.cn/
Redis下载与安装
Redis 安装包分为 Windows 版和 Linux 版:
- Windows 版下载地址:https://github.com/microsoftarchive/redis/releases
- Linux 版下载地址: https://download.redis.io/releases/
在资料中已经准备好了 Windows 版本的:
Redis 的 Windows 版属于绿色软件,直接解压即可使用,解压后目录结构如下:
Redis服务启动与停止
服务启动命令:
redis-server.exe redis.windows.conf
在 redis 解压目录的地址栏,输入 cmd 后回车,输入启动命令:
- Redis 服务默认端口号为 6379 ,通过快捷键 Ctrl + c 可以停止 Redis 服务
客户端连接命令:
redis-cli.exe
在服务端启动的前提下,再打开一个命令行窗口,输入客户端连接命令,如下图表示成功:
- 客户端退出命令:
exit
通过
redis-cli.exe
命令默认连接的是本地的redis
服务,并且使用默认6379
端口。我们也可以通过指定如下参数连接:-h
:ip地址-p
:端口号-a
:密码(如果需要)
通过修改 redis.windows.conf,我们可以设置 redis 的服务密码,redis 的客户登录并不需要用户名,但是需要密码
打开 redis.windows.conf,Ctrl + F 找到图中内容,然后设置一下你的 redis 服务密码:
注意:
- 修改密码后需要重启 Redis 服务才能生效
- Redis 配置文件中的
#
表示注释
用命令来操作多少还是有点麻烦,下面我们介绍一个 Redis 客户端图形工具:
安装后,打开:
连接后,我们可以看到服务端的信息:
Redis数据类型
5种常用数据类型介绍
Redis 存储的是 key-value 结构的数据,其中 key 是字符串类型,value 有 5 种常用的数据类型:
- 字符串
string
- 哈希
hash
- 列表
list
- 集合
set
- 有序集合
sorted set / zset
各种数据类型的特点
- 字符串 (
string
):普通字符串,Redis 中最简单的数据类型 - 哈希 (
hash
):也叫散列,类似于 Java 中的 HashMap 结构,适合存储对象 - 列表 (
list
):按照插入顺序排序,可以有重复元素,类似于 Java 中的 LinkedList - 集合 (
set
):无序集合,没有重复元素,类似于 Java 中的 HashSet - 有序集合 (
sorted set / zset
):集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素
Redis常用命令
字符串操作命令
Redis 字符串类型常用命令:
SET key value
:设置指定 key 的值GET key
:获取指定 key 的值SETEX key seconds value
:设置指定 key 的值,并将 key 的过期时间设为 seconds 秒,经典应用场景为短信验证码SETNX key value
:只有当 key 不存在时才设置 key 的值
ps:命令不区分大小写
打开图形界面:
SETEX key seconds value
: 时间到,key 会自动销毁
哈希操作命令
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象,常用命令:
HSET key field value
:将哈希表 key 中的字段 field 的值设为 valueHGET key field
:获取存储在哈希表中指定字段的值HDEL key field
:删除存储在哈希表中的指定字段HKEYS key
:获取哈希表中所有字段HVALS key
:获取哈希表中所有值
列表操作命令
Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令:
LPUSH key value1 [value2]
:将一个或多个值插入到列表头部 (左边)LRANGE key start stop
:获取列表指定范围内的元素RPOP key
:移除并获取列表最后一个元素 (右边)LLEN key
:获取列表长度
集合操作命令
Redis set 是 string 类型的无序集合。集合成员是唯一的,集合中不能出现重复的数据,常用命令:
SADD key member1 [member2]
:向集合添加一个或多个成员SMEMBERS key
:返回集合中的所有成员SCARD key
:获取集合的成员数SINTER key1 [key2]
:返回给定所有集合的交集SUNION key1 [key2]
:返回所有给定集合的并集SREM key member1 [member2]
:删除集合中一个或多个成员
有序集合操作命令
Redis 有序集合是 string 类型元素的集合,且不允许有重复成员。每个元素都会关联一个 double 类型的分数。常用命令:
ZADD key score1 member1 [score2 member2]
:向有序集合添加一个或多个成员ZRANGE key start stop [WITHSCORES]
:通过索引区间返回有序集合中指定区间内的成员ZINCRBY key increment member
:有序集合中对指定成员的分数加上增量 incrementZREM key member [member ...]
:移除有序集合中的一个或多个成员
通用命令
Redis 的通用命令是不分数据类型的,都可以使用的命令,因为都是在操作 key:
KEYS pattern
:查找所有符合给定模式 (pattern) 的 keyEXISTS key
:检查给定 key 是否存在TYPE key
:返回 key 所储存的值的类型DEL key
:该命令用于在 key 存在时删除 key
在Java中操作Redis
Redis的Java客户端
Redis 的 Java 客户端很多,常用的几种:
- Jedis
- Lettuce
- Spring Data Redis
Spring Data Redis 是 Spring 的一部分,对 Redis 底层开发包进行了高度封装。在 Spring 项目中,可以使用 Spring Data Redis 来简化操作。
Spring Data Redis使用方式
操作步骤:
- 导入 Spring Data Redis 的 maven 坐标
- 配置 Redis 数据源
- 编写配置类,创建 RedisTemplate 对象
- 通过 RedisTemplate 对象操作 Redis
导入 Spring Data Redis 的 maven 坐标(项目中已导入)
配置 Redis 数据源
在配置文件中配置 redis 数据源,不要将数据写死,这样方便我们项目环境的切换。Redis 默认会给我们创建 16 个数据库,每个数据库之间的数据是隔离开的,默认使用 0 号数据库,我们也可以自己设置。
编写配置类,创建 RedisTemplate 对象
新建配置类 RedisConfiguration:
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@Slf4j
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
log.info("开始创建redis模板对象...");
RedisTemplate redisTemplate = new RedisTemplate();
//设置redis的连接工厂对象
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置redis key的序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
通过 RedisTemplate 对象操作 Redis
在测试包下新建测试类 SpringDataRedisTest:
注入 RedisTemplate 对象,测试对象是否创建成功:
RedisTemplate 针对大量 api 进行了归类封装,将同一数据类型的操作封装为对应的 Operation 接口,具体分类如下:
- ValueOperations:string 数据操作
- HashOperations:hash 类型的数据操作
- ListOperations:list 类型的数据操作
- SetOperations:set 类型数据操作
- ZSetOperations:zset 类型数据操作
操作 String 类型的数据
/**
* 操作String类型数据
*/
@Test
public void testString() {
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set("city", "北京"); //set
String city = (String) valueOperations.get("city"); //get
System.out.println(city);
valueOperations.set("code", "123", 3, TimeUnit.MINUTES); //setex
valueOperations.setIfAbsent("lock", "1"); //setnx
valueOperations.setIfAbsent("lock", "2");
}
执行这个测试方法,然后查看 1 号数据库(前面我们设置使用的是 1 号数据库):
可以看到 value 部分好像是乱码的,而 key 是正常的,这时因为在配置类中我们给 key 设置了序列化器:
操作 Hash 类型的数据
/**
* 操作Hash类型的数据
*/
@Test
public void testHash() {
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.put("user", "name", "Tom"); //hset
hashOperations.put("user", "age", "20");
String name = (String) hashOperations.get("user", "name"); //hget
System.out.println(name);
Set keys = hashOperations.keys("user"); //hkeys
System.out.println(keys);
List values = hashOperations.values("user"); //hvals
System.out.println(values);
hashOperations.delete("user", "age"); //hdel
}
操作 List 类型的数据
/**
* 操作List类型的数据
*/
@Test
public void testList() {
ListOperations listOperations = redisTemplate.opsForList();
listOperations.leftPushAll("myList", "a", "b", "c"); //lpush
listOperations.leftPush("myList", "d"); //lpush
List myList = listOperations.range("myList", 0, -1); //lrange
System.out.println(myList);
listOperations.rightPop("myList"); //rpop
Long size = listOperations.size("myList"); //llen
System.out.println(size);
}
操作 Set 类型的数据
/**
* 操作Set类型的数据
*/
@Test
public void testSet() {
SetOperations setOperations = redisTemplate.opsForSet();
setOperations.add("set1", "a", "b", "c", "d"); //sadd
setOperations.add("set2", "b", "c", "d", "e");
Set members = setOperations.members("set1"); //smembers
System.out.println(members);
Long size = setOperations.size("set1"); //scard
System.out.println(size);
Set intersect = setOperations.intersect("set1", "set2"); //sinter
System.out.println(intersect);
Set union = setOperations.union("set1", "set2"); //sunion
System.out.println(union);
setOperations.remove("set1", "a", "b"); //srem
}
操作 ZSet 类型的数据
/**
* 操作ZSet类型的数据
*/
@Test
public void testZSet() {
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
zSetOperations.add("zset1", "a", 1); //zadd
zSetOperations.add("zset1", "b", 2);
zSetOperations.add("zset1", "c", 3);
Set zset1 = zSetOperations.range("zset1", 0, -1); //zrange
System.out.println(zset1);
zSetOperations.incrementScore("zset1", "c", 10); //zincrby
zSetOperations.remove("zset1", "a"); //zrem
}
通用命令操作
/**
* 通用命令操作
*/
@Test
public void testCommon() {
Set keys = redisTemplate.keys("*"); //keys
System.out.println(keys);
Boolean name = redisTemplate.hasKey("name"); //exists
System.out.println("name:" + name);
Boolean set1 = redisTemplate.hasKey("set1");
System.out.println("set1:" + set1);
for (Object key : keys) {
DataType type = redisTemplate.type(key); //type
System.out.println(key + ": " + type.name());
}
redisTemplate.delete("myList"); //del
}
店铺营业状态设置
需求分析和设计
产品原型:
由于本项目约定:
- 管理端发出的请求,统一使用
/admin
作为前缀 - 用户端发出的请求,统一使用
/user
作为前缀
所以查询营业状态这个操作我们需要设计成两个接口。当然,这样设计也是有好处的,我们可以很容易地判断这个请求是来自用户端还是管理端。
接口设计:
设置营业状态
管理端查询营业状态
用户端查询营业状态
营业状态数据存储方式:基于 Redis 的字符串来进行存储。
约定:1 表示营业;0 表示打烊。
代码开发
先把前面写的这个测试类注释掉,不然会影响我们项目的启动速度:
管理端两个接口的开发
新建控制类 ShopController,专门用来存放店铺相关接口,然后开发设置店铺营业状态和管理端查询营业状态两个接口:
import com.sky.result.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
@RestController("adminShopController") //与用户端的ShopController区分开
@RequestMapping("/admin/shop")
@Slf4j
@Api("店铺操作相关接口")
public class ShopController {
public static final String KEY = "SHOP_STATUS";
@Autowired
private RedisTemplate redisTemplate;
/**
* 设置店铺的营业状态
*
* @param status
* @return
*/
@PutMapping("/{status}")
@ApiOperation("设置店铺的营业状态")
public Result setStatus(@PathVariable Integer status) {
log.info("设置店铺的营业状态为:{}", status == 1 ? "营业中" : "打烊");
redisTemplate.opsForValue().set(KEY, status);
return Result.success();
}
/**
* 获取店铺的营业状态
*
* @return
*/
@GetMapping("/status")
@ApiOperation("获取店铺的营业状态")
public Result<Integer> getStatus() {
Integer status = (Integer) redisTemplate.opsForValue().get(KEY);
log.info("获取到店铺的营业状态为:{}", status == 1 ? "营业中" : "打烊");
return Result.success(status);
}
}
用户端查询营业状态接口开发
其实,用户端查询营业状态这个接口的逻辑和管理端的一模一样,所以我们可以先在 controller 包下新建 user 包用来存放用户端的控制类,然后将管理端的 ShopController 复制一份到 user 包下,再稍加修改即可:
功能测试
可以进行接口测试,也可以进行前后端联调测试。
在进行接口测试的时候,我们发现,两个查询营业状态的接口被放在同一个菜单下,如果我们想把它们区分开发,也就是分为放在不同的目录下,应该如何操作呢?
(ps:两个菜单名为英文的不知道什么原因,代码没有错误)
之所以它们会在同一目录下,是因为我们在配置类中生成接口文档部分的代码只指定了扫描 controller 这个包中的内容,所以将它们都扫描进去了。
我们可以再写一个方法单独扫描 user 包,然后原先的 docket 方法扫描 admin 包:
重启服务,再查看接口文档,发现只剩下一个接口,因为我们现在是在用户端:
我们可以点击切换到管理端:
还没有评论,来说两句吧...