1. Introduction

Database read/write separation is a means to optimize application performance. The principle of read/write separation is as follows: The master node is responsible for application write operations (also processing real-time read scenarios), while the slave node is responsible for application read operations. RedisCluster also contains master and slave nodes. Can the slave nodes in the RedisCluster process read requests the same as the slave nodes in the database to achieve read/write separation?

2.RedisClusterThe principle of

How is connection pooling implemented in Jedis? In standalone mode, JedisConnectionFactory initialization will directly create a JedisPool to achieve the management and reuse of connection resources.

public void afterPropertiesSet(a) {
    // 1. If a connection pool is configured and the connection mode is not clustered, create a connection pool directly
    if(getUsePool() && ! isRedisClusterAware()) {this.pool = createPool();
    }

    // 2. In cluster mode, create a cluster
    if (isRedisClusterAware()) {
        this.cluster = createCluster();
        this.topologyProvider = createTopologyProvider(this.cluster);
        this.clusterCommandExecutor = new ClusterCommandExecutor(this.topologyProvider,
                                                                 new JedisClusterConnection.JedisClusterNodeResourceProvider(this.cluster, this.topologyProvider), EXCEPTION_TRANSLATION); }}Copy the code

2.1 Connecting Nodes

private void initializeSlotsCache(Set<HostAndPort> startNodes,
      int connectionTimeout, int soTimeout, String user, String password, String clientName,
                                  boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) {
    // 1. Traverse all cluster nodes
    for (HostAndPort hostAndPort : startNodes) {
        Jedis jedis = null;
        try {
            // 2. Create a Jedis object based on the node information to operate redis
            jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort(), connectionTimeout, soTimeout, ssl, sslSocketFactory, sslParameters, hostnameVerifier);
            // 3. Initialize slot
            cache.discoverClusterNodesAndSlots(jedis);
            break;
        } catch (JedisConnectionException e) {
            // try next nodes
        } finally {
            if(jedis ! =null) { jedis.close(); }}}}Copy the code

2.2 the initializationslot

Before analyzing the initialization slot code, execute the Cluster Slots command

Cluster slots returns the start and end values of the slot, the IP address, port, and ID of the master slot, and the IP address, port, and ID of the slave slot. It’s pretty easy to analyze the source code once you know what’s returned

public void discoverClusterNodesAndSlots(Jedis jedis) {
    w.lock();

    try {
        reset();
        // 1. Run the clster slots command
        List<Object> slots = jedis.clusterSlots();
        // 2. The traversal command returns a result
        for (Object slotInfoObj : slots) {
            List<Object> slotInfo = (List<Object>) slotInfoObj;

            if (slotInfo.size() <= MASTER_NODE_INDEX) {
                continue;
            }
			
            // 3. Obtain the number of slots. For example, the number of the first slot is 501
            List<Integer> slotNums = getAssignedSlotArray(slotInfo);

            // hostInfos
            int size = slotInfo.size();
            // 4. Start with the master message
            for (int i = MASTER_NODE_INDEX; i < size; i++) {
                // 5. Obtain the corresponding IP address, port number, and ID
                List<Object> hostInfos = (List<Object>) slotInfo.get(i);
                if (hostInfos.isEmpty()) {
                    continue;
                }
				
                // 6. Assemble the HostAndPort based on the IP address and port
                HostAndPort targetNode = generateHostAndPort(hostInfos);
                // 7. Store IP address: mapping between port and JedisPool
                setupNodeIfNotExist(targetNode);
                // 8. If the slot is master, the mapping between slot and JedisPool is stored. Therefore, the connection created by JedisPool can only operate mater
                if(i == MASTER_NODE_INDEX) { assignSlotsToNode(slotNums, targetNode); }}}}finally{ w.unlock(); }}Copy the code

2.3 IP: Mapping between ports and JedisPool

2.4 Mapping between Slot and JedisPool

2.5 Compute the slot corresponding to the key

public static int getSlot(byte[] key) {
    if (key == null) {
        throw new JedisClusterOperationException("Slot calculation of null is impossible");
    }

    int s = -1;
    int e = -1;
    boolean sFound = false;
    for (int i = 0; i < key.length; i++) {
        if (key[i] == '{' && !sFound) {
            s = i;
            sFound = true;
        }
        if (key[i] == '} ' && sFound) {
            e = i;
            break; }}if (s > -1 && e > -1&& e ! = s +1) {
        return getCRC16(key, s + 1, e) & (16384 - 1);
    }
    return getCRC16(key) & (16384 - 1);
}
Copy the code

CRC is performed on the key when the command is executed and the and operation is performed to obtain slot values

2.6 Obtaining the JedisPool

public JedisPool getSlotPool(int slot) {
    r.lock();
    try {
        return slots.get(slot);
    } finally{ r.unlock(); }}Copy the code

With slot values, you can obtain the corresponding JedisPool from the mapping in section 2.4. The JedisPool obtained is constructed based on the master address. Therefore, you can know that slave nodes of RedisCluster do not provide read services, but only provide high availability.