【大家好,我是爱干饭的猿,本文重点介绍Redis7 Spring Boot集成Redis,包括Jedis、lettuce、集成RedisTemplate、集群时一台master宕机,java报错的情况分析。
后续会继续分享Redis7和其他重要知识点总结,如果喜欢这篇文章,点个赞👍,关注一下吧】
上一篇文章:《【Redis7】Redis7 集群(重点:哈希槽分区)》
目录
🍖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初级篇分享到此,感谢大家观看!!!
如果你喜欢这篇文章,请点赞加关注吧,或者如果你对文章有什么困惑,可以私信我。
🏓🏓🏓