访问链接:https://github.com/redis/jedis
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.0</version>
</dependency>
bind 127.0.0.1
;2)redis.conf配置文件中设置protected-mode no
;3)重启Redis服务; private static String host="Redis服务器IP地址";
private static int port=6379;
public static void main(String[] args) {
// 创建Jedis对象
Jedis jedis = new Jedis(host, port);
// 测试是否连通
String pong = jedis.ping();
System.out.println(pong);
jedis.close();
}
分析:
1)用户在前端输入手机号,点击发送验证码将手机号传送到后端接收;
2)后端接收手机号,1)记录手机号,为其绑定发送次数为3,每发送一次,次数减1;2)发送并保存验证码,验证码有过期时间;
3)用户输入验证码,后端在验证码有效时间内核对验证码是否正确;
代码:
/**
* Author:NorthSmile
* Create:2023/6/2 16:55
* 模拟手机验证码
*/
public class AuthCode {
/**
* 获取Jedis对象
*/
public Jedis getJedis(){
String host="192.168.139.100";
int port=6379;
return new Jedis(host,port);
}
/**
* 1.获取并保存电话号
* 2.为手机号绑定发送次数
* 3.发送验证码;
* 4.验证码具有过期时间;
*/
public String verifyCode(String phone){
Jedis jedis = getJedis();
// 获取手机号
// 获取验证码
String code=getCode();
System.out.println(code);
// 手机号是否存在
if (!jedis.exists(phone)){
String num="3";
jedis.setex(phone,24*60*60,num);
// 发送验证码,为其绑定过期时间
jedis.setex("authCode",120,code);
// 发送次数减1
jedis.decr(phone);
}else{
// 判断是否还有发送次数
if (Integer.parseInt(jedis.get(phone))<=0){
System.out.println("今日发送次数已经用光,请明天再尝试!");
jedis.close();
}else{
// 发送验证码,为其绑定过期时间
jedis.setex("authCode",120,code);
// 发送次数减1
jedis.decr(phone);
}
}
return code;
}
/**
* 生成6位随机数
* @return
*/
private String getCode() {
// 生成6位随机数字
Random random = new Random();
StringBuilder code = new StringBuilder();
for (int i = 0; i < 6; i++) {
code.append(random.nextInt(10));
}
return code.toString();
}
/**
* 验证码校验
* @param phone
* @param code
* @return
*/
private void checkCode(String phone,String code){
Jedis jedis = getJedis();
// 确保该电话号存在且验证码没有超时
if (jedis.exists(phone)){
if (jedis.exists("authCode")) {
String authCode = jedis.get("authCode");
if (authCode.equals(code)) {
System.out.println("验证成功");
} else {
System.out.println("输入验证码有误");
}
}else{
System.out.println("请先发送验证码");
}
}else{
System.out.println("输入电话有误");
}
jedis.close();
}
@Test
public void testVerify() {
String phone="12345";
// String code=verifyCode(phone);
checkCode(phone,"351225");
}
}
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring2.X集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>
#Redis服务器地址
spring.redis.host=Redis服务器IP地址
#Redis服务器连接端口
spring.redis.port=6379
#Redis数据库索引(默认为0)
spring.redis.database= 0
#连接超时时间(毫秒)
spring.redis.timeout=1800000
#连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=20
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-wait=-1
#连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=5
#连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=0
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
RedisTestController中添加测试方法
@RestController
@RequestMapping("/redisTest")
public class RedisTestController {
@Autowired
private RedisTemplate redisTemplate;
@GetMapping
public String testRedis() {
//设置值到redis
redisTemplate.opsForValue().set("name","lucy");
//从redis获取值
String name = (String)redisTemplate.opsForValue().get("name");
return name;
}
}
Redis 提供了2个不同形式的持久化方式:
介绍:在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是Snapshot快照,它恢复时是将快照文件直接读到内存里;
持久化流程:Redis会单独创建(fork)一个子进程来进行持久化 1)将数据写入到一个临时文件中 ;2)持久化过程结束以后,使用该临时文件替换上次持久化好的文件;
优缺点:
1)优点:整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能;如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效;
2)缺点:最后一次持久化后的数据可能丢失;
Fork介绍:
1)Fork的作用是复制一个与当前进程一样的进程:新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程;
2)在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术”;
3)一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程;
config get dir
查询rdb文件的目录;save
:save时只管保存,其它不管,全部阻塞,手动保存,不建议;save 秒钟 写操作次数
;bgsave
:Redis会在后台异步进行快照操作, 快照同时还可以响应客户端请求;lastsave
命令获取最后一次成功执行快照的时间;config get dir
查询rdb文件的目录,默认为Redis启动目录;介绍:以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作;
AOF默认不开启,需要在redis.conf文件中设置appendonly yes
;
AOF持久化文件名默认为 appendonly.aof;
AOF文件的保存路径,同RDB的路径一致,默认为服务启动的当前目录;
AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失);
持久化流程:
1)客户端的请求写命令会被append追加到AOF缓冲区内;
2)AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中;
3)AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;
4)Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的;
appendfsync always
始终同步,每次Redis的写入都会立刻记入日志;性能较差但数据完整性比较好;appendfsync everysec
每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失;appendfsync no
redis不主动进行同步,把同步时机交给操作系统;bgrewriteaof
手动触发重写机制;auto-aof-rewrite-percentage
:设置重写的基准值,文件达到100%时开始重写(文件是原来重写后文件的2倍时触发);auto-aof-rewrite-min-size
:设置重写的基准值,最小文件64MB,达到这个值开始重写;no-appendfsync-on-rewrite=yes
,不写入aof文件只写入缓存,用户请求不会阻塞,但是在这段时间如果宕机会丢失这段时间的缓存数据。(降低数据安全性,提高性能);no-appendfsync-on-rewrite=no
, 还是会把数据往磁盘里刷,但是遇到重写操作,可能会发生阻塞。(数据安全,但是性能降低);config get dir
查询rdb文件的目录,默认为Redis启动目录;redis-check-aof --fix appendonly.aof
命令修复发生异常的appendonly.aof文件;参考资料:《尚硅谷》