java – spring data redis master slave config
Here is my jedis configuration
@Bean public JedisConnectionFactory getJedisConnectionFactory() { JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(); jedisConnectionFactory.setUsePool(true); return jedisConnectionFactory; } @Bean public Redistemplate<String,Object> getRedistemplate() { Redistemplate<String,Object> redistemplate = new Redistemplate<String,Object>(); redistemplate.setConnectionFactory(getJedisConnectionFactory()); return redistemplate; }
This configuration works well when I have a single server What I want to do is to have one redis master and multiple redis slave According to redis documents, reads should occur from slave and writes should occur from master How do I change the above configuration to use master for writing and slave for reading?
It can be said that my master is at 192.168 10.10, slave in localhost
thank you!
Solution
At present, there are no configuration options in spring data redis that can realize the required behavior Jedis also did not support this situation (see jedis #458)
One possible solution is to customize redisconnectionfactory, which can provide connection - a slave device you own when you execute read-only commands
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); } } }
The above solution has several problems For example Multiple exec blocks in your application may no longer work as expected because commands may now be sent somewhere you don't want them Therefore, it may make sense to use multiple redistemplates for dedicated read and write purposes