Redis集群xml配置和工具类

Redis集群xml配置和工具类

redis单机改集群

由于公司项目在用为redis单机,在存储和查询性能方面需要调优,所以改为redis集群,自己也研究了一下,配置redisCluster可以通过bean注入也可以写一个工具类,本质都是通过spring容器实例化redisCluster来提供redis数据的存取。下面介绍一下xml配置bean和工具类配置。

bean文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
     http://www.springframework.org/schema/cache 
     http://www.springframework.org/schema/cache/spring-cache.xsd          
        http://www.springframework.org/schema/context   
    http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="minIdle" value="${min-idle}" />
        <property name="maxIdle" value="${max-idle}" />
        <property name="maxTotal" value="${max-active}" />
    </bean>

<bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">
        <property name="maxRedirects" value="${maxRedirects}"></property>
        <property name="clusterNodes">
            <set>
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${host1}" />
                    <constructor-arg name="port" value="${port1}" />
                </bean>

                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${host2}" />
                    <constructor-arg name="port" value="${port2}" />
                </bean>

                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${host3}" />
                    <constructor-arg name="port" value="${port3}" />
                </bean>
                
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${host4}" />
                    <constructor-arg name="port" value="${port4}" />
                </bean>

                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${host5}" />
                    <constructor-arg name="port" value="${port5}" />
                </bean>

                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${host6}" />
                    <constructor-arg name="port" value="${port6}" />
                </bean>
            </set>
        </property>
    </bean>
    <bean id="jedisConnectionFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="poolConfig" ref="jedisPoolConfig"></property>
        <constructor-arg name="clusterConfig" ref="redisClusterConfiguration" />
        <property name="password" value="${password}" />
    </bean>

    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory" />
        <property name="KeySerializer">
            <bean
                class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
        </property>
        <property name="ValueSerializer">
            <bean
                class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"></bean>
        </property>
        <property name="HashKeySerializer">
            <bean
                class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
        </property>
        <property name="HashValueSerializer">
            <bean
                class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"></bean>
        </property>
    </bean>
</beans>

使用的简单的三主三从,通过配置文件注入。

RedisCluster工具类


@Log4j2
@Configuration
@EnableAutoConfiguration
@Component
public class CacheConfig implements Serializable {
    private static final long serialVersionUID = -2539468533583469346L;
    @Value("${password}")
    private String password;
    @Value("${timeout}")
    private int timeout;
    @Value("${namespace}")
    private String namespace;
    @Value("${maxtotal}")
    private int maxtotal;
    @Value("${maxidle}")
    private int maxidle;
    @Value("${minidle}")
    private int minidle;
    @Value("${maxwaitmillis}")
    private int maxwaitmillis;
    @Value("${redisClusterNotes}")
    private String redisClusterNotes;
    @Value("${soTimeout}")
    private int soTimeout;
    @Value("${maxAttempts}")
    private int maxAttempts;
    @Value("${maxRedirects}")
    private int maxRedirects;

    
    @Bean(name = "jedisPoolConfig")
    public JedisPoolConfig getJedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(maxtotal);
        jedisPoolConfig.setMaxIdle(maxidle);
        jedisPoolConfig.setMinIdle(minidle);
        jedisPoolConfig.setMaxWaitMillis(maxwaitmillis);
        jedisPoolConfig.setTestOnBorrow(false);
        jedisPoolConfig.setTestOnReturn(false);
        jedisPoolConfig.setTestWhileIdle(false);
        return jedisPoolConfig;
    }

    @Bean(name = "redisClusterConfiguration")
    public RedisClusterConfiguration getRedisClusterConfiguration(){

        if (StringUtils.isEmpty(redisClusterNotes)){
            log.error("redis集群节点为空");
            throw new RuntimeException();
        }

        String[] hostAndPorts = redisClusterNotes.split(",");
        Set<RedisNode> nodes = new HashSet<>();
        for (String hostAndPort : hostAndPorts){
            String[] ipAndPort = hostAndPort.split(":");
            nodes.add(new RedisNode(ipAndPort[0],Integer.parseInt(ipAndPort[1])));
        }

        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();

        redisClusterConfiguration.setClusterNodes(nodes);
        redisClusterConfiguration.setMaxRedirects(maxRedirects);
        return redisClusterConfiguration;
    }


    @Bean(name = "jedisConnectionFactory")
    public JedisConnectionFactory jedisConnectionFactory(@Qualifier("redisClusterConfiguration") RedisClusterConfiguration redisClusterConfiguration,
                                                         @Qualifier("jedisPoolConfig") JedisPoolConfig jedisPoolConfig) {
        //集群模式
        JedisConnectionFactory  factory = new JedisConnectionFactory(redisClusterConfiguration,jedisPoolConfig);
        factory.setPassword(password);
        return factory;
    }



    @Bean(name = "jedisCluster")
    public JedisCluster getJedisCluster(@Qualifier("jedisPoolConfig") JedisPoolConfig jedisPoolConfig){
        String[] hostAndPorts = redisClusterNotes.split(",");
        Set<HostAndPort> nodes = new HashSet<>();
        for (String hostAndPort : hostAndPorts){
            String[] ipAndPort = hostAndPort.split(":");
            nodes.add(new HostAndPort(ipAndPort[0],Integer.parseInt(ipAndPort[1])));
        }
        
        JedisCluster jedisCluster = new JedisCluster(nodes,timeout,1000,3,password, jedisPoolConfig);
        Map<String,JedisPool> node =  jedisCluster.getClusterNodes();
        return jedisCluster;
    }

    @Bean(name = "redisTemplate")
    public RedisTemplate redisTemplate(@Qualifier("jedisConnectionFactory") JedisConnectionFactory jedisConnectionFactory){
        StringRedisTemplate redisTemplate = new StringRedisTemplate();
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        return redisTemplate;
    }


    
    
    public String getPreFixKey(String key){
        return String.format("%s:%s",namespace,key);
    }


	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public int getTimeout() {
		return timeout;
	}

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

	public String getNamespace() {
		return namespace;
	}

	public void setNamespace(String namespace) {
		this.namespace = namespace;
	}

	public int getMaxtotal() {
		return maxtotal;
	}

	public void setMaxtotal(int maxtotal) {
		this.maxtotal = maxtotal;
	}

	public int getMaxidle() {
		return maxidle;
	}

	public void setMaxidle(int maxidle) {
		this.maxidle = maxidle;
	}

	public int getMinidle() {
		return minidle;
	}

	public void setMinidle(int minidle) {
		this.minidle = minidle;
	}

	public int getMaxwaitmillis() {
		return maxwaitmillis;
	}

	public void setMaxwaitmillis(int maxwaitmillis) {
		this.maxwaitmillis = maxwaitmillis;
	}

	public int getSoTimeout() {
		return soTimeout;
	}

	public void setSoTimeout(int soTimeout) {
		this.soTimeout = soTimeout;
	}

	public int getMaxAttempts() {
		return maxAttempts;
	}

	public void setMaxAttempts(int maxAttempts) {
		this.maxAttempts = maxAttempts;
	}

	public int getMaxRedirects() {
		return maxRedirects;
	}

	public void setMaxRedirects(int maxRedirects) {
		this.maxRedirects = maxRedirects;
	}

	public String getRedisClusterNotes() {
		return redisClusterNotes;
	}

	public void setRedisClusterNotes(String redisClusterNotes) {
		this.redisClusterNotes = redisClusterNotes;
	}
}

redisCluster API

@Log4j2
@Component
public class CacheApi {

    @Resource
    CacheConfig cacheConfig;

    public void expire(JedisCluster jedisCluster,String key, int seconds) {

        try {
            jedisCluster.expire(cacheConfig.getPreFixKey(key), seconds);
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        } catch (JedisDataException e) {
            log.error("JedisDataException",e);
        }
    }

    public <T extends Serializable> T getCache(JedisCluster jedisCluster,String key) {
        log.info("===========getCache===========key:"+key);
        try {
            byte[] result = jedisCluster.get(cacheConfig.getPreFixKey(key).getBytes());
            if (null==result ) {
                return null;
            }
            ByteArrayInputStream bis = new ByteArrayInputStream(result);
            try (ObjectInputStream ois = new ObjectInputStream(bis)) {
                return (T) ois.readObject();
            } catch (ClassNotFoundException e) {
                log.error("对象类型不存在", e);
            } catch (IOException e) {
                log.error("对象类型不存在", e);
            }
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
            //连接超时异常,则返回不再推送
            //String str = "timeout";
            //return (T) str;
        }
        return null;
    }

    public <T extends Serializable> List<T> getCache(JedisCluster jedisCluster,String... keys) {
        if (ArrayUtils.isEmpty(keys)) {
            return new ArrayList<>();
        }
        List<T> resultList = new ArrayList<>(keys.length);
        try {
            for (String key : keys) {
                byte[] result = jedisCluster.get(cacheConfig.getPreFixKey(key).getBytes());
                if (null==result) {
                    resultList.add(null);
                } else {
                    ByteArrayInputStream bis = new ByteArrayInputStream(result);
                    try (ObjectInputStream ois = new ObjectInputStream(bis)) {
                        resultList.add((T) ois.readObject());
                    } catch (ClassNotFoundException e) {
                        resultList.add(null);
                    } catch (IOException e) {
                        resultList.add(null);
                    }
                }
            }
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }
        return resultList;
    }

    public <T extends Serializable> List<T> getCache(JedisCluster jedisCluster,List<String> keys) {
        if (CollectionUtils.isEmpty(keys)) {
            return new ArrayList<>();
        }
        List<T> resultList = new ArrayList<>(keys.size());
        try {
            for (String key : keys) {
                byte[] result = jedisCluster.get(cacheConfig.getPreFixKey(key).getBytes());
                if (null==result) {
                    resultList.add(null);
                } else {
                    ByteArrayInputStream bis = new ByteArrayInputStream(result);
                    try (ObjectInputStream ois = new ObjectInputStream(bis)) {
                        resultList.add((T) ois.readObject());
                    } catch (ClassNotFoundException e) {
                        log.error(e.getLocalizedMessage(),e);
                        resultList.add(null);
                    } catch (IOException e) {
                        log.error(e.getLocalizedMessage(),e);
                        resultList.add(null);
                    }
                }
            }
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }
        return resultList;
    }

    public Long incr(JedisCluster jedisCluster,String key,Integer liveSeconds ) {

        try {
            if (null==liveSeconds ) {
                return jedisCluster.incr(key);
            } else {
                long result = jedisCluster.incr(cacheConfig.getPreFixKey(key));
                jedisCluster.expire(cacheConfig.getPreFixKey(key), liveSeconds);
                return result;
            }
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        } catch (JedisDataException e) {
            log.error("JedisDataException",e);
        }
        return null;
    }

    public Long incrGetCache(JedisCluster jedisCluster,String key) {

        try {
            String result = jedisCluster.get(cacheConfig.getPreFixKey(key));
            if (null==result ) {
                return null;
            }
            return Long.valueOf(result);
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }
        return 0L;
    }

    public List<Long> incrGetCache(JedisCluster jedisCluster,String... keys) {
        if (ArrayUtils.isEmpty(keys)) {
            return new ArrayList<>();
        }
        List<Long> resultList = new ArrayList<>(keys.length);
        try {
            for (String key : keys) {
                String result = jedisCluster.get(cacheConfig.getPreFixKey(key));
                if (null==result ) {
                    resultList.add(null);
                } else {
                    resultList.add(Long.valueOf(result));
                }
            }
            return resultList;
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }
        return resultList;
    }

    public List<Long> incrGetCache(JedisCluster jedisCluster,List<String> keys) {
        if (CollectionUtils.isEmpty(keys)) {
            return new ArrayList<>();
        }
        List<Long> resultList = new ArrayList<>(keys.size());
        try {
            for (String key : keys) {
                String result = jedisCluster.get(cacheConfig.getPreFixKey(key));
                if (null==result ) {
                    resultList.add(null);
                } else {
                    resultList.add(Long.valueOf(result));
                }
            }
            return resultList;
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }
        return resultList;
    }

    public Long incrNoExp(JedisCluster jedisCluster,String key) {

        try {
            long result = jedisCluster.incr(cacheConfig.getPreFixKey(key));
            return result;
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        } catch (JedisDataException e) {
            log.error("JedisDataException",e);
        }
        return null;
    }

    public void incrPutCache(JedisCluster jedisCluster,String key, long value,Integer liveSeconds ) {

        try {
            if (null==liveSeconds ) {
                jedisCluster.setnx(cacheConfig.getPreFixKey(key), String.valueOf(value));
            } else {
                jedisCluster.setex(cacheConfig.getPreFixKey(key), liveSeconds, String.valueOf(value));
            }
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }
    }

    public void putCache(JedisCluster jedisCluster,String[] keys, Object[] values,Integer liveSeconds) {
        if (ArrayUtils.isEmpty(keys) || ArrayUtils.isEmpty(values) || keys.length != values.length) {
            return;
        }
        JedisClusterPipeline jcp = JedisClusterPipeline.pipelined(jedisCluster);
        jcp.refreshCluster();
        try {
            for (int i = 0; i < keys.length; i++) {
                byte[] key = cacheConfig.getPreFixKey(keys[i]).getBytes();
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
                    oos.writeObject(values[i]);
                    oos.flush();
                    if (null==liveSeconds ) {
                        jcp.setnx(key, bos.toByteArray());
                    } else {
                        jcp.setex(key, liveSeconds, bos.toByteArray());
                    }
                } catch (IOException e) {
                    log.error("对象缓存失败", e);
                }
            }
            jcp.sync();
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }finally {
            jcp.close();
        }
    }

    public void putCache(JedisCluster jedisCluster,List<String> keys, List<Object> values,Integer liveSeconds) {
        if (CollectionUtils.isEmpty(keys) || CollectionUtils.isEmpty(values) || keys.size() != values.size()) {
            return;
        }
        JedisClusterPipeline jcp = JedisClusterPipeline.pipelined(jedisCluster);
        jcp.refreshCluster();
        try {
            for (int i = 0; i < keys.size(); i++) {
                byte[] key = cacheConfig.getPreFixKey(keys.get(i)).getBytes();
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
                    oos.writeObject(values.get(i));
                    oos.flush();
                    if (null==liveSeconds ) {
                    	jcp.setnx(key, bos.toByteArray());
                    } else {
                    	jcp.setex(key, liveSeconds, bos.toByteArray());
                    }
                } catch (IOException e) {
                    log.error("对象缓存失败", e);
                }
            }
            jcp.sync();

        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }finally {
            jcp.close();
        }
    }

    public void zaddCache(JedisCluster jedisCluster,String key, Map<String, Double> scoreMembers) {
        if (StringUtils.isEmpty(key)||CollectionUtils.isEmpty(scoreMembers) ) {
            return;
        }
        try {
            jedisCluster.zadd(cacheConfig.getPreFixKey(key),scoreMembers);
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }
    }

    public void zaddCache(JedisCluster jedisCluster,String key, String value,Double score) {
        if (StringUtils.isEmpty(key)||StringUtils.isEmpty(value) ) {
            return;
        }
        try {
            jedisCluster.zadd(cacheConfig.getPreFixKey(key),score,value);
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }
    }

    /**
     * 根据key查询存入缓存的排序map(含score)
     *
     * @param jedisCluster
     * @param key
     * @param start
     * @param end
     * @param isInverted: 查询结果是否倒排
     * @return*/

    public Map<String, Double> zrangeWithScores(JedisCluster jedisCluster,String key, Integer start, Integer end,
                                                Boolean isInverted) {
        // 使用LinkedHashMap保证map中的顺序按key的插入顺序排列
        Map<String, Double> result = new LinkedHashMap<String, Double>();
        List<String> keyList = new ArrayList<String>();
        List<Double> valueList = new ArrayList<Double>();
        start = start == null ? 0 : start;
        end = end == null ? -1 : end;
        // 查询结果是否倒排,默认为false
        isInverted = isInverted == null ? false : isInverted;
        try {
            Set<Tuple> set = jedisCluster.zrangeWithScores(cacheConfig.getPreFixKey(key), start, end);
            if (set != null && !set.isEmpty()) {
                for (Tuple tuple : set) {
                    keyList.add(tuple.getElement());
                    valueList.add(tuple.getScore());
                }

                if (isInverted) {
                    // 将key-value结果倒排
                    Collections.reverse(keyList);
                    Collections.reverse(valueList);
                }

                for (int i = 0; i < keyList.size(); i++) {
                    result.put(keyList.get(i), valueList.get(i));
                }
            }

            return result;
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException", e);
            return null;

        }
    }

    /**
     * 根据key查询存入缓存的排序map-key(不含score)
     *
     * @param jedisCluster
     * @param key
     * @param start
     * @param end
     * @param isInverted: 查询结果是否倒排
     * @return*/

    public List<String> zrangeList(JedisCluster jedisCluster,String key, Integer start, Integer end,
                                   Boolean isInverted) {
        List<String> result = new ArrayList<String>();
        start = start == null ? 0 : start;
        end = end == null ? -1 : end;
        // 查询结果是否倒排,默认为false
        isInverted = isInverted == null ? false : isInverted;
        try {
            Set<String> set = jedisCluster.zrange(cacheConfig.getPreFixKey(key), start, end);
            if (set != null && !set.isEmpty()) {
                result.addAll(set);
                if (isInverted) {
                    Collections.reverse(result);
                }
            }
            return result;
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException", e);
            return null;

        }

    }


    public void zremPreFixKeyCache(JedisCluster jedisCluster,String key,String member) {
        if (StringUtils.isEmpty(key)||StringUtils.isEmpty(member)) {
            return;
        }
        try {
            jedisCluster.zrem(key,member);
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }
    }

    public void zremCache(JedisCluster jedisCluster,String key,String member) {
        if (StringUtils.isEmpty(key)||StringUtils.isEmpty(member)) {
            return;
        }
        try {
            jedisCluster.zrem(cacheConfig.getPreFixKey(key),member);
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }
    }

    public Long zcardCache(JedisCluster jedisCluster,String key) {
        if (StringUtils.isEmpty(key)) {
            return 0L;
        }
        try {
            return jedisCluster.zcard(cacheConfig.getPreFixKey(key));
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }
        return 0L;
    }

    public <T extends Serializable> void putCache(JedisCluster jedisCluster,String key, T object,Integer liveSeconds) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(object);
            oos.flush();
            if (null==liveSeconds ) {
                jedisCluster.setnx(cacheConfig.getPreFixKey(key).getBytes(), bos.toByteArray());
            } else {
                jedisCluster.setex(cacheConfig.getPreFixKey(key).getBytes(), liveSeconds, bos.toByteArray());
            }
        } catch (IOException e) {
            log.error("对象缓存失败", e);
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
        }
    }

    public boolean removeCache(JedisCluster jedisCluster) {
        throw new UnsupportedOperationException();
    }

    public boolean removeCache(JedisCluster jedisCluster,String key) {
        try {
            jedisCluster.del(cacheConfig.getPreFixKey(key));
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
            return false;
        }
        return true;
    }

    public boolean removeCache(JedisCluster jedisCluster,String key, int seconds) {
        try {
            jedisCluster.expire(cacheConfig.getPreFixKey(key), seconds);
        } catch (JedisConnectionException e) {
            log.error("JedisConnectionException",e);
            return false;
        }
        return true;
    }
}

这这里说明一下,因为redisCluster的api执行最后都要执行releaseConnection方法,自动关闭jedis连接,所以不用手动关闭。

private void releaseConnection(Jedis connection) {
    if (connection != null) {
      connection.close();
    }
  }

redis集群是不支持批量操作的,这方面的知识博客上一大堆,所以在这里就不详细说明了,至于如何使用我下面贴出使用批量操作的工具类。直接在代码中使用即可。

@Log4j2
public class JedisClusterPipeline extends PipelineBase implements Closeable{


    // 部分字段没有对应的获取方法,只能采用反射来做
    // 你也可以去继承JedisCluster和JedisSlotBasedConnectionHandler来提供访问接口
    private static final Field FIELD_CONNECTION_HANDLER;
    private static final Field FIELD_CACHE; 
    static {
        FIELD_CONNECTION_HANDLER = getField(BinaryJedisCluster.class, "connectionHandler");
        FIELD_CACHE = getField(JedisClusterConnectionHandler.class, "cache");
    }

    private JedisSlotBasedConnectionHandler connectionHandler;
    private JedisClusterInfoCache clusterInfoCache;
    private Queue<Client> clients = new LinkedList<Client>();   // 根据顺序存储每个命令对应的Client
    private Map<JedisPool, Jedis> jedisMap = new HashMap<>();   // 用于缓存连接
    private boolean hasDataInBuf = false;   // 是否有数据在缓存区

    /**
     * 根据jedisCluster实例生成对应的JedisClusterPipeline
     * @param 
     * @return
     */
    public static JedisClusterPipeline pipelined(JedisCluster jedisCluster) {
        JedisClusterPipeline pipeline = new JedisClusterPipeline();
        pipeline.setJedisCluster(jedisCluster);
        return pipeline;
    }

    public JedisClusterPipeline() {
    }

    public void setJedisCluster(JedisCluster jedis) {
        connectionHandler = getValue(jedis, FIELD_CONNECTION_HANDLER);
        clusterInfoCache = getValue(connectionHandler, FIELD_CACHE);
    }

    /**
     * 刷新集群信息,当集群信息发生变更时调用
     * @param 
     * @return
     */
    public void refreshCluster() {
        connectionHandler.renewSlotCache();
    }

    /**
     * 同步读取所有数据. 与syncAndReturnAll()相比,sync()只是没有对数据做反序列化
     */
    public void sync() {
        innerSync(null);
    }

    /**
     * 同步读取所有数据 并按命令顺序返回一个列表
     * 
     * @return 按照命令的顺序返回所有的数据
     */
    public List<Object> syncAndReturnAll() {
        List<Object> responseList = new ArrayList<Object>();

        innerSync(responseList);

        return responseList;
    }

    private void innerSync(List<Object> formatted) {
        HashSet<Client> clientSet = new HashSet<Client>();

        try {
            for (Client client : clients) {
                // 在sync()调用时其实是不需要解析结果数据的,但是如果不调用get方法,发生了JedisMovedDataException这样的错误应用是不知道的,因此需要调用get()来触发错误。
                // 其实如果Response的data属性可以直接获取,可以省掉解析数据的时间,然而它并没有提供对应方法,要获取data属性就得用反射,不想再反射了,所以就这样了
                Object data = generateResponse(client.getOne()).get();
                if (null != formatted) {
                    formatted.add(data);
                }

                // size相同说明所有的client都已经添加,就不用再调用add方法了
                if (clientSet.size() != jedisMap.size()) {
                    clientSet.add(client);
                }
            }
        } catch (JedisRedirectionException jre) {
            if (jre instanceof JedisMovedDataException) {
                // if MOVED redirection occurred, rebuilds cluster's slot cache,
                // recommended by Redis cluster specification
                refreshCluster();
            }

            throw jre;
        } finally {
            if (clientSet.size() != jedisMap.size()) {
                // 所有还没有执行过的client要保证执行(flush),防止放回连接池后后面的命令被污染
                for (Jedis jedis : jedisMap.values()) {
                    if (clientSet.contains(jedis.getClient())) {
                        continue;
                    }

                    flushCachedData(jedis);
                }
            }

            hasDataInBuf = false;
            close();
        }
    }

    @Override
    public void close() {
        clean();

        clients.clear();

        for (Jedis jedis : jedisMap.values()) {
            if (hasDataInBuf) {
                flushCachedData(jedis);
            }

            jedis.close();
        }

        jedisMap.clear();

        hasDataInBuf = false;
    }

    private void flushCachedData(Jedis jedis) {
        try {
            jedis.getClient().getAll();
        } catch (RuntimeException ex) {
        }
    }

    @Override
    protected Client getClient(String key) {
        byte[] bKey = SafeEncoder.encode(key);

        return getClient(bKey);
    }

    @Override
    protected Client getClient(byte[] key) {
        Jedis jedis = getJedis(JedisClusterCRC16.getSlot(key));

        Client client = jedis.getClient();
        clients.add(client);

        return client;
    }

    private Jedis getJedis(int slot) {
        JedisPool pool = clusterInfoCache.getSlotPool(slot);

        // 根据pool从缓存中获取Jedis
        Jedis jedis = jedisMap.get(pool);
        if (null == jedis) {
            jedis = pool.getResource();
            jedisMap.put(pool, jedis);
        }

        hasDataInBuf = true;
        return jedis;
    }

    private static Field getField(Class<?> cls, String fieldName) {
        try {
            Field field = cls.getDeclaredField(fieldName);
            field.setAccessible(true);

            return field;
        } catch (NoSuchFieldException | SecurityException e) {
            throw new RuntimeException("cannot find or access field '" + fieldName + "' from " + cls.getName(), e);
        }
    }

    @SuppressWarnings({"unchecked" })
    private static <T> T getValue(Object obj, Field field) {
        try {
            return (T)field.get(obj);
        } catch (IllegalArgumentException | IllegalAccessException e) {
            log.error("get value fail", e);

            throw new RuntimeException(e);
        }
    }   

}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值