03、Redis项目实战:Redis的Java客户端

**作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
*上期文章:Redis:原理速成+项目实战——Redis常见命令(数据结构、常见命令总结)
*订阅专栏:Redis:原理速成+项目实战
希望文章对你们有所帮助

Redis的Java客户端

  • 客户端对比
  • Jedis快速入门
  • Jedis连接池
  • 认识SpringDataRedis
  • RedisTemplate快速入门
  • RedisTemplate的RedisSerializer
  • StringRedisTemplate

客户端对比

官方给Java推荐了几个客户端:

客户端 优点
Jedis 以Redis命令作为方法名称,学习成本低,简单实用。但Jedis实例的线程不安全,多线程环境需要基于连接池来使用
Lettuce(Spring官方默认兼容) Lettuce是基于Netty实现的,支持同步、异步和响应式编程方式,并且线程安全。支持Redis的哨兵模式、集群模式和管道模式
Redisson Redisson是一个基于Redis实现的分布式、可伸缩的Java数据结构集合

我们主要要掌握前两种,Spring的整合能力强,而SpringDataRedis正好整合了Jedis与Lettuce。

Jedis快速入门

Jedis的官网地址:Jedis官网地址
我们先进行一个快速的入门,我们需要进行一个Jedis的单元测试的实验:
1、 创建一个maven工程并且引入Jedis依赖以及单元测试依赖:;

	<dependencies>
       <!--Jedis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.7.0</version>
        </dependency>
        <!--单元测试-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
	</dependencies>

2、 建立测试类,直接上代码:;

package com.wang.test;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;

public class JedisTest {
   
     
    private Jedis jedis;

    @BeforeEach
    void setUp(){
   
     
        //建立连接,需要传入IP地址和端口号
        jedis = new Jedis("192.168.177.130", 6379);
        //设置密码
        jedis.auth("123456");
        //选择库
        jedis.select(0);
    }

    @Test
    void testString(){
   
     
        //插入数据,方法名称即为Redis命令名称,比较方便简洁
        String result = jedis.set("name", "布布");
        System.out.println("result = " + result);
        //获取数据
        String name = jedis.get("name");
        System.out.println("name = " + name);
    }

    @Test
    void testHash(){
   
     
        //插入哈希数据
        jedis.hset("user:1", "name", "Jack");
        jedis.hset("user:1", "age", "21");

        //获取
        Map<String, String> map = jedis.hgetAll("user:1");
        System.out.println(map);
    }

    @AfterEach
    void tearDown(){
   
     
        //释放资源
        if (jedis != null){
   
     
            jedis.close();
        }
    }
}

过程总结:
1、 引入依赖;
2、 创建Jedis对象,建立连接;
3、 使用Jedis,方法名与Redis命令一致;
4、 释放资源;

Jedis连接池

Jedis本身线程不安全,且频繁的创建和释放连接会造成性能上的损耗,所以需要使用Jedis的连接池来代替Jedis的直连方式。
官方也提供了这样的一个Jedis连接池的工厂类,这个工厂类的主要作用是设置池的一些配置(最大连接数,最大、最小空闲连接等),并且设置了一个静态get方法,当我们需要一个Jedis对象的时候,就可以在这个类中直接调用这个方法了:

package com.wang.jedis.util;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisConnectionFactory {
   
     
    private static final JedisPool jedisPool;

    static {
   
     
        //配置连接池
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        //最大连接数
        poolConfig.setMaxTotal(8);
        //最大空闲连接
        poolConfig.setMaxIdle(8);
        //最小空闲连接
        poolConfig.setMinIdle(0);
        //设置最长等待时间(ms)
        poolConfig.setMaxWaitMillis(1000);
        //创建连接池对象
        jedisPool = new JedisPool(poolConfig,
                "192.168.177.130", 6379, 1000, "123456");
    }
    //获取Jedis对象
    public static Jedis getJedis(){
   
     
        return jedisPool.getResource();
    }
}

以后当我们需要创建Jedis对象的时候就不需要再new了,直接用

jedis = JedisConnectionFactory.getJedis();

即可。

认识SpringDataRedis

SpringData是Spring中数据操作的模块,包含了对各种数据库的集成,其中SpringDataRedis就是对Redis的集成模块,其官网地址:
SpringDataRedis官网地址
其具备的一些特征:
1、 提供了对不同Redis客户端的整合(Jedis与Lettuce);
2、 提供了RedisTemplate统一API来操作Redis;
3、 支持Redis的发布订阅模型;
4、 支持Redis哨兵和Redis集群;
5、 支持基于Luttuce的响应式编程;
6、 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化;
7、 支持基于Redis的JDKCollection实现;

