【大家好,我是爱干饭的猿,本文重点介绍Redis7 Spring Boot集成Redis,包括Jedis、lettuce、集成RedisTemplate、集群时一台master宕机,java报错的情况分析。
后续会继续分享Redis7和其他重要知识点总结,如果喜欢这篇文章,点个赞👍,关注一下吧】
上一篇文章:《【Redis7】Redis7 集群(重点:哈希槽分区)》
目录
🍖1. redis 配置文件
🍖2.Jedis
2.1 介绍
2.2 操作使用
🍖3. lettuce
3.1 介绍
3.2 Jedis和Lettuce的区别
3.3 操作使用
🍖4. 集成RedisTemplate - 推荐使用
4.1 连接单机
1. 操作使用
2. 测试
4.2 连接集群
1.正常启动
2. 测试
3. 一台master宕机,java报错
🍖1. redis 配置文件
- redis.conf配置文件,改完后确保生效,记得重启,记得重启
- 默认daemonize no 改为 daemonize yes
- 默认protected-mode yes 改为 protected-mode no
- 默认bind 127.0.0.1 改为 直接注释掉(默认bind 127.0.0.1只能本机访问)或改成本机IP地址,否则影响远程IP连接
- 添加redis密码 改为 requirepass 你自己设置的密码
🍖2.Jedis
2.1 介绍
Jedis Client 是Redis 官网推荐的一个面向 Java 客户端,库文件实现了对各类API进行封装调用。
2.2 操作使用
-
新建项目
-
pom.xm
<!--jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.1</version>
</dependency>
-
yml文件
server.port=7777
spring.application.name=redis7_study
- 业务类
package com.haomin.redis7_study.jedis_lettuce;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;
import java.util.*;
/**
* @author haomin
* @date 2023/04/22 11:32
**/
@Slf4j
public class JedisDemo {
public static void main(String[] args) {
Jedis jedis = new Jedis("**.***.***.***", 6379);
jedis.auth("***");
log.info("redis conn status:{}", "连接成功");
log.info("redis ping retvalue:{}", jedis.ping());
jedis.set("k1","v1");
jedis.set("k2","v2");
jedis.set("k3","v3");
Set<String> keys = jedis.keys("*");
for (String key : keys) {
System.out.println(key);
}
// String
jedis.mset("m1", "v1","m2","v2","m3","v3");
System.out.println(jedis.mget("m1", "m2","m3"));
// list
jedis.lpush("myList","v1","v2","v3","v4","v5");
System.out.println(jedis.lrange("myList", 0, -1));
// set
jedis.sadd("orders","111");
jedis.sadd("orders","222");
jedis.sadd("orders","333");
Set<String> set1 = jedis.smembers("orders");
for(Iterator iterator = set1.iterator(); iterator.hasNext();) {
String string = (String) iterator.next();
System.out.println(string);
}
jedis.srem("orders","222");
System.out.println(jedis.smembers("orders").size());
//hash
jedis.hset("hash1","userName","lisi");
System.out.println(jedis.hget("hash1","userName"));
Map<String,String> map = new HashMap<String,String>();
map.put("telphone","138xxxxxxxx");
map.put("address","atguigu");
map.put("email","zzyybs@126.com");//课后有问题请给我发邮件
jedis.hmset("hash2",map);
List<String> result = jedis.hmget("hash2", "telphone","email");
for (String element : result) {
System.out.println(element);
}
//zset
jedis.zadd("zset01",60d,"v1");
jedis.zadd("zset01",70d,"v2");
jedis.zadd("zset01",80d,"v3");
jedis.zadd("zset01",90d,"v4");
List<String> zset01 = jedis.zrange("zset01", 0, -1);
zset01.forEach(System.out::println);
}
}
🍖3. lettuce
3.1 介绍
Lettuce是一个Redis的Java驱动包,Lettuce翻译为生菜
3.2 Jedis和Lettuce的区别
jedis和Lettuce都是Redis的客户端,它们都可以连接Redis服务器,但是在SpringBoot2.0之后默认都是使用的Lettuce这个客户端连接Redis服务器。因为当使用Jedis客户端连接Redis服务器的时候,每个线程都要拿自己创建的Jedis实例去连接Redis客户端,当有很多个线程的时候,不仅开销大需要反复的创建关闭一个Jedis连接,而且也是线程不安全的,一个线程通过Jedis实例更改Redis服务器中的数据之后会影响另一个线程。
但是如果使用Lettuce这个客户端连接Redis服务器的时候,就不会出现上面的情况,Lettuce底层使用的是Netty,当有多个线程都需要连接Redis服务器的时候,可以保证只创建一个Lettuce连接,使所有的线程共享这一个Lettuce连接,这样可以减少创建关闭一个Lettuce连接时候的开销;而且这种方式也是线程安全的,不会出现一个线程通过Lettuce更改Redis服务器中的数据之后而影响另—个线程的情况。
3.3 操作使用
- pom.xml
<!--lettuce-->
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.2.1.RELEASE</version>
</dependency>
- 业务类
package com.haomin.redis7_study.jedis_lettuce;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.SortArgs;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author haomin
* @date 2023/04/22 16:39
**/
@Slf4j
public class LettuceDemo {
public static void main(String[] args) {
// 1.使用构建器链式编程来builder我们RedisURI
RedisURI uri = RedisURI.builder()
.redis("**.***.***.***")
.withPort(6379)
.withAuthentication("default", "***")
.build();
// 2.创建连接客户端
RedisClient client = RedisClient.create(uri);
StatefulRedisConnection conn = client.connect();
// 3.通过conn创建操f作的comnand
RedisCommands<String, String> commands = conn.sync();
//===================操作=======================
// keys
List<String> list = commands.keys("*");
for (String s : list) {
log.info("key:{}", s);
}
commands.set("k1","1111");
String s1 = commands.get("k1");
System.out.println("String s ==="+s1);
//list
commands.lpush("myList2", "v1","v2","v3");
List<String> list2 = commands.lrange("myList2", 0, -1);
for(String s : list2) {
System.out.println("list ssss==="+s);
}
//set
commands.sadd("mySet2", "v1","v2","v3");
Set<String> set = commands.smembers("mySet2");
for(String s : set) {
System.out.println("set ssss==="+s);
}
//hash
Map<String,String> map = new HashMap<>();
map.put("k1","111");
map.put("k2","222");
map.put("k3","333");
commands.hmset("myHash2", map);
Map<String,String> retMap = commands.hgetall("myHash2");
for(String k : retMap.keySet()) {
System.out.println("hash k="+k+" , v=="+retMap.get(k));
}
//zset
commands.zadd("myZset2", 100.0,"s1",110.0,"s2",90.0,"s3");
List<String> list3 = commands.zrange("myZset2",0,10);
for(String s : list3) {
System.out.println("zset ssss==="+s);
}
//sort
SortArgs sortArgs = new SortArgs();
sortArgs.alpha();
sortArgs.desc();
List<String> list4 = commands.sort("myList2",sortArgs);
for(String s : list4) {
System.out.println("sort ssss==="+s);
}
//===================操作=======================
//4. 关闭
conn.close();
client.shutdown();
}
}
🍖4. 集成RedisTemplate - 推荐使用
4.1 连接单机
1. 操作使用
- pom.xml
<!--SpringBoot与Redis整合依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--swagger2-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
- yml
server.port=7777
spring.application.name=redis7_study
# ========================logging=====================
logging.level.root=info
logging.level.com.atguigu.redis7=info
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger- %msg%n
logging.file.name=D:/mylogs2023/redis7_study.log
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger- %msg%n
# ========================swagger=====================
spring.swagger2.enabled=true
#在springboot2.6.X结合swagger2.9.X会提示documentationPluginsBootstrapper空指针异常,
#原因是在springboot2.6.X中将SpringMVC默认路径匹配策略从AntPathMatcher更改为PathPatternParser,
# 导致出错,解决办法是matching-strategy切换回之前ant_path_matcher
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
# ========================redis单机=====================
spring.redis.database=0
# 修改为自己真实IP
spring.redis.host=192.168.111.185
spring.redis.port=6379
spring.redis.password=111111
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
- 业务类
- config.redisConfig
package com.haomin.redis7_study.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author haomin
* @date 2023/04/22 17:28
**/
@Configuration
public class RedisConfig {
/**
* redis序列化的工具配置类,下面这个请一定开启配置
* 127.0.0.1:6379> keys *
* 1) "ord:102" 序列化过
* 2) "\xac\xed\x00\x05t\x00\aord:102" 野生,没有序列化过
* this.redisTemplate.opsForValue(); //提供了操作string类型的所有方法
* this.redisTemplate.opsForList(); // 提供了操作list类型的所有方法
* this.redisTemplate.opsForSet(); //提供了操作set的所有方法
* this.redisTemplate.opsForHash(); //提供了操作hash表的所有方法
* this.redisTemplate.opsForZSet(); //提供了操作zset的所有方法
*
* @param lettuceConnectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
//设置key序列化方式string
redisTemplate.setKeySerializer(new StringRedisSerializer());
//设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
- config.SwaggerConfig
package com.haomin.redis7_study.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* @author haomin
* @date 2023/04/22 17:28
**/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Value("${spring.swagger2.enabled}")
private Boolean enabled;
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(enabled)
.select()
.apis(RequestHandlerSelectors.basePackage("com.haomin.redis7_study")) //你自己的package
.paths(PathSelectors.any())
.build();
}
public ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("springboot利用swagger2构建api接口文档 " + "\t" + DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDateTime.now()))
.description("springboot+redis整合")
.version("1.0")
.termsOfServiceUrl("https://haomin.blog.csdn.net/")
.build();
}
}
- controller.OrderController
package com.haomin.redis7_study.controller;
import com.haomin.redis7_study.service.OrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author haomin
* @date 2023/04/22 17:52
**/
// 访问:http://localhost:7777/swagger-ui.html#/
@Api(tags = "订单接口")
@RestController
@Slf4j
public class OrderController
{
@Resource
private OrderService orderService;
@ApiOperation("新增订单")
@RequestMapping(value = "/order/add",method = RequestMethod.POST)
public void addOrder()
{
orderService.addOrder();
}
@ApiOperation("按orderId查订单信息")
@RequestMapping(value = "/order/{id}", method = RequestMethod.GET)
public String findUserById(@PathVariable Integer id)
{
return orderService.getOrderById(id);
}
}
- service.OrderService
package com.haomin.redis7_study.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
/**
* @author haomin
* @date 2023/04/22 17:28
**/
@Service
@Slf4j
public class OrderService {
public static final String ORDER_KEY = "order:";
@Resource
private RedisTemplate redisTemplate;
public void addOrder() {
int keyId = ThreadLocalRandom.current().nextInt(1000) + 1;
String orderNo = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(ORDER_KEY + keyId, "京东订单" + orderNo);
log.info("=====>编号" + keyId + "的订单流水生成:{}", orderNo);
}
public String getOrderById(Integer id) {
return (String) redisTemplate.opsForValue().get(ORDER_KEY + id);
}
}
2. 测试
访问:访问:http://localhost:7777/swagger-ui.html#/
-
如果使用RedisTemplate,推荐序列化用StringRedisSerializer,默认使用的是JdkSerializationRedisSerializer,存入Redis会出现乱码问题,查询非常不方便
4.2 连接集群
1.正常启动
-
启动前面配的集群
-
改写YML(注意IP和端口)
# ========================redis集群=====================
spring.redis.password=111111
# 获取失败 最大重定向次数
spring.redis.cluster.max-redirects=3
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
spring.redis.cluster.nodes=***.***.***.***:6381,***.***.***.***:6382 (主机:port)
2. 测试
访问:访问:http://localhost:7777/swagger-ui.html#/
3. 一台master宕机,java报错
-
让master-6381宕机,shutdown
-
查看集群信息 ,看slave是否上位 Cluster nodes
-
我们客户端再次读写
-
原因是因为SpringBoot客户端没有动态感知到RedisCluster的最新集群信息
-
导致这个的原因是
- Spring Boot 2,Redis默认的是 Lettuce
- 当Redis集群节点发生变化后,Lettuce默认是不会刷新节点拓扑
-
解决方法:
-
排除Lettuce采用jedis(不推荐)
-
-
排除Lettuce采用jedis(不推荐)
-
刷新节点结群拓扑和动态感应(推荐)
-
修改pom.xml
-
# ========================redis集群===================== spring.redis.password=111111 # 获取失败 最大重定向次数 spring.redis.cluster.max-redirects=3 spring.redis.lettuce.pool.max-active=8 spring.redis.lettuce.pool.max-wait=-1ms spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.min-idle=0 # ========================新增===================== #支持集群拓扑动态感应刷新,自适应拓扑刷新是否使用所有可用的更新,默认false关闭 spring.redis.lettuce.cluster.refresh.adaptive=true #定时刷新 spring.redis.lettuce.cluster.refresh.period=2000 spring.redis.cluster.nodes=***.***.***.***:6381,***.***.***.***:6382 (主机:port)
-
-
reids初级篇分享到此,感谢大家观看!!!
如果你喜欢这篇文章,请点赞加关注吧,或者如果你对文章有什么困惑,可以私信我。
🏓🏓🏓