Spring Data Redis主从配置

3
以下是我的jedis配置:
@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主服务器和多个Redis从服务器。根据Redis文档,读应该发生在从服务器上,写应该发生在主服务器上。我如何改变上面的配置来使用主服务器进行写入和使用从服务器进行读取?

假设我的主服务器位于192.168.10.10,从服务器位于localhost。

谢谢!

4个回答

5

目前在Spring Data Redis中没有配置选项可以启用所需的行为。同时,Jedis本身也不支持这种情况(请参见jedis #458)。 RedisConnection在执行操作时从工厂请求连接。此时请求的资源的使用目的不明确,因为命令可以是rwrw

一个潜在的解决方案是自定义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用于专门的读取写入目的也是有意义的。

两个Jedis连接工厂和两个Redis模板怎么样?一个用于读取,另一个用于写入。理论上可行吗? - hrishikeshp19

4

为了保持Redis的主/从配置,您应该使用Redis Sentinel...您可以使用以下内容连接到Sentinel池-

@Bean
public RedisConnectionFactory jedisConnectionFactory() {
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration() .master("mymaster")
    .sentinel("127.0.0.1", 26379) .sentinel("127.0.0.1", 26380);
return new JedisConnectionFactory(sentinelConfig);
}

reference: Spring Sentinel support


2
仅使用Redis Sentinel支持,当Sentinel将新节点提升为主节点时,您可以获得故障转移,但您无法实现负载均衡,所有请求仍然会发送到主节点。 - dan carter

2
你想使用"写入主节点,从副本读取"功能。以下是如何在Sentinel集群和Spring Boot中配置它:

你需要参考文档

spring:
  data:
    redis:
      sentinel: 
        master: mymaster
        nodes: my.sentinel.hostname1:26379,my.sentinel.hostname2:26379
      port: 6379

And spring config

@Configuration
public class RedisDatasourceConfig {

  @Bean
  public LettuceClientConfigurationBuilderCustomizer lettuceClientConfigurationBuilderCustomizer() {
    return p -> p.readFrom(SLAVE_PREFERRED);
  }
}

0

使用主/从架构进行复制与Sentinel不同。

设置Redis只读副本非常容易。

我的Spring Boot应用程序yaml文件。

redis:
  master:
    host: localhost
    port: 6379
  slaves:
    - host: localhost
      port: 16379
    - host: localhost
      port: 26379

对于上述实例,从属实例的Redis配置应如下所示进行更新。

################################# REPLICATION #################################

# Master-Replica replication. Use replicaof to make a Redis instance a copy of
# another Redis server. A few things to understand ASAP about Redis replication.
#
#   +------------------+      +---------------+
#   |      Master      | ---> |    Replica    |
#   | (receive writes) |      |  (exact copy) |
#   +------------------+      +---------------+
#
# 1) Redis replication is asynchronous, but you can configure a master to
#    stop accepting writes if it appears to be not connected with at least
#    a given number of replicas.
# 2) Redis replicas are able to perform a partial resynchronization with the
#    master if the replication link is lost for a relatively small amount of
#    time. You may want to configure the replication backlog size (see the next
#    sections of this file) with a sensible value depending on your needs.
# 3) Replication is automatic and does not need user intervention. After a
#    network partition replicas automatically try to reconnect to masters
#    and resynchronize with them.
#
replicaof master 6379

点击此处查看 Docker 设置。

https://www.vinsguru.com/redis-master-slave-with-spring-boot/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接