话不多说,我们知道RedisTemplate在SpringBoot 2.x以后使用了redis的高级客户端Lettuce进行了开发,题主在测试的过程中发现,即使搭建了哨兵的主从架构,使用RedisTemplate读写命令的时候无论是读还是写的命令都会在主节点进行。而从节点只是同步数据而已
如果我们在使用RedisTemplate的时候想用到读写分离的功能的话,首先题主在网上找了很多文章都没有讲到这个点(可能大家在生产环境都是用的集群多主架构吧),通过debug源码,发现RedisTemplate提供了一个可以设置的连接方式,可以在读的时候将请求的打到从服务器,写的时候打到主服务器。于是通过扩展的方式实现了可以将读写请求分离的效果(不过在测试的过程中发现只会选择一台从节点进行读请求,这点确实不够强,还以为能够根据多个从节点进行负载均衡)
核心代码如下:
/**
* @className LuttuceReadFromConfig
* @description TOO
* @Author cfx
* @DATE 2020/3/9 9:23
* @VERSION 1.0
**/
@Component
public class LuttuceReadFromConfig implements LettuceClientConfigurationBuilderCustomizer {
@Override
public void customize(LettuceClientConfiguration.LettuceClientConfigurationBuilder clientConfigurationBuilder) {
//设置读优先读从机
clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}
}
实现的效果如下所示
- 主节点可接收写请求
- 从节点接收读请求。
感兴趣的朋友可以看看Lettuce的这几个选项,是个枚举对象。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package io.lettuce.core;
import io.lettuce.core.ReadFromImpl.ReadFromAnyNode;
import io.lettuce.core.ReadFromImpl.ReadFromMaster;
import io.lettuce.core.ReadFromImpl.ReadFromMasterPreferred;
import io.lettuce.core.ReadFromImpl.ReadFromNearest;
import io.lettuce.core.ReadFromImpl.ReadFromReplica;
import io.lettuce.core.ReadFromImpl.ReadFromReplicaPreferred;
import io.lettuce.core.models.role.RedisNodeDescription;
import java.util.List;
public abstract class ReadFrom {
public static final ReadFrom MASTER = new ReadFromMaster();
public static final ReadFrom MASTER_PREFERRED = new ReadFromMasterPreferred();
public static final ReadFrom REPLICA_PREFERRED = new ReadFromReplicaPreferred();
/** @deprecated */
@Deprecated
public static final ReadFrom SLAVE_PREFERRED;
public static final ReadFrom REPLICA;
/** @deprecated */
@Deprecated
public static final ReadFrom SLAVE;
public static final ReadFrom NEAREST;
public static final ReadFrom ANY;
public ReadFrom() {
}
public abstract List<RedisNodeDescription> select(ReadFrom.Nodes var1);
protected boolean isOrderSensitive() {
return false;
}
public static ReadFrom valueOf(String name) {
if (LettuceStrings.isEmpty(name)) {
throw new IllegalArgumentException("Name must not be empty");
} else if (name.equalsIgnoreCase("master")) {
return MASTER;
} else if (name.equalsIgnoreCase("masterPreferred")) {
return MASTER_PREFERRED;
} else if (!name.equalsIgnoreCase("slave") && !name.equalsIgnoreCase("replica")) {
if (!name.equalsIgnoreCase("slavePreferred") && !name.equalsIgnoreCase("replicaPreferred")) {
if (name.equalsIgnoreCase("nearest")) {
return NEAREST;
} else if (name.equalsIgnoreCase("any")) {
return ANY;
} else {
throw new IllegalArgumentException("ReadFrom " + name + " not supported");
}
} else {
return REPLICA_PREFERRED;
}
} else {
return REPLICA;
}
}
static {
SLAVE_PREFERRED = REPLICA_PREFERRED;
REPLICA = new ReadFromReplica();
SLAVE = REPLICA;
NEAREST = new ReadFromNearest();
ANY = new ReadFromAnyNode();
}
public interface Nodes extends Iterable<RedisNodeDescription> {
List<RedisNodeDescription> getNodes();
}
}
官方文档对这块的描述地址是