SpringDataRedis提供了RedisTemplate工具类,封装了各种对Redis多的操作,并且将不同数据类型的操作API封装到了不同的类型中:

API 说明
redisTemplate.opsForValue() 操作String类型数据
redisTemplate.opsForHash() 操作Hash类型数据
redisTemplate.opsForList() 操作List类型数据
redisTemplate.opsForSet() 操作Set类型数据
redisTemplate.opsForZset() 操作SortedSet类型数据
redisTemplate 通用命令

RedisTemplate快速入门

SpringBoot已经提供了对SpringDataRedis的支持,使用很方便,这里进行一些实验:
1、 新建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>
            <!--如果报错,就不要指定版本-->
            <version>2.10.0</version>
        </dependency>

2、 配置Redis信息:;

spring:
  data:
    redis:
      host: 192.168.177.130
      port: 6379
      password: 123456
      lettuce:
        pool:
          max-active: 8
          max-idle: 8
          min-idle: 0
          max-wait: 1000ms

3、 注入RedisTemplate:;

@Autowired
private RedisTemplate redisTemplate;

4、 编写测试类代码:;

@Test
void testString() {
   
     
	//写入一个String数据
    redisTemplate.opsForValue().set("name", "布布");
    //获取String数据
    Object name = redisTemplate.opsForValue().get("name");
    System.out.println("name = " + name);
}

RedisTemplate的RedisSerializer

上述的代码运行完毕以后,我们回到命令行去查看:
*
这时候我们可以发现,前面插入的键值对在这里完全乱码了。
这是RedisTemplate的序列化的问题,原理自行去理解,重点是我们不能再用这种序列化了。
因此,我们可以自定义RedisTemplate的序列化方式,并且bean方式注入,代码如下:

package com.wang.redis.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

@Configuration
public class RedisConfig {
   
     

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
   
     
        //创建RedisTemplate对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        //设置连接工厂
        template.setConnectionFactory(connectionFactory);
        //创建JSON序列化工具
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        //设置Key的序列化
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        //设置value的序列化
        template.setValueSerializer(jsonRedisSerializer);
        template.setHashValueSerializer(jsonRedisSerializer);
        //返回
        return template;
    }

}

以上代码需要引入JackSon依赖:

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.14.2</version>
        </dependency>

之后就可以修改一下我们的测试类的template使用方式了:

package com.wang;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class RedisDemoApplicationTests {
   
     

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Test
    void testString() {
   
     
        //写入一个String数据
        redisTemplate.opsForValue().set("name", "布布");
        //获取String数据
        Object name = redisTemplate.opsForValue().get("name");
        System.out.println("name = " + name);
    }
}

现在已经正常序列化了:
*
我们再做个实验,来看看对于一个对象,序列化是如何进行的,转化的json又是怎样的:
1、 我们创建一个User类:;

package com.wang.redis.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

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

2、 编写测试类:;

@Test
void testSaveUser(){
   
     
	//写入数据
    redisTemplate.opsForValue().set("user:100", new User("布布", 21));
    //获取数据
    User user = (User) redisTemplate.opsForValue().get("user:100");
    System.out.println("user = " + user);
}

运行结果:
*

StringRedisTemplate

尽管json的序列化方式可以满足我们的需求,但仍然存在一些问题,为了在反序列化时知道对象的类型,json序列化器会将类的class写入json,存入Redis,会造成内存额外开销。
而StringRedisTemplate可以解决这个问题,编写代码测试:

private static final ObjectMapper mapper = new ObjectMapper();

@Test
void testSaveUser() throws JsonProcessingException {
   
     
	//创建对象
    User user = new User("布布", 21);
    //手动序列化
    String json = mapper.writeValueAsString(user);
    //写入数据
    stringRedisTemplate.opsForValue().set("user:200", json);
    //获取数据
    String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
    //手动反序列化
    User user1 = mapper.readValue(jsonUser, User.class);
    System.out.println("user1 = " + user1);
}

*
在这里将RedisTemplate的两种序列化方案总结:
方案一(简单但占空间):
1、 自定义RedisTemplate;
2、 修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer;
方案二(省内存但复杂):
1、 使用StringRedisTemplate;
2、 写入Redis时手动把对象序列化为JSON;
3、 读取Redis时,手动把读取JSON反序列化为对象;

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: