一个redis实例,默认包含16个库,序号从0到15。在redis命令行中,可以用select 序号来切换。我最近在做的一个项目中,需要使用redis的2个库。一个是由其他子系统写入,web后端(java)只读取;数据读出来,处理后,再写入另一个redis库,供WEB前端请求。

同时,对Redis的操作支持哈希表。即运行过程中,可以修改哈希类型的键值。比如该值是一个Hash<String,Object>类型,赋值的时候,如果不存在指定元素,则添加;否则更新。这样做的好处是,该键值的元素,可由不同的步骤产生。比如写入的值,有些是同步产生的,有些是异步产生的,有先有后。

具体如下:

一、配置文件中设置redis信息

redis:
    db:
        db1:
            host: 10.0.1.8
            port: 6379
            database: 5
            timeout: 6000 # 单位是毫秒
        db2:
            host: 10.0.1.8
            port: 6379
            database: 6
            timeout: 6000 # 单位是毫秒

二、Configuration

1、创建一个RedisProperties类来读取配置文件信息

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
@ConfigurationProperties(prefix = "redis")
public class RedisProperties {

    private Map<String, DatabaseProperties> db = new HashMap<>();

    public Map<String, DatabaseProperties> getDb() {
        return db;
    }
    public void setDb(Map<String, DatabaseProperties> db) {
        this.db = db;
    }

    public static class DatabaseProperties {
        private String host;
        private int port;
        private int database;
        private int timeout;

        public String getHost() {
            return host;
        }

        public void setHost(String host) {
            this.host = host;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }

        public int getDatabase() {
            return database;
        }

        public void setDatabase(int database) {
            this.database = database;
        }

        public int getTimeout() {
            return timeout;
        }

        public void setTimeout(int timeout) {
            this.timeout = timeout;
        }
    }
}

2、Redis.config

里面声明了2个工厂,可供产生2个redisTemplate,分别对应不同的redis数据库。

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {

    @Bean(name = "redisConnectionFactory1")
    @Primary
    public RedisConnectionFactory redisConnectionFactory1(RedisProperties redisProperties) {
        return createConnectionFactory(redisProperties.getDb().get("db1"));
    }

    @Bean(name = "redisConnectionFactory2")
    public RedisConnectionFactory redisConnectionFactory2(RedisProperties redisProperties) {
        return createConnectionFactory(redisProperties.getDb().get("db2"));
    }

    private RedisConnectionFactory createConnectionFactory(RedisProperties.DatabaseProperties properties) {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName(properties.getHost());
        config.setPort(properties.getPort());
        config.setDatabase(properties.getDatabase());

        LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
                .commandTimeout(Duration.ofMillis(properties.getTimeout()))
                .build();

        return new LettuceConnectionFactory(config, clientConfig);
    }

    @Bean(name = "redisTemplate1")
    @Primary
    public RedisTemplate<String, Object> redisTemplate1(
            @Qualifier("redisConnectionFactory1") RedisConnectionFactory factory) {
        return createRedisTemplate(factory);
    }

    @Bean(name = "redisTemplate2")
    public RedisTemplate<String, Object> redisTemplate2(
            @Qualifier("redisConnectionFactory2") RedisConnectionFactory factory) {
        return createRedisTemplate(factory);
    }

    private RedisTemplate<String, Object> createRedisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        // 配置具体的序列化方式
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        return template;
    }
}

三、如何使用两个 redis库?

思路是:
1)创建一个接口RedisService,声明各种对redis的操作
2)针对该接口,实现redisService1和redisService2,分别使用redisTemplate1和redisTemplate2
3)使用的时候,redisService1就是对应库1,redisService2对应库2

具体如下:

1、接口RedisService

import java.util.Map;
import java.util.Set;

public interface RedisService {
    Set<String> keys(String key);
    Set<String> keysAll();

    String get(String key);
    String get(String key, String hashKey);

    void set(String key,String value);

    void upsertArrayElements(String hashKey, Map<String, Object> newElements);

    Map<Object, Object> getArrayElements(String hashKey);

    boolean exists(String key);
    boolean hasKey(String key, String hashKey);
}

2、实现类1RedisService1Impl

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.Set;

@Service("redisService1")
public class RedisService1Impl implements  RedisService{

    private final RedisHandler redisHandler;

    @Autowired
    public RedisService1Impl(@Qualifier("redisTemplate1") RedisTemplate<String, Object> redisTemplate) {
        this.redisHandler = new RedisHandler(redisTemplate);
    }

    @Override
    public Set<String> keys(String key){
        return redisHandler.keys(key);
    }
    @Override
    public Set<String> keysAll(){
        return redisHandler.keysAll();
    }

    @Override
    public String get(String key){
        return redisHandler.get(key);
    }
    @Override
    public String get(String key, String hashKey){
        return redisHandler.get(key,hashKey);
    }

    @Override
    public void set(String key,String value){
        redisHandler.set(key,value);
    }

    @Override
    public void upsertArrayElements(String hashKey, Map<String, Object> newElements) {
        // 对于每个新元素,如果它不存在于哈希表中,则添加它;如果它已经存在,则覆盖它。
        redisHandler.upsertArrayElements(hashKey,newElements);
    }
    @Override
    public Map<Object, Object> getArrayElements(String hashKey) {
        return redisHandler.getArrayElements(hashKey);
    }

