当前位置:   article > 正文

Spring Boot 集成Redis_springboot集成redis

springboot集成redis

总体概述

java要通过程序访问redis服务器。需要一个中间件或驱动包,初代使用的就是jedis,Jedis Client是Redis官网推荐的一个面向java客户端,库文件实现了对各类API进行封装调用,随着出现一些问题,如线程池不安全等,就出现了lettuce,是一个Redis的java驱动包,是对jedis的优化,之后出现了redisTemplate,使用spring整合了redis,redisTemplate底层包含了lettuce。

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服务器中的数据之后而影响另一个线程的情况。

本地java连接redis常见问题

  • bind配置请注释掉保护模式设置为no
  • Linux系统的防火墙设置
  • redis服务器的IP地址和密码是否正确忘记写访问redis的服务端口号和auth密码

集成Jedis

建Module:redis7_study
改POM

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>com.atguigu.redis7</groupId>
  7. <artifactId>redis7_study</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <parent>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-parent</artifactId>
  12. <version>2.3.4.RELEASE</version>
  13. <relativePath/>
  14. </parent>
  15. <properties>
  16. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  17. <maven.compiler.source>1.8</maven.compiler.source>
  18. <maven.compiler.target>1.8</maven.compiler.target>
  19. <junit.version>4.12</junit.version>
  20. <log4j.version>1.2.17</log4j.version>
  21. <lombok.version>1.16.18</lombok.version>
  22. </properties>
  23. <dependencies>
  24. <!--SpringBoot通用依赖模块-->
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-web</artifactId>
  28. </dependency>
  29. <!--jedis-->
  30. <dependency>
  31. <groupId>redis.clients</groupId>
  32. <artifactId>jedis</artifactId>
  33. <version>4.3.1</version>
  34. </dependency>
  35. <!--通用基础配置-->
  36. <dependency>
  37. <groupId>junit</groupId>
  38. <artifactId>junit</artifactId>
  39. <version>${junit.version}</version>
  40. </dependency>
  41. <dependency>
  42. <groupId>org.springframework.boot</groupId>
  43. <artifactId>spring-boot-starter-test</artifactId>
  44. <scope>test</scope>
  45. </dependency>
  46. <dependency>
  47. <groupId>log4j</groupId>
  48. <artifactId>log4j</artifactId>
  49. <version>${log4j.version}</version>
  50. </dependency>
  51. <dependency>
  52. <groupId>org.projectlombok</groupId>
  53. <artifactId>lombok</artifactId>
  54. <version>${lombok.version}</version>
  55. <optional>true</optional>
  56. </dependency>
  57. </dependencies>
  58. <build>
  59. <plugins>
  60. <plugin>
  61. <groupId>org.springframework.boot</groupId>
  62. <artifactId>spring-boot-maven-plugin</artifactId>
  63. </plugin>
  64. </plugins>
  65. </build>
  66. </project>

写YML

  1. server:
  2. port: 7777
  3. spring:
  4. application:
  5. name: redis7_study

主启动

  1. import org.springframework.boot.SpringApplication;
  2. import org.springframework.boot.autoconfigure.SpringBootApplication;
  3. @SpringBootApplication
  4. public class Redis7Study7777
  5. {
  6. public static void main(String[] args)
  7. {
  8. SpringApplication.run(Redis7Study7777.class,args);
  9. }
  10. }

业务类

入门案例

  1. import lombok.extern.slf4j.Slf4j;
  2. import redis.clients.jedis.Jedis;
  3. @Slf4j
  4. public class JedisDemo {
  5. public static void main(String[] args) {
  6. Jedis jedis = new Jedis("192.168.67.100", 6379);
  7. jedis.auth("123456");
  8. log.info("redis conn status:{}","连接成功");
  9. log.info("redis ping retvalue:{}",jedis.ping());
  10. jedis.set("k1","jedis");
  11. log.info("k1 value:{}",jedis.get("k1"));
  12. }
  13. }

 效果:

 我的redis并不是集群模式,需要修改replica-read-only属性为no,使这个实例可读可写就可以了。

 

