Spring Boot JedisCluster

簡述

Redis是一個開源的使用ANSI C語言編寫、支持網絡、可基於內存亦可持久化的日誌型、Key-Value數據庫,並提供多種語言的APIjava

配置

一、添加Maven依賴 (或jar包)

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

二、配置數據源相關信息

#redis cluster  
mc.cluster=192.168.10.20:6000,192.168.10.20:6001,192.168.10.20:6002,192.168.10.20:6003,192.168.10.20:6004,192.168.10.20:6005
mc.commandTimeout=5000

三、配置Redis

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Configuration
@ConditionalOnClass({JedisCluster.class})
public class JedisClusterConfig {
    @Value("#{'${mc.cluster}'.split(',')}")
    private List<String> clusterIpPortList;

    @Value("${mc.commandTimeout:5000}")
    private Integer commandTimeout;

    @Bean
    public JedisCluster getJedisCluster() {
        Set<HostAndPort> nodes = new HashSet<>();
        for (String ipPort : clusterIpPortList) {
            String[] ipPortPair = ipPort.split(":");
            nodes.add(new HostAndPort(ipPortPair[0].trim(), Integer.valueOf(ipPortPair[1].trim())));
        }
        return new JedisCluster(nodes, commandTimeout);
    }
}

四、測試

Maven 依賴node

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

Junitweb

import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import com.cl.config.Application;

import redis.clients.jedis.JedisCluster;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class) // 指定spring-boot的啓動類  
@WebAppConfiguration
public class TestJedis {

    private Logger logger = Logger.getLogger(getClass());

    @Autowired
    private JedisCluster jedisCluster;

    @Test
    public void testJedis() {
        jedisCluster.set("test_jedis_cluster", "38967");
        String string = jedisCluster.get("test_jedis_cluster");
        logger.info(string);
        jedisCluster.del("test_jedis_cluster");
    }
}

LUA

須要注意keys要在相同的Slot可考慮使用 hash tag 即在key中加入{相同標識}redis

JedisCluster.java

/**
 * 執行lua腳本
 * <p>
 * 須要注意keys要在相同的Slot
 * 可考慮使用 hash tag 即在key中加入{相同標識}
 * </p>
 */
@Override
public Object eval(final String script, final List<String> keys, final List<String> args) {
    return new JedisClusterCommand<Object>(connectionHandler, maxAttempts) {
        @Override
        public Object execute(Jedis connection) {
            return connection.eval(script, keys, args);
        }
    }.run(keys.size(), keys.toArray(new String[keys.size()]));
}

slot檢測

public T run(int keyCount, String... keys) {
  if (keys == null || keys.length == 0) {
    throw new JedisClusterException("No way to dispatch this command to Redis Cluster.");
  }

  // For multiple keys, only execute if they all share the
  // same connection slot.
  if (keys.length > 1) {
    int slot = JedisClusterCRC16.getSlot(keys[0]);
    for (int i = 1; i < keyCount; i++) {
      int nextSlot = JedisClusterCRC16.getSlot(keys[i]);
      if (slot != nextSlot) {
        throw new JedisClusterException("No way to dispatch this command to Redis Cluster "
            + "because keys have different slots.");
      }
    }
  }

JedisClusterCRC16.getSlot(String key)spring

public static int getSlot(String key) {
  key = JedisClusterHashTagUtil.getHashTag(key);
  // optimization with modulo operator with power of 2
  // equivalent to getCRC16(key) % 16384
  return getCRC16(key) & (16384 - 1);
}

JedisClusterHashTagUtil.getHashTag(String key)數據庫

public final class JedisClusterHashTagUtil {

  private JedisClusterHashTagUtil() {
    throw new InstantiationError("Must not instantiate this class");
  }

  public static String getHashTag(String key) {
    return extractHashTag(key, true);
  }

  public static boolean isClusterCompliantMatchPattern(String matchPattern) {
    String tag = extractHashTag(matchPattern, false);
    return tag != null && !tag.isEmpty();
  }

  private static String extractHashTag(String key, boolean returnKeyOnAbsence) {
    int s = key.indexOf("{");
    if (s > -1) {
      int e = key.indexOf("}", s + 1);
      if (e > -1 && e != s + 1) {
        return key.substring(s + 1, e);
      }
    }
    return returnKeyOnAbsence ? key : null;
}
}
相關文章
相關標籤/搜索