(二)连接Redis,Lettuce与Jedis客户端

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(二)连接Redis,Lettuce与Jedis客户端相关的知识,希望对你有一定的参考价值。

参考技术A Redis优势

从 Spring Boot 2.x 开始 Lettuce 已取代 Jedis 成为首选 Redis 的客户端。当然 Spring Boot 2.x 仍然支持 Jedis,并且你可以任意切换客户端。

使用Lecttuce需要再引用一个包

配置

org.springframework.boot.autoconfigure.data.redis.RedisProperties中集成了Jedis与Lettuce。

概念:

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

Redisson:实现了分布式和可扩展的Java数据结构。

Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。

优点:

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

Redisson:促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过Redis支持延迟队列

Lettuce:主要在一些分布式缓存框架上使用比较多

可伸缩:

Jedis:使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。

Redisson:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Redisson的API是线程安全的,所以可以操作单个Redisson连接来完成各种操作

Lettuce:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作

Redis 学习笔记Jedis & Lettuce

前言:Jedis是之前被推荐的最为广泛的操作Redis的客户端连接开发工具,现在被lettuce代替了,我们简单了解一下这个的使用吧。

1.导入Jedis依赖

1.jedis坐标如下

    <dependencies>
        <!--        导入jedis-->
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.5.2</version>
        </dependency>

        <!--        导入fastjson-->
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>

    </dependencies>

2.Java端测试连接

public class TestPing {

    public static void main(String[] args) {
        //new 一个Jedis对象即可
        Jedis jedis = new Jedis("127.0.0.1",6379);
        // 返回PING代表连接成功
        System.out.println(jedis.ping());
        //关机连接
        jedis.close();
    }
}

3.使用Jedis开启事务

public class TestTX {

    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("name","zhangsan");
        jsonObject.put("age","18");
        String s = jsonObject.toString();
        //开启事务
        Transaction multi = jedis.multi();
        try {
            multi.set("user1",s);
            multi.set("user2",s);
            //执行事务
            multi.exec();
        } catch (Exception e) {
            //放弃事务
            multi.discard();
            e.printStackTrace();
        }finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            //关闭连接
            jedis.close();
        }
    }
}

2.SpringBoot整合Redis

  • 导入基本坐标:
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-data-redis</artifactId>
       </dependency>

  • 说明:在SpringBoot2.x之后,原来使用的jedis被替换为了lettuce
  • jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全,使用jedis pool连接池。BIO模式
  • lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况。可以减少线程数量,更像NIO模式

redis自动配置类分析

1.自动配置类,专门提取了一个操作String字符串的实例(StringRedisTemplate)

@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")//在没有redisTemplate这个bean时才生效,这样我们可以自定义一个RedisTemple来替换。
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
	//默认的RedisTemplate没有过多的设置,redis对象都是需要序列化。(尤其是使用了netty这种异步通信,默认使用jdk序列化)
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

2.配置

spring:
  redis:
    host: 192.168.242.132	//虚拟机的ip
    port: 6379

3.测试

    @Test
    void contextLoads() {
        redisTemplate.opsForValue().set("name","zhangsan");
        System.out.println(redisTemplate.opsForValue().get("name"));
    }

############控制台###############

zhangsan

3.关于Redis序列化问题

1.准备一个对象

@Component
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User{
    private String name;
    private int age;
}

2.测试添加到redis中

 	@Autowired
    private RedisTemplate redisTemplate;
    
 	@Test
    void test() {
        //真实的开发一般都是使用json来传递对象
        User user = new User("张三", 3);
        redisTemplate.opsForValue().set("user", user);
        System.out.println(redisTemplate.opsForValue().get("user"));
    }

异常抛出:(形成的原因就是实体类未实现序列化接口 Serializable)

