coding……
但行好事 莫问前程

Spring Boot + Jedis实现redis常规数据结构存储

Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库。是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以用作数据库、缓存和消息中间件,可以说是实现网站高并发不可或缺的一部分。它支持多种类型的数据结构,如字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets)。Redis 使用C语言开发,支持的客户端语言也非常丰富,如C、C#、C++、Object-C、PHP、Python、 Java、Perl、Lua等。Redis 支持master-slave模式应用,支持数据的持久化,可以将内存中的数据存储在硬盘中,重启、断电的时候并不会丢失数据。至于Redis的基本操作命令,请参考Redis命令参考

通过上述介绍,对Redis有个最基础的概念了解——Redis是一个有着高速读取的内存数据库,而本篇文章也着重介绍一下,Redis作为存储的使用方法。Redis的各种语言客户端列表,请参见Redis Client。其中Java客户端在github上start最高的是Jedis和Redisson。Jedis提供了完整Redis命令,而Redisson有更多分布式的容器实现,本文使用的客户端是Jedis。Jedis不是线程安全的,故不应该在多线程环境中共用一个Jedis实例。同时,也应该避免直接创建多个Jedis实例,因为这种做法会导致创建过多的socket连接,性能不高。 要保证线程安全且获得较好的性能,可以使用JedisPool。JedisPool是一个连接池,既可以保证线程安全,又可以保证了较高的效率。本文基于JedisPool实现了一个自定义的RedisTemplate,通过RedisTemplate实现字符串、散列、列表、集合、有序集合的存储。

1. 项目结构

|   pom.xml
|   springboot-12-redis.iml
|
+---redis-client
|   |   pom.xml
|   |   redis-client.iml
|   |
|   +---src
|   |   +---main
|   |   |   +---java
|   |   |   |   \---com
|   |   |   |       \---zhuoli
|   |   |   |           \---service
|   |   |   |               \---redis
|   |   |   |                   \---client
|   |   |   |                       +---action
|   |   |   |                       |       GedisAction.java
|   |   |   |                       |
|   |   |   |                       \---template
|   |   |   |                           |   GedisTemplate.java
|   |   |   |                           |
|   |   |   |                           \---redis
|   |   |   |                                   RedisTemplate.java
|   |   |   |
|   |   |   \---resources
|   |   \---test
|   |       \---java
\---redis-repository
    |   pom.xml
    |   redis-repository.iml
    |
    +---src
    |   +---main
    |   |   +---java
    |   |   |   \---com
    |   |   |       \---zhuoli
    |   |   |           \---service
    |   |   |               \---springboot
    |   |   |                   \---redis
    |   |   |                       \---repository
    |   |   |                           |   SpringBootRedisApplicationContext.java
    |   |   |                           |
    |   |   |                           +---config
    |   |   |                           |       RedisConfig.java
    |   |   |                           |
    |   |   |                           +---controller
    |   |   |                           |       RedisController.java
    |   |   |                           |
    |   |   |                           +---core
    |   |   |                           |   |   RedisControllerService.java
    |   |   |                           |   |
    |   |   |                           |   \---impl
    |   |   |                           |           RedisControllerServiceImpl.java
    |   |   |                           |
    |   |   |                           \---repository
    |   |   |                                   RedisHashRepository.java
    |   |   |                                   RedisSetRepository.java
    |   |   |                                   RedisStringRepository.java
    |   |   |                                   RedisZSetRepository.java
    |   |   |
    |   |   \---resources
    |   |           application.properties
    |   |
    |   \---test
    |       \---java
  • redis-client:封装自定义RedisTemplate,用于实现Redis基本数据结构的操作
  • redis-repository:Redis基本数据结构操作应用

2. pom.xml

2.1 redis-client pom.xml

<?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>
        <artifactId>springboot-12-redis</artifactId>
        <groupId>com.zhuoli.service</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>redis-client</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
    </dependencies>
</project>

