本文介绍了spring数据redis主从配置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是我的绝地配置

@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;
}

当我只有一台服务器时,此配置运行良好.我想要做的是有 1 个 redis master 和多个 redis slave.根据 redis 文档,读取应该从奴隶发生,写入应该发生在主人身上.如何更改上述配置以使用 master 进行写入和使用 slave 进行读取?

This config works well when I have single server. What I want to do is have 1 redis master and multiple redis slaves. Per redis documentation, read should happen from slaves, and write should happen from master. How do I change above configuration to use master for writing and slave for reading?

假设我的主人在 192.168.10.10,奴隶在 localhost.

Lets say that my master is at 192.168.10.10 and slave is at localhost.

谢谢!

推荐答案

目前 Spring Data Redis 中没有配置选项可以启用所需的行为.Jedis 本身也不提供对这种场景的支持(参见 jedis #458).RedisConnection 在执行操作时向工厂请求连接.此时请求的资源的用途还不清楚,因为命令可能是rwrw.

At this time there's no configuration option in Spring Data Redis that would enable the desired behaviour. Nor does Jedis iteself offer support for this kind of scenario (see jedis #458).RedisConnection requests a connection from the factory when executing operations. At this point the usage purpose of the resource requested it is not clear as the command could be r, w or rw.

一个潜在的解决方案是自定义 RedisConnectionFactory 能够提供连接 - 到您拥有的一个从站 - 在执行只读命令的情况下.

One potential solution would be a custom RedisConnectionFactory capable of providing the connection - to one of the slaves you have - in case of a readonly command being executed.

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 用于专门的读取写入目的也是有意义的.

There solution above holds serveral issues. Eg. MULTI EXEC blocks in you application might no longer work as expected since commands now potentially get piped somewhere you do not want them to be. So maybe it would also make sense to have multiple RedisTemplates for dedicated read, write purpose.

这篇关于spring数据redis主从配置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-07 03:45