常用5大数据类型的使用

  1. import lombok.extern.slf4j.Slf4j;
  2. import redis.clients.jedis.Jedis;
  3. import java.util.*;
  4. @Slf4j
  5. public class JedisDemo {
  6. public static void main(String[] args) {
  7. Jedis jedis = new Jedis("192.168.67.100", 6379);
  8. jedis.auth("123456");
  9. //key
  10. Set<String> keys = jedis.keys("*");
  11. for (Iterator iterator= keys.iterator();iterator.hasNext();){
  12. String key = (String) iterator.next();
  13. System.out.println(key);
  14. }
  15. System.out.println("jedis.exists:"+jedis.exists("k1"));
  16. System.out.println(jedis.ttl("k1"));
  17. //String
  18. jedis.append("k6","append_v6");
  19. System.out.println(jedis.get("k6"));
  20. jedis.set("k2","k2_redis");
  21. System.out.println(jedis.get("k2"));
  22. jedis.mset("k3","v3","k4","v4","k5","v5");
  23. System.out.println(jedis.mget("k3", "k4", "k5"));
  24. //list
  25. jedis.lpush("mylist","1","2","3","4","5");
  26. List<String> list = jedis.lrange("mylist", 0, -1);
  27. for (String element : list) {
  28. System.out.println(element);
  29. }
  30. //set
  31. jedis.sadd("orders","jd001");//添加元素
  32. jedis.sadd("orders","jd002");
  33. jedis.sadd("orders","jd003");
  34. Set<String> set1 = jedis.smembers("orders");//获取所有元素
  35. Iterator iterator=set1.iterator();
  36. if (iterator.hasNext()){
  37. String order= (String) iterator.next();
  38. System.out.println(order);
  39. }
  40. jedis.srem("orders","jd002");//删除元素
  41. System.out.println(jedis.smembers("orders").size());
  42. //hash
  43. jedis.hset("hash1","userName","lisi");//添加hash类型key的field的值
  44. System.out.println(jedis.hget("hash1", "userName"));
  45. Map<String, String> map = new HashMap<>();
  46. map.put("telphone","138xxxxxxxx");
  47. map.put("address","atguigu");
  48. map.put("email","123@qq.com");
  49. jedis.hmset("hash2",map);
  50. List<String> result = jedis.hmget("hash2", "telphone", "email");
  51. for (String element : result) {
  52. System.out.println(element);
  53. }
  54. //zset
  55. jedis.zadd("zset1",60d,"v1");
  56. jedis.zadd("zset1",70d,"v2");
  57. jedis.zadd("zset1",80d,"v3");
  58. jedis.zadd("zset1",90d,"v4");
  59. List<String> zset1 = jedis.zrange("zset1", 0, -1);
  60. zset1.forEach(System.out::println);
  61. }
  62. }

集成lettuce

改pom(添加依赖)

  1. <!--lettuce-->
  2. <dependency>
  3. <groupId>io.lettuce</groupId>
  4. <artifactId>lettuce-core</artifactId>
  5. <version>6.2.1.RELEASE</version>
  6. </dependency>

业务类

  1. import io.lettuce.core.RedisClient;
  2. import io.lettuce.core.SortArgs;
  3. import io.lettuce.core.api.StatefulRedisConnection;
  4. import io.lettuce.core.api.sync.RedisCommands;
  5. import lombok.extern.slf4j.Slf4j;
  6. import io.lettuce.core.RedisURI;
  7. import java.util.HashMap;
  8. import java.util.List;
  9. import java.util.Map;
  10. import java.util.Set;
  11. @Slf4j
  12. public class lettuceDemo {
  13. public static void main(String[] args) {
  14. //使用构建器构建RedisURI.builder(链式编程)
  15. RedisURI uri = RedisURI.builder()
  16. .redis("192.168.67.100")
  17. .withPort(6379)
  18. .withAuthentication("default","123456")
  19. .build();
  20. //创建连接客户端
  21. RedisClient client = RedisClient.create(uri);
  22. StatefulRedisConnection connect = client.connect();
  23. //操作命令api
  24. RedisCommands commands = connect.sync();
  25. //keys
  26. List list = commands.keys("*");
  27. list.forEach(System.out::println);
  28. //String
  29. commands.set("001","111");
  30. System.out.println(commands.get("001"));
  31. //list
  32. commands.lpush("mylist2","a","b","c");
  33. List list2 = commands.lrange("mylist2", 0, -1);
  34. list2.forEach(System.out::println);
  35. //set
  36. commands.sadd("mySet2","d","e","f");
  37. Set set2 = commands.smembers("mySet2");
  38. set2.forEach(System.out::println);
  39. //hash
  40. Map<String,String> map = new HashMap<>();
  41. map.put("k1","138xxxxx");
  42. map.put("k2","atguigu");
  43. map.put("k3","123@qq.com");
  44. commands.hmset("myhash2",map);
  45. Map<String,String> hashMap2 = commands.hgetall("myhash2");
  46. for (String k : hashMap2.keySet()) {
  47. System.out.println("hash k="+k+",v="+hashMap2.get(k));
  48. }
  49. //zset
  50. commands.zadd("myzset2",100.0,"z1",110.0,"z2",120.0,"z3");
  51. List<String> list3 = commands.zrange("myzset2", 0, 10);
  52. list3.forEach(System.out::println);
  53. //sort
  54. SortArgs sortArgs = new SortArgs();
  55. //SortArgs是Lettuce提供的一个用于构建SORT命令参数的类。SORT命令可以对存储在Redis中的列表、集合和有序集合进行排序。
  56. sortArgs.alpha();//对列表进行按字母顺序排序
  57. sortArgs.desc();//降序排序
  58. List list4 = commands.sort("mylist2", sortArgs);
  59. list4.forEach(System.out::println);
  60. //关闭
  61. connect.close();
  62. client.shutdown();
  63. }
  64. }