2.2 redis-repository pom.xml

<?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>
        <artifactId>springboot-12-redis</artifactId>
        <groupId>com.zhuoli.service</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>redis-repository</artifactId>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8</version>
        </dependency>

        <!--redis-client module依赖-->
        <dependency>
            <groupId>com.zhuoli.service</groupId>
            <artifactId>redis-client</artifactId>
        </dependency>
    </dependencies>
</project>

2.3 springboot-12-redis pom.xml

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zhuoli.service</groupId>
    <artifactId>springboot-12-redis</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>redis-client</module>
        <module>redis-repository</module>
    </modules>

    <!-- 继承说明:这里继承SpringBoot提供的父工程 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.7.RELEASE</version>
        <relativePath/>
    </parent>

    <!-- 版本说明:这里统一管理依赖的版本号 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.zhuoli.service</groupId>
                <artifactId>redis-client</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

    </dependencies>

</project>

3. redis-client定义

3.1 GedisAction定义

GedisAction是一个函数式接口,主要功能是,通过一个Redis Java客户端实例jedis,返回jedis操作Redis后的结果,比如String的存取结果、Set的随机结果等等,如下:

/**
 * 函数式接口,用于通过Jedis实例获取Redis查询结果
 * @author: zhuoli
 * @date: 2018/8/24 11:08
 */
public interface GedisAction<T> {
    T doAction(Jedis jedis);
}

3.2 GedisTemplate定义

GedisTemplate定义一个模板接口,所有客户端模板都要继承此接口。接口提供一个excute方法,接收一个GedisAction函数式接口参数,返回一个泛型结果,如下:

/**
 * Template接口,所有客户端模板都要继承该接口
 * @author: zhuoli
 * @date: 2018/8/24 11:11
 */
public interface GedisTemplate {
    <T> T execute(GedisAction<T> action) throws Exception;
}

3.3 RedisTemplate定义

RedisTemplate是Redis存取操作的客户端,内部持有一个JedisPool实例。通过GedisAction的doAction方法,实现Redis具体数据结构的存取操作,如下:

@Slf4j
public class RedisTemplate implements GedisTemplate {

    private JedisPool jedisPool;

    public RedisTemplate(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    public <T> T execute(GedisAction<T> action) {
        T result = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            result = action.doAction(jedis);
        } catch (Exception e) {
            log.error("Call redis failed.", e);
        } finally {
            assert jedis != null;
            jedis.close();
        }
        return result;
    }
}

4. redis-repository定义

4.1 application.properties定义Redis数据源信息

##数据源配置
zhuoli.redis.url=redis://127.0.0.1:6379/0
zhuoli.redis.timeout=2000
zhuoli.redis.size=2

4.2 RedisConfig定义

主要是为了定义一个RedisTemplate Bean,其中JedisPoolConfig的参数是我参考一些资料自己定义的,也可以使用默认JedisPoolConfig或者按照自己的实际需求进行修改。

@Configuration
@Slf4j
public class RedisConfig {

    @Value("${zhuoli.redis.url}")
    private String redisUri;

    @Value("${zhuoli.redis.timeout}")
    private Integer timeOut;

    @Value("${zhuoli.redis.size}")
    private Integer size;

    @Bean
    public RedisTemplate redisTemplate(){
        JedisPool jedisPool = null;
        try {
            jedisPool = new JedisPool(jedisPoolConfig(size), new URI(redisUri), timeOut);
        } catch (URISyntaxException e) {
            log.info("Jedis Pool Init Exception, message is {}", e);
        }
        return new RedisTemplate(jedisPool);
    }

    private JedisPoolConfig jedisPoolConfig(int size) {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMinIdle(size);
        jedisPoolConfig.setMaxIdle(2 * size);
        jedisPoolConfig.setMaxTotal(2 * size);
        jedisPoolConfig.setMaxWaitMillis(3000L);
        jedisPoolConfig.setTestOnBorrow(true);
        jedisPoolConfig.setTestOnReturn(true);
        jedisPoolConfig.setTestWhileIdle(true);
        jedisPoolConfig.setTimeBetweenEvictionRunsMillis(1000);
        return jedisPoolConfig;
    }
}