org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is org.springframework.core.NestedIOException: Failed to deserialize object type; nested exception is java.lang.ClassNotFoundException: com.zzgele.qmkx.info.enums.InfomationTypeEnum
    at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:84) ~[spring-data-redis-2.0.7.RELEASE.jar!/:2.0.7.RELEASE]
    at org.springframework.data.redis.serializer.SerializationUtils.deserializeValues(SerializationUtils.java:54) ~[spring-data-redis-2.0.7.RELEASE.jar!/:2.0.7.RELEASE]
    at org.springframework.data.redis.serializer.SerializationUtils.deserialize(SerializationUtils.java:68) ~[spring-data-redis-2.0.7.RELEASE.jar!/:2.0.7.RELEASE]
    at org.springframework.data.redis.core.AbstractOperations.deserializeValues(AbstractOperations.java:268) ~[spring-data-redis-2.0.7.RELEASE.jar!/:2.0.7.RELEASE]
    at org.springframework.data.redis.core.DefaultListOperations.lambda$range$6(DefaultListOperations.java:171) ~[spring-data-redis-2.0.7.RELEASE.jar!/:2.0.7.RELEASE]
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224) ~[spring-data-redis-2.0.7.RELEASE.jar!/:2.0.7.RELEASE]
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184) ~[spring-data-redis-2.0.7.RELEASE.jar!/:2.0.7.RELEASE]
    at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:95) ~[spring-data-redis-2.0.7.RELEASE.jar!/:2.0.7.RELEASE]
    at org.springframework.data.redis.core.DefaultListOperations.range(DefaultListOperations.java:171) ~[spring-data-redis-2.0.7.RELEASE.jar!/:2.0.7.RELEASE]
    at com.zzgele.qmkx.info.service.impl.InfomationServiceImpl.refreshInfo(InfomationServiceImpl.java:1116) ~[classes!/:0.0.1-SNAPSHOT]

3.添加到redis中数据被转义,形成了一堆看不懂的东西

在这里插入图片描述
4.追加到RedisTemplate中的序列化方式

在这里插入图片描述

5.默认实现(JDK序列化)

   if (this.defaultSerializer == null) {
            this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
        }

        if (this.enableDefaultSerializer) {
            if (this.keySerializer == null) {
                this.keySerializer = this.defaultSerializer;
                defaultUsed = true;
            }

            if (this.valueSerializer == null) {
                this.valueSerializer = this.defaultSerializer;
                defaultUsed = true;
            }

            if (this.hashKeySerializer == null) {
                this.hashKeySerializer = this.defaultSerializer;
                defaultUsed = true;
            }

            if (this.hashValueSerializer == null) {
                this.hashValueSerializer = this.defaultSerializer;
                defaultUsed = true;
            }

6.如何修改默认序列化方式?

  • 在其中提供了setKeySerializer方法,参数只需要传入序列化方式即可。
    在这里插入图片描述
    补充:RedisSerializer是个接口,里面提供多种方式,这里我们使用常用的Jackson序列化方式
    在这里插入图片描述
    7.修改Redis序列化的模板如下:
@Configuration
public class RedisConfig {

    //编写自定义的RedisTemplate
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        //设置Redis连接工厂
        template.setConnectionFactory(redisConnectionFactory);
        //Json序列化配置
        //传入Object对象
        Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jsonRedisSerializer.setObjectMapper(objectMapper);
        //String 序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //key 采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //hash的key 采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        //value 采用json的序列化方式
        template.setValueSerializer(jsonRedisSerializer);
        //hash的value 采用json的序列化方式
        template.setHashValueSerializer(jsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

8.再次查看redis中存入的数据

127.0.0.1:6379> keys *
1) "user"
127.0.0.1:6379>

序列化完成

以上是关于(二)连接Redis,Lettuce与Jedis客户端的主要内容,如果未能解决你的问题,请参考以下文章

Redis连接池Lettuce Jedis 区别

redis网络波动,Jedis/Lettuce是阻塞还是失败?

Redis 学习笔记Jedis & Lettuce

springboot2.x版本整合redis(单机/集群)(使用lettuce)

springboot整合redis

Redis连接池Lettuce踩坑记录