    @Override
    public boolean exists(String key){
        return redisHandler.exists(key);
    }
    @Override
    public boolean hasKey(String key, String hashKey){
        return redisHandler.hasKey(key,hashKey);
    }
}

注意代码中,声明使用redisTemplate1

    @Autowired
    public RedisService1Impl(@Qualifier("redisTemplate1") RedisTemplate<String, Object> redisTemplate) {
        this.redisHandler = new RedisHandler(redisTemplate);
    }

3、实现类2RedisService2Impl

基本上与RedisService1Impl没有什么区别,只是对应的redisTemplate不一样。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.Set;

@Service("redisService2")
public class RedisService2Impl implements  RedisService{

    private final RedisHandler redisHandler;

    @Autowired
    public RedisService2Impl(@Qualifier("redisTemplate2") RedisTemplate<String, Object> redisTemplate) {
        this.redisHandler = new RedisHandler(redisTemplate);
    }

    @Override
    public Set<String> keys(String key){
        return redisHandler.keys(key);
    }
    @Override
    public Set<String> keysAll(){
        return redisHandler.keysAll();
    }

    @Override
    public String get(String key){
        return redisHandler.get(key);
    }
    @Override
    public String get(String key, String hashKey){
        return redisHandler.get(key,hashKey);
    }

    @Override
    public void set(String key,String value){
        redisHandler.set(key,value);
    }

    @Override
    public void upsertArrayElements(String hashKey, Map<String, Object> newElements) {
        // 对于每个新元素,如果它不存在于哈希表中,则添加它;如果它已经存在,则覆盖它。
        redisHandler.upsertArrayElements(hashKey,newElements);
    }
    @Override
    public Map<Object, Object> getArrayElements(String hashKey) {
        return redisHandler.getArrayElements(hashKey);
    }

    @Override
    public boolean exists(String key){
        return redisHandler.exists(key);
    }
    @Override
    public boolean hasKey(String key, String hashKey){
        return redisHandler.hasKey(key,hashKey);
    }
}

4、公共Redis操作类RedisHandler

import com.landtool.server.modules.utils.JsonUtil;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Set;

@Component
public class RedisHandler {

    private RedisTemplate<String, Object> redisTemplate;

    public RedisHandler(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 是否存在指定key
     */
    public boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 是否存在键
     */
    public boolean hasKey(String key, String hashKey) {
        assert key != null && hashKey != null;
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        return hashOperations.hasKey(key, hashKey);
    }

    /**
     * 获取所有key
     */
    public Set<String> keys(String key) {
        assert key != null;
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        return hashOperations.keys(key);
    }

    public Set<String> keysAll() {
        return redisTemplate.keys("*");
    }

    public String get(String key) {
        Object value = redisTemplate.opsForValue().get(key);
        return value != null ? value.toString() : null;
    }
    public String get(String key, String hashKey) {
        assert key != null && hashKey != null;
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        String json = null;
        try {
            json = JsonUtil.toJsonString(hashOperations.get(hashKey, key));
        } catch (Exception e) {
            e.printStackTrace();
        }

        return json;
    }

    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    public void upsertArrayElements(String hashKey, Map<String, Object> newElements) {
        // 对于每个新元素,如果它不存在于哈希表中,则添加它;如果它已经存在,则覆盖它。
        redisTemplate.opsForHash().putAll(hashKey, newElements);
    }

    public Map<Object, Object> getArrayElements(String hashKey) {
        return redisTemplate.opsForHash().entries(hashKey);
    }

    /**
     * 删除数据
     */
    public Long delete(String key, String... hashKeys) {
        assert key != null && hashKeys != null;
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        return hashOperations.delete(key, hashKeys);
    }
}

四、调用RedisService

@Autowired
@Qualifier("redisService1")
RedisService redisService;

@Autowired
@Qualifier("redisService2")
RedisService redisService2;

redisService.***,使用库1
redisService2.***,使用库2

五、操作哈希表

private static final String REDIS_CHECK_LINK = "check_link";
public void freshCheckLink() {
    Map<String, Object> links = new HashMap<>();

    //nce
    getNce();

    //outer-api
    links.put("outerApi",某值);

    redisService2.upsertArrayElements(REDIS_CHECK_LINK,links);
}
private void getNce() {
    AtomicLong myCount = new AtomicLong();
    CompletableFuture.supplyAsync(() -> {
		。。。
    }).thenAccept(count -> {
	    Map<String, Object> links = new HashMap<>();
	    links.put("nce",myCount.get() >= 0);
	    redisService2.upsertArrayElements(REDIS_CHECK_LINK,links);
    });
}
private void setNceStatus(boolean status){

}

这样子,键名为“check_link”的结构如下:
在这里插入图片描述

六、小结

Redis使用多个库有现实意义。在我这个项目中,是为了避免冲突。库1由其他子系统频繁写入,而库2则是将数据从库1读取、分析、整理后产生的结果。web前端向后端请求数据,后端直接从结果从库2读出,避免了每个请求都分析、整理。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