4.3 repository定义

在repository包中,我定义了String、Set、Sorted Set、Hash四种数据结构的常用方法,这里贴出String的常用方法。其实原理很简单,每个方法都试通过jedis的方法实现的,目的是为了隐藏jedis操作的具体细节,方便使用。

@Component
@AllArgsConstructor
@Slf4j
public class RedisStringRepository {

    private RedisTemplate redisTemplate;

    /*通过key获取对应地value*/
    public String getString(String key) {
        return redisTemplate.execute(jedis -> jedis.get(key));
    }

    /*set key-value对*/
    public String setKey(String key, String value) {
        return redisTemplate.execute(jedis -> jedis.set(key, value));
    }

    /*为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除*/
    public Long setKeyExpire(String key, Integer expireTime) {
        return redisTemplate.execute(jedis -> jedis.expire(key, expireTime));
    }

    /*set key-value对,同时设置过期时间*/
    public String setKeyWithExpire(String key, Integer ddl, String value){
        return redisTemplate.execute(jedis -> jedis.setex(key, ddl, value));
    }

    /*将key中储存的数字值加1*/
    public Long incr(String key) {
        return redisTemplate.execute(jedis -> jedis.incr(key));
    }

    /*将key中储存的数字值减1*/
    public Long decr(String key) {
        return redisTemplate.execute(jedis -> jedis.decr(key));
    }

    /*从当前数据库中随机返回(不删除)一个key*/
    public String randomKey() {
        return redisTemplate.execute(Jedis::randomKey);
    }

    /*删除给定的key*/
    public Long delete(String key) {
        return redisTemplate.execute(jedis -> jedis.del(key));
    }
}

所有jedis的方法都有相应的Redis原生操作命令对应,需要了解Redis操作命令的同学,请参考Redis 命令参考

4.4 ControllerService定义

public interface RedisControllerService {
    String insertKV(String key, String  value);

    String getValue(String key);
}

@Service
@AllArgsConstructor
@Slf4j
public class RedisControllerServiceImpl implements RedisControllerService {

    private RedisStringRepository redisStringRepository;

    @Override
    public String insertKV(String key, String value) {
        return redisStringRepository.setKey(key, value);
    }

    @Override
    public String getValue(String key) {
        return redisStringRepository.getString(key);
    }
}

4.5 Controller定义

@RestController
@AllArgsConstructor
@RequestMapping(value = "/redis")
@Slf4j
public class RedisController {

    private RedisControllerService redisControllerService;

    @RequestMapping(value = "/set_kv", method = RequestMethod.POST)
    public ResponseEntity setKV(@RequestParam(value = "key") String key,
                                @RequestParam(value = "value") String value) {
        return ResponseEntity.status(HttpStatus.OK).body(redisControllerService.insertKV(key, value));
    }

    @RequestMapping(value = "/get_value", method = RequestMethod.POST)
    public ResponseEntity getValue(@RequestParam(value = "key") String key) {
        return ResponseEntity.status(HttpStatus.OK).body(redisControllerService.getValue(key));
    }
}

5. 测试

说明数据已存入取数据成功,复合预期

除了本文提供的实现方式外,SpringBoot其实自己也基于Jedis实现了一个Redis访问框架Spring Data Redis,使用起来也挺方便的,场景也比较丰富,有兴趣的同学可以参考如何使用RedisTemplate访问Redis数据结构

示例代码:码云 – 卓立 – Spring Boot +Jedis实现redis常规数据结构存储

赞(0) 打赏
Zhuoli's Blog » Spring Boot + Jedis实现redis常规数据结构存储
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

zhuoli's blog

联系我关于我

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