В настоящее время в Spring Data Redis отсутствует опция конфигурации, которая обеспечивала бы желаемое поведение. Также Jedis предлагают поддержку для такого сценария (см. jedis #458). RedisConnection
запрашивает соединение с заводом при выполнении операций. На этом этапе цель использования запрошенного ресурса неясна, так как команда может быть r
, w
или rw
.
Одним из возможных решений может быть пользовательский RedisConnectionFactory
, способный обеспечить соединение - с одним из подчиненных вам - в случае выполнения команды readonly.
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
для выделенных читать, написать цель.
как около двух двух Jedis подключения заводов и два шаблона redis, один для чтения, а другой для записи? Будет ли это работать (теоретически)? – riship89
да 2 фабрики и шаблоны будут работать. –
alrighty! благодаря! – riship89