目前,Spring Data Redis 中没有可以启用所需行为的配置选项。Jedis本身也不提供对这种场景的支持(参见jedis #458)。
RedisConnection
执行操作时向工厂请求连接。此时,请求的资源的使用目的不清楚,因为命令可能是r
,w
或者rw
。
一种可能的解决方案是自定义RedisConnectionFactory
能够提供连接 - 到您拥有的其中一个从属设备 - 在执行只读命令的情况下。
SlaveAwareJedisConnectionFactory factory = new SlaveAwareJedisConnectionFactory();
factory.afterPropertiesSet();
RedisConnection connection = factory.getConnection();
// writes to master
connection.set("foo".getBytes(), "bar".getBytes());
// reads from slave
connection.get("foo".getBytes());
/**
* SlaveAwareJedisConnectionFactory wraps JedisConnection with a proy that delegates readonly commands to slaves.
*/
class SlaveAwareJedisConnectionFactory extends JedisConnectionFactory {
/**
* Get a proxied connection to Redis capable of sending
* readonly commands to a slave node
*/
public JedisConnection getConnection() {
JedisConnection c = super.getConnection();
ProxyFactory proxyFactory = new ProxyFactory(c);
proxyFactory.addAdvice(new ConnectionSplittingInterceptor(this));
proxyFactory.setProxyTargetClass(true);
return JedisConnection.class.cast(proxyFactory.getProxy());
};
/**
* This one will get the connection to one of the slaves to read from there
*
* @return
*/
public RedisConnection getSlaveConnection() {
//TODO: find the an available slave serving the data
return new JedisConnection(new Jedis("your slave host lookup here"));
}
static class ConnectionSplittingInterceptor implements MethodInterceptor,
org.springframework.cglib.proxy.MethodInterceptor {
private final SlaveAwareJedisConnectionFactory factory;
public ConnectionSplittingInterceptor(SlaveAwareJedisConnectionFactory factory) {
this.factory = factory;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
RedisCommand commandToExecute = RedisCommand.failsafeCommandLookup(method.getName());
if (!commandToExecute.isReadonly()) {
return invoke(method, obj, args);
}
RedisConnection connection = factory.getSlaveConnection();
try {
return invoke(method, connection, args);
} finally {
// properly close the connection after executing command
if (!connection.isClosed()) {
connection.close();
}
}
}
private Object invoke(Method method, Object target, Object[] args) throws Throwable {
try {
return method.invoke(target, args);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return intercept(invocation.getThis(), invocation.getMethod(), invocation.getArguments(), null);
}
}
}
上面的解决方案包含几个问题。例如。 MULTI
EXEC
应用程序中的块可能不再按预期工作,因为命令现在可能会通过管道传输到您不希望它们出现的地方。RedisTemplates
所以也许有多个用于专用读、写目的也是有意义的。