结果:

 Spring boot的版本问题将版本换为如下就可以了

集成redisTemplate-推荐使用

连接单机

改POM(添加依赖)

  1. <!--SpringBoot与Redis整合依赖-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-data-redis</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.apache.commons</groupId>
  8. <artifactId>commons-pool2</artifactId>
  9. </dependency>
  10. <!--swagger2-->
  11. <dependency>
  12. <groupId>io.springfox</groupId>
  13. <artifactId>springfox-swagger2</artifactId>
  14. <version>2.9.2</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>io.springfox</groupId>
  18. <artifactId>springfox-swagger-ui</artifactId>
  19. <version>2.9.2</version>
  20. </dependency>

 写YML

  1. server:
  2. port: 7777
  3. spring:
  4. application:
  5. name: redis7_study
  6. redis:
  7. database: 0
  8. host: 192.168.67.100
  9. port: 6379
  10. password: 123456
  11. lettuce:
  12. pool:
  13. max-active: 8
  14. max-wait: -1ms
  15. max-idle: 8
  16. min-idle: 0
  17. swagger2:
  18. enabled: true
  19. mvc:
  20. pathmatch:
  21. matching-strategy: ant_path_matcher

 #springboot2.6.X结合swagger2.9.X会提示documentationPluginsBootstrapper空指针异常,
#原因是在springboot2.6.X中将SpringMVC默认路径匹配策略从AntPathMatcher更改为PathPatternParser
导致出错,解决办法是matching-strategy切换回之前ant_path_matcher

业务类

配置类

swaggerconfig

  1. package com.atguigu.redis7.config;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import springfox.documentation.builders.ApiInfoBuilder;
  6. import springfox.documentation.builders.PathSelectors;
  7. import springfox.documentation.builders.RequestHandlerSelectors;
  8. import springfox.documentation.service.ApiInfo;
  9. import springfox.documentation.spi.DocumentationType;
  10. import springfox.documentation.spring.web.plugins.Docket;
  11. import springfox.documentation.swagger2.annotations.EnableSwagger2;
  12. import java.time.LocalDateTime;
  13. import java.time.format.DateTimeFormatter;
  14. @Configuration
  15. @EnableSwagger2
  16. public class SwaggerConfig
  17. {
  18. @Value("${spring.swagger2.enabled}")
  19. private Boolean enabled;
  20. @Bean
  21. public Docket createRestApi() {
  22. return new Docket(DocumentationType.SWAGGER_2)
  23. .apiInfo(apiInfo())
  24. .enable(enabled)
  25. .select()
  26. .apis(RequestHandlerSelectors.basePackage("com.atguigu.redis7")) //你自己的package
  27. .paths(PathSelectors.any())
  28. .build();
  29. }
  30. public ApiInfo apiInfo() {
  31. return new ApiInfoBuilder()
  32. .title("springboot利用swagger2构建api接口文档 "+"\t"+ DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDateTime.now()))
  33. .description("springboot+redis整合")
  34. .version("1.0")
  35. .build();
  36. }
  37. }
service
  1. package com.atguigu.redis7.service;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.data.redis.core.RedisTemplate;
  4. import org.springframework.stereotype.Service;
  5. import javax.annotation.Resource;
  6. import java.util.UUID;
  7. import java.util.concurrent.ThreadLocalRandom;
  8. @Service
  9. @Slf4j
  10. public class OrderService {
  11. public static final String ORDER_KEY = "order:";//前缀,这里使用前缀+随机keyId组合订单key
  12. @Resource
  13. private RedisTemplate redisTemplate;
  14. public void addOrder() {
  15. int keyId = ThreadLocalRandom.current().nextInt(100) + 1;
  16. String orderNo = UUID.randomUUID().toString();
  17. redisTemplate.opsForValue().set(ORDER_KEY + keyId, "京东订单" + orderNo);
  18. log.info("=======>编号" + keyId + "的订单流水生成:{}", orderNo);
  19. }
  20. public String getOrderByKeyId(Integer keyId){
  21. return (String) redisTemplate.opsForValue().get(ORDER_KEY+keyId);
  22. }
  23. }
controller
  1. package com.atguigu.redis7.controller;
  2. import com.atguigu.redis7.service.OrderService;
  3. import io.swagger.annotations.Api;
  4. import io.swagger.annotations.ApiOperation;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.springframework.web.bind.annotation.*;
  7. import javax.annotation.Resource;
  8. @Api(tags = "订单接口")
  9. @RestController
  10. @Slf4j
  11. public class OrderController {
  12. @Resource
  13. private OrderService orderService;
  14. @ApiOperation("新增订单")
  15. @PostMapping("/order/add")
  16. public void addOrder(){
  17. orderService.addOrder();
  18. }
  19. @ApiOperation("按keyId查找订单")
  20. @GetMapping("/order/{keyId}")
  21. public String etOrderByKeyId(@PathVariable Integer keyId){
  22. return orderService.getOrderByKeyId(keyId);
  23. }
  24. }

测试

访问:http://localhost:7777/swagger-ui.html#/

 后端控制台查看:

swagger控制台输出keyId查询该订该订单:

序列化问题

如果在linux中查询,出现了以下乱码,

当获取值时,还是出现了乱码:

 键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。
RedisTemplate默认使用的是JdkSerializationRedisSerializer,stringRedisTemplate默认使用的是StringRedisSerializer。使用RedisTemplate默认的序列化就会上面图中那样,源码如下:

 这里有两种解决方案:

解决方案1

直接使用StringRedisTemplate,修改orderService

 再次在虚拟机中查询,key保存没问题了,但查询出来的值还是乱码,,swagger中没问题,前面是京东快递,说明仅仅是在redis客户端展示有问题。

 在redis客户端登录时加上--raw,表示redis客户端对中文的支持。

解决方案2 

恢复前面的orderService,添加RedisConfig配置类,使用GenericJackson2JsonRedisSerializer替换默认序列化。

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
  4. import org.springframework.data.redis.core.RedisTemplate;
  5. import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
  6. import org.springframework.data.redis.serializer.StringRedisSerializer;
  7. @Configuration
  8. public class RedisConfig
  9. {
  10. /**
  11. * redis序列化的工具配置类,下面这个请一定开启配置
  12. * 127.0.0.1:6379> keys *
  13. * 1) "ord:102" 序列化过
  14. * 2) "\xac\xed\x00\x05t\x00\aord:102" 野生,没有序列化过
  15. * this.redisTemplate.opsForValue(); //提供了操作string类型的所有方法
  16. * this.redisTemplate.opsForList(); // 提供了操作list类型的所有方法
  17. * this.redisTemplate.opsForSet(); //提供了操作set的所有方法
  18. * this.redisTemplate.opsForHash(); //提供了操作hash表的所有方法
  19. * this.redisTemplate.opsForZSet(); //提供了操作zset的所有方法
  20. * @param lettuceConnectionFactory
  21. * @return
  22. */
  23. @Bean
  24. public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory)
  25. {
  26. RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
  27. redisTemplate.setConnectionFactory(lettuceConnectionFactory);
  28. //设置key序列化方式string
  29. redisTemplate.setKeySerializer(new StringRedisSerializer());
  30. //设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化
  31. redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
  32. redisTemplate.setHashKeySerializer(new StringRedisSerializer());
  33. redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
  34. redisTemplate.afterPropertiesSet();
  35. return redisTemplate;
  36. }
  37. }

 

连接集群

启动redis集群6台实例

集群状态如下:

redis集群参考:

Redis集群(cluster)_@YanM的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_54239478/article/details/133078997?spm=1001.2014.3001.5501修改yml

  1. server:
  2. port: 7777
  3. spring:
  4. application:
  5. name: redis7_study
  6. redis:
  7. password: 123456
  8. lettuce:
  9. pool:
  10. max-active: 8
  11. max-wait: -1ms
  12. max-idle: 8
  13. min-idle: 0
  14. cluster:
  15. nodes: 192.168.67.100:6381,192.168.67.100:6382,192.168.67.101:6383,192.168.67.101:6384,192.168.67.102:6385,192.168.67.102:6386
  16. swagger2:
  17. enabled: true
  18. mvc:
  19. pathmatch:
  20. matching-strategy: ant_path_matcher

启动项目,添加三个订单:

人为模拟,master-6381机器意外宕机,手动shutdown。
先对redis集群命令方式,手动验证各种读写命令,看看6384是否上位


Redis Cluster集群能自动感知并自动完成主备切换,对应的slave6384会被选举为新的master节点 

微服务客户端再次读写访问,错误信息是不能连接到6381。

 结论:SpringBoot客户端没有动态感知到RedisCluster的最新集群信息。

SpringBoot 2.X版本,Redis默认的连接池采用Lettuce,当Redis集群节点发生变化后,Letture默认是不会刷新节点拓扑。
解决方案:修改yml,添加支持集群拓扑动态感应刷新配置。

再起启动项目,进行读写访问。 

 此时就OK了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/152084
推荐阅读
相关标签
  

闽ICP备14008679号