zoukankan      html  css  js  c++  java
  • 20200610 千锋教育 Redis 3. Redis 客户端,与 SpringBoot 整合

    Redis 客户端,与 SpringBoot 整合

    常用的 Redis 客户端介绍以及对比

    常用的 Redis 客户端:

    • Jedis

      • 是老牌的 Redis 的 Java 实现客户端,提供了比较全面的 Redis 命令的支持

      • 比较全面的提供了 Redis 的操作特性

      • 使用阻塞的 I/O ,且其方法调用都是同步的,程序流需要等到 Sockets 处理完 I/O オ能执行,不支持异步

      • 客户端实例不是线程安全的,所以需要通过连接池来使用 Jedis

    • Redisson

      • 实现了分布式和可扩展的 Java 数据结构。
      • 促使使用者对 Redis 的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过 Redis 支持延迟队列
      • 基于 Netty 框架的事件驱动的通信层,其方法调用是异步的。Redisson 的 API 是线程安全的,所以可以操年单个 Redisson 连接来完成各种操作
    • Lettuce

      • 高級 Redis 客户端,用于线程安全同步,异步和响应使用,支持集群, Sentinel,管道和编码器。
      • 基于 Netty 框架的事件驱动的通信层,其方法调用是异步的。 Lettuce 的 API 是线程安全的,所以可以操作单个 Lettuce 连接来完成各种操作
      • 基于 Netty 框架的事件驱动的通信层,其方法调用是异步的。 Lettuce 的 API 是线程安全的,所以可以操作单个 Lettuce 连接来完成各种操作
      • Lettuce 能够支持 Redis 4,需要 Java8 及以上 Lettuce是基于 Netty 实现的可以与 Redis 进行同步和异步的通信

    总结:

    首先,在 Spring Boot2 之后,对 Redis 连接的支持,默认就采用了 Lettuce。这就一定程度说明了 Lettuce 和 Jedis 的优劣。

    优先使用 Lettuce,如果需要分布式锁,分布式集合等分布式的高级特性,添加 Redisson 结合使用,因为 Redisson 本身对字符串的操作支持很差。

    在一些高并发的场景中,比如秒杀,抢票,抢购这些场景,都存在对核心资源,商品库存的争夺,控制不好,库存数量可能被减少到负数,出现超卖的情况,或者产生唯一的一个递増 ID,由于 web 应用部署在多个机器上,简单的同步加锁是无法实现的,给数据库加锁的话,对于高并发,1000/s 的并发,数据库可能由行锁变成表锁,性能下降会历害。那相对而言,Redis 的分布式锁,相对而言,是个很好的选择,Redis 官方推荐使用的 Redisson 就提供了分布式锁和相关服务。

    SpringBoot 整合 Jedis

    Redis 的命令名称对应 Jedis 的方法名称

    1. POM

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.2.2.RELEASE</version>
              <relativePath/> <!-- lookup parent from repository -->
          </parent>
      
          <modelVersion>4.0.0</modelVersion>
      
          <groupId>org.example</groupId>
          <artifactId>studyJedis</artifactId>
          <version>1.0-SNAPSHOT</version>
      
          <dependencies>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter</artifactId>
              </dependency>
      
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <optional>true</optional>
              </dependency>
      
              <dependency>
                  <groupId>redis.clients</groupId>
                  <artifactId>jedis</artifactId>
                  <version>${jedis.version}</version>
              </dependency>
      
          </dependencies>
      
      </project>
      
    2. YML

      server:
        port: 8090
      
      spring:
        redis:
          host: 192.168.181.128
          port: 6379
          password: 123456
          timeout: 2000       # 连接超时
          jedis:
            pool:
              max-idle: 6     # 最大空闲数
              max-active: 10  # 最大连接数
              min-idle: 2     # 最小空闲数
      
    3. 配置类

      package study.hwj.redis.config;
      
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import redis.clients.jedis.JedisPool;
      import redis.clients.jedis.JedisPoolConfig;
      
      @Slf4j
      @Configuration
      public class JedisConfig {
          @Value("${spring.redis.host}")
          private String host;
          @Value("${spring.redis.port}")
          private Integer port;
          @Value("${spring.redis.password}")
          private String password;
          @Value("${spring.redis.timeout}")
          private Integer timeout;
          @Value("${spring.redis.jedis.pool.max-idle}")
          private Integer maxIdle;
          @Value("${spring.redis.jedis.pool.max-active}")
          private Integer maxActive;
          @Value("${spring.redis.jedis.pool.min-idle}")
          private Integer minIdle;
      
          @Bean
          public JedisPool jedisPool() {
              JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
              jedisPoolConfig.setMaxIdle(maxIdle);
              jedisPoolConfig.setMinIdle(minIdle);
              jedisPoolConfig.setMaxTotal(maxActive);
      
              JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
              log.info("JedisPool 连接成功:{} 	 {}", host, port);
              return jedisPool;
          }
      }
      
    4. 测试类,测试整合 Jedis 成功

      package study.hwj.redis;
      
      import org.junit.Test;
      import org.junit.runner.RunWith;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.boot.test.context.SpringBootTest;
      import org.springframework.test.context.junit4.SpringRunner;
      import redis.clients.jedis.JedisPool;
      
      @RunWith(SpringRunner.class)
      @SpringBootTest
      public class JedisTest {
          @Autowired
          private JedisPool jedisPool;
      
          @Test
          public void contextLoads() {
              System.out.println(jedisPool);
          }
      }
      
    5. 业务类,使用 Jedis 操作 Redis 的 string 数据类型

      @Service
      @Slf4j
      public class UserService {
          
          @Autowired
          private JedisPool jedisPool;
          
          public String getValue(String key) {
              String retVal;
      
              // 得到 Jedis 对象
              Jedis jedis = jedisPool.getResource();
      
              // 判断在 Redis 中 key 是否存在
              if (jedis.exists(key)) {
                  log.info("获取 【Redis】 中的数据");
                  // 从 Redis 中获取数据
                  retVal = jedis.get(key);
              } else {
                  log.info("获取 【数据库】 中的数据");
                  // 模拟从 数据库 中获取数据
                  retVal = "2020-6-6";
      
                  // 将数据存入 Redis
                  jedis.set(key, retVal);
              }
      
              // 关闭连接
              jedis.close();
      
              return retVal;
          }
      }
      
    6. 工具类,改写业务类

      @Component
      public class JedisUtil {
          @Autowired
          private JedisPool jedisPool;
      
          public Jedis getJedis() {
              return jedisPool.getResource();
          }
      
          public void close(Jedis jedis) {
              if (jedis != null) {
                  jedis.close();
              }
          }
      }
      

      改写后的业务类:

      @Service
      @Slf4j
      public class UserService {
      
          @Autowired
          private JedisUtil jedisUtil;
      
          public String getValue(String key) {
              String retVal;
      
              // 得到 Jedis 对象
              Jedis jedis = jedisUtil.getJedis();
      
              // 判断在 Redis 中 key 是否存在
              if (jedis.exists(key)) {
                  log.info("获取 【Redis】 中的数据");
                  // 从 Redis 中获取数据
                  retVal = jedis.get(key);
              } else {
                  log.info("获取 【数据库】 中的数据");
                  // 模拟从 数据库 中获取数据
                  retVal = "2020-6-6";
      
                  // 将数据存入 Redis
                  jedis.set(key, retVal);
              }
      
              // 关闭连接
              jedisUtil.close(jedis);
      
              return retVal;
          }
      }
      
    7. 使用 Jedis 操作 Redis 的 hash 数据类型

      public User selectUserById(String id) {
          String key = "user:" + id;
      
          User user = new User();
      
          // 得到 Jedis 对象
          Jedis jedis = jedisUtil.getJedis();
      
          // 判断在 Redis 中 key 是否存在
          if (jedis.exists(key)) {
              log.info("获取 【Redis】 中的数据");
              // 从 Redis 中获取数据
              Map<String, String> map = jedis.hgetAll(key);
              user.setId(map.get("id"));
              user.setName(map.get("name"));
              user.setAge(Integer.valueOf(map.get("age")));
      
          } else {
              log.info("获取 【数据库】 中的数据");
              // 模拟从 数据库 中获取数据
              user.setId(key);
              user.setName("testName");
              user.setAge(18);
      
              Map<String,String> map = new HashMap();
              map.put("id", user.getId());
              map.put("name", user.getName());
              map.put("age", user.getAge().toString());
      
      
              // 将数据存入 Redis
              jedis.hset(key, map);
          }
      
          // 关闭连接
          jedisUtil.close(jedis);
      
          return user;
      }
      

    SpringBoot 整合 Lettuce

    SpringBoot2 默认采用了 Lettuce

    默认的 RedisTemplate 没有配置序列化器,会使用 JDK 的序列化器,导致存入 Redis 中的内容乱码。

    1. POM

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.2.2.RELEASE</version>
              <relativePath/> <!-- lookup parent from repository -->
          </parent>
      
          <modelVersion>4.0.0</modelVersion>
      
          <groupId>org.example</groupId>
          <artifactId>studyLettuce</artifactId>
          <version>1.0-SNAPSHOT</version>
      
          <dependencies>
      
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-data-redis</artifactId>
                  <version>2.2.2.RELEASE</version>
              </dependency>
      
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
      
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
              </dependency>
      
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <optional>true</optional>
              </dependency>
      
              <dependency>
                  <groupId>org.apache.commons</groupId>
                  <artifactId>commons-pool2</artifactId>
                  <version>${commons-pool2.version}</version>
              </dependency>
      
              <dependency>
                  <groupId>com.fasterxml.jackson.core</groupId>
                  <artifactId>jackson-databind</artifactId>
              </dependency>
      
          </dependencies>
      </project>
      
    2. YML

      server:
        port: 8090
      
      spring:
        redis:
          host: 192.168.181.128
          port: 6379
          password: 123456
          timeout: 2000       # 连接超时
          lettuce:
            pool:
              max-idle: 6     # 最大空闲数
              max-active: 10  # 最大连接数
              min-idle: 2     # 最小空闲数
            shutdown-timeout: 100   # 关闭超时时间
      
    3. 配置类

      @Configuration
      public class RedisConfig  {
      
          @Bean
          public RedisTemplate redisTemplate(LettuceConnectionFactory factory){
              RedisTemplate<Object, Object> template = new RedisTemplate<>();
              template.setConnectionFactory(factory);
      
              Jackson2JsonRedisSerializer<Object> 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);
      
              StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
      
              template.setKeySerializer(stringRedisSerializer);
              template.setValueSerializer(jackson2JsonRedisSerializer);
      
              template.setHashKeySerializer(stringRedisSerializer);
              template.setHashValueSerializer(jackson2JsonRedisSerializer);
              template.afterPropertiesSet();
              return template;
          }
      
      }
      
      
    4. 业务类,操作 string 和 hash 类型

      @Service
      @Slf4j
      public class UserService {
          @Autowired
          private RedisTemplate redisTemplate;
      
          public void getRedisTemplate() {
              System.out.println(redisTemplate);
          }
      
          public String getString(String key) {
              String retVal;
      
              // 判断在 Redis 中 key 是否存在
              if (redisTemplate.hasKey(key)) {
                  log.info("获取 【Redis】 中的数据");
                  // 从 Redis 中获取数据
                  retVal = (String) redisTemplate.opsForValue().get(key);
              } else {
                  log.info("获取 【数据库】 中的数据");
                  // 模拟从 数据库 中获取数据
                  retVal = "2020-6-6";
      
                  // 将数据存入 Redis
                  redisTemplate.opsForValue().set(key, retVal);
              }
      
              return retVal;
          }
      
          public User selectUserById(String id) {
              String key = "user";
      
              User user = new User();
      
              // 判断在 Redis 中 key 是否存在
              if (redisTemplate.opsForHash().hasKey(key, id)) {
                  log.info("获取 【Redis】 中的数据");
                  // 从 Redis 中获取数据
                  user = (User) redisTemplate.opsForHash().get(key, id);
              } else {
                  log.info("获取 【数据库】 中的数据");
                  // 模拟从 数据库 中获取数据
                  user.setId(key);
                  user.setName("testName");
                  user.setAge(18);
      
                  // 将数据存入 Redis
                  redisTemplate.opsForHash().put(key, id, user);
              }
      
              return user;
          }
      }
      
    5. 优化业务类

      @Service
      @Slf4j
      public class UserService {
          @Autowired
          private RedisTemplate redisTemplate;
      
          @Resource(name = "redisTemplate")
          private ValueOperations<String, String> valueOperations;
      
          @Resource(name = "redisTemplate")
          private HashOperations<String, String, User> hashOperations;
      
          public void getRedisTemplate() {
              System.out.println(redisTemplate);
          }
      
          public String getString(String key) {
              String retVal;
      
              // 判断在 Redis 中 key 是否存在
              if (redisTemplate.hasKey(key)) {
                  log.info("获取 【Redis】 中的数据");
                  // 从 Redis 中获取数据
                  retVal = valueOperations.get(key);
              } else {
                  log.info("获取 【数据库】 中的数据");
                  // 模拟从 数据库 中获取数据
                  retVal = "2020-6-6";
      
                  // 将数据存入 Redis
                  valueOperations.set(key, retVal);
              }
      
              return retVal;
          }
      
          public User selectUserById(String id) {
              String key = "user";
      
              User user = new User();
      
              // 判断在 Redis 中 key 是否存在
              if (redisTemplate.opsForHash().hasKey(key, id)) {
                  log.info("获取 【Redis】 中的数据");
                  // 从 Redis 中获取数据
                  user = hashOperations.get(key, id);
              } else {
                  log.info("获取 【数据库】 中的数据");
                  // 模拟从 数据库 中获取数据
                  user.setId(key);
                  user.setName("testName");
                  user.setAge(18);
      
                  // 将数据存入 Redis
                  hashOperations.put(key, id, user);
              }
      
              return user;
          }
      }
      
      
  • 相关阅读:
    第一篇正式文章 随便聊聊吧
    CSS 28 块之间的空格
    CSS 27 贴在下方
    CSS 26 左右固定
    CSS 25 垂直居中
    CSS 24 左侧固定
    CSS 23 水平居中
    CSS 22 显示方式
    CSS 21 浮动
    CSS 20 相对定位
  • 原文地址:https://www.cnblogs.com/huangwenjie/p/13088703.html
Copyright © 2011-2022 走看看