Новая функция Spring Boot 2.3.0: динамическое определение топологии Redis

Spring Boot Java

Эта статья является оригинальной статьей. Любая форма перепечатки приветствуется, но обязательно указывайте источник Leng Leng https://lltx.github.io.

Новые функции Spring Boot 2.3, элегантное завершение работы, подробное объяснение

Spring Boot 2.3 Новый многоуровневый JAR с функциями

Эта статья является третьей статьей из серии spring boot v2.3, в которой рассказывается об оптимизации отказоустойчивости v2.3 о Spring Data Redis.

задний план

О Redis В рабочей среде мы обычно выбираем развертывание архитектуры высокой доступности кластера Redis, которая может обеспечить фрагментацию данных и реализовать автоматическое переключение узла при сбое. Базовая топология развертывания выглядит следующим образом:

Создайте тестовый кластер

  • Вот что я инкапсулировалpig4cloud/redis-cluster:4.0Image, вы можете создать тестовую среду кластера Redis с 6 узлами.
docker run --name redis-cluster -d -e CLUSTER_ANNOUNCE_IP=宿主机IP \
-p 7000-7005:7000-7005 -p 17000-17005:17000-17005  pig4cloud/redis-cluster:4.0
  • Просмотр информации об узле кластера
⋊> ./redis-cli -h 172.17.0.111 -p 7000 -c                     16:09:48
172.17.0.111:7000> cluster nodes
3d882206d40935beef84ff564b538d57369e4fd9 172.17.0.111:7003@17003 slave b8d24150df4a221c1045cd9a0696bd1972912d52 0 1591344590000 4 connected
b8d24150df4a221c1045cd9a0696bd1972912d52 172.17.0.111:7001@17001 master - 0 1591344590513 2 connected 5461-10922
c21167a6da7f8af31d2dd612d449cdf92ad2e7e9 172.17.0.111:7005@17005 slave 810baa140db6e008a137708f09d4335f5207ede3 0 1591344591000 6 connected
810baa140db6e008a137708f09d4335f5207ede3 172.17.0.111:7000@17000 myself,master - 0 1591344590000 1 connected 0-5460
05d2f9884d350a50ac9e38f575b57f19e864e74c 172.17.0.111:7004@17004 slave b3cf24a918d96a1949f49a1d7b3a965ff9dc858c 0 1591344590011 5 connected
b3cf24a918d96a1949f49a1d7b3a965ff9dc858c 172.17.0.111:7002@17002 master - 0 1591344591617 3 connected 10923-16383

Кластер доступа прикладного уровня

  • Здесь используется демо Spring Boot 2.2, используется пул соединений по умолчанию.lettuce
spring:
  redis:
    cluster:
      nodes:
        - 172.17.0.111:7000
        - 172.17.0.111:7001
        - 172.17.0.111:7002
        - 172.17.0.111:7003
        - 172.17.0.111:7004
        - 172.17.0.111:7005
  • Простое использование redisTemplate для управления кластером
@RestController
public class DemoController {
    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("/add")
    public String redis() {
        redisTemplate.opsForValue().set("k1", "v1");
        return "ok";
    }
}
  • Звоните для просмотра журнала
⋊> curl http://localhost:8080/add
ok⏎

Мы обнаружим, что операция k1 — это операция записи на 7000 узлов.

[channel=0x5ff7aa8f, /172.17.0.156:50783 -> /172.17.0.111:7000, epid=0x8] write() writeAndFlush command ClusterCommand [command=AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command], redirections=0, maxRedirections=5]
[channel=0x5ff7aa8f, /172.17.0.156:50783 -> /172.17.0.111:7000, epid=0x8] write() done

Моделирование единой точки отказа

  • Отключить 7000 узлов
./redis-cli -h 172.17.0.111 -p 7000 -c
172.17.0.111:7000> SHUTDOWN
  • Просмотрите журнал кластера кластера redis. Docker logs -f redis-cluster.

Мы видим, что выбор кластера завершен и отработка отказа завершена.

23:S 05 Jun 08:24:49.387 # Starting a failover election for epoch 7.
29:M 05 Jun 08:24:49.388 # Failover auth granted to c21167a6da7f8af31d2dd612d449cdf92ad2e7e9 for epoch 7
26:M 05 Jun 08:24:49.388 # Failover auth granted to c21167a6da7f8af31d2dd612d449cdf92ad2e7e9 for epoch 7
23:S 05 Jun 08:24:49.389 # Failover election won: I'm the new master.
23:S 05 Jun 08:24:49.389 # configEpoch set to 7 after successful failover
23:M 05 Jun 08:24:49.389 # Setting secondary replication ID to 5253748ecf5bd7ab3536058fba8cad62d2d5e825, valid up to offset: 1622. New replication ID is 21d6a0b199a1ba655c0279d9c78f9682477ac9a3
23:M 05 Jun 08:24:49.389 * Discarding previously cached master state.
23:M 05 Jun 08:24:49.390 # Cluster state changed: ok
  • Состояние кластера на данный момент. 7005 Переход от ведомого к ведущему

Журнал прикладного уровня

  • Большое количество выходных соединений 7000 узлов являются ненормальными
io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: /172.17.0.111:7000
Caused by: java.net.ConnectException: Connection refused

io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: /172.17.0.111:7000
Caused by: java.net.ConnectException: Connection refused


io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: /172.17.0.111:7000
Caused by: java.net.ConnectException: Connection refused
  • Если вы снова запустите redisTemplate, вы обнаружите, что он застрял, и дождитесь возврата результата.
⋊> curl http://localhost:8080/add
  • Анализ причин

В это время еще работает k1, и он подключен к 7000 узлам по слот-соответствию, и проблема бесконечных попыток переподключения не подключена. Клиент lettuce не обновляется синхронно с состоянием кластера Redis, а отключенный узел удаляется для завершения отработки отказа.

Динамическое зондирование кластерной топологии

Динамическое определение топологии означает, что клиент может динамически изменять состояние узла клиента в соответствии с изменениями кластера кластера Redis и завершать отработку отказа.

нам просто нужноspring boot 2.3.0Эта функция может быть включена в версии.

spring:
  redis:
    lettuce:
      cluster:
        refresh:
          adaptive: true
  • На самом деле, в салате всегда была эта функция официально, но Spring Data Redis не последовал за ней.Подробности см.user-content-refreshing-the-cluster-topology-viewглава

Совместимость со старыми версиями

Нам нужно только обратиться к тому, что мы сделали после включения адаптивного переключателя, и настроить представление топологии для нашего собственного проекта.

	@Bean
	public LettuceConnectionFactory redisConnectionFactory(RedisProperties redisProperties) {
		RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(redisProperties.getCluster().getNodes());

		// https://github.com/lettuce-io/lettuce-core/wiki/Redis-Cluster#user-content-refreshing-the-cluster-topology-view
		ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
				.enablePeriodicRefresh()
				.enableAllAdaptiveRefreshTriggers()
				.refreshPeriod(Duration.ofSeconds(5))
				.build();

		ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
				.topologyRefreshOptions(clusterTopologyRefreshOptions).build();

		// https://github.com/lettuce-io/lettuce-core/wiki/ReadFrom-Settings
		LettuceClientConfiguration lettuceClientConfiguration = LettuceClientConfiguration.builder()
				.readFrom(ReadFrom.REPLICA_PREFERRED)
				.clientOptions(clusterClientOptions).build();

		return new LettuceConnectionFactory(redisClusterConfiguration, lettuceClientConfiguration);
	}

image