Опыт архитектора Netease: яма, на которую наступил redis под Springboot

Java

Поделитесь опытом моих коллег-архитекторов NetEase по использованию Redis под весенней загрузкой~

Во-первых, в нем обобщается однопоточная рабочая модель сервера redis, четыре метода развертывания и сценарии использования redis, а затем с точки зрения исходного кода анализируются некоторые ямки использования redis в Springboot под клиентами jedis и lettuce, особенно в кластерный режим Проблема несовместимости!

Недавно скомпилированные обучающие видеоролики по архитектуре Java и базовые знания о проектах Dachang. Студенты, которые в них нуждаются, могут отправить мне личное сообщение и отправить их вам ~ Давайте учиться и развиваться вместе!

1 однопоточная модель сервера Redis

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p3-tt.byteimg.com/origin/pgc-image/b3645477e82644bfa65c5dea095496cf?from=pc)

Redis использует внутренний обработчик файловых событий для обработки клиентских запросов.Обработчик файловых событий является однопоточным, поэтому Redis называется однопоточной моделью.

Структура обработчика файловых событий состоит из 4 частей: несколько сокетов, мультиплексор ввода-вывода, диспетчер файловых событий, обработчик событий (обработчик ответа на соединение, обработчик запроса на команду, обработчик ответа на команду).

Обработчик файловых событий использует механизм мультиплексирования ввода-вывода для одновременного мониторинга нескольких сокетов и выбирает соответствующий обработчик событий для обработки в соответствии с событиями на сокете.

Клиент Redis подключается к серверу reids через сокеты. Несколько сокетов могут одновременно генерировать разные операции, и каждая операция соответствует разным файловым событиям, но мультиплексор ввода-вывода будет прослушивать несколько сокетов и помещать события, сгенерированные сокетами, в очередь в очереди. , диспетчер событий извлекает событие из очереди за раз и передает событие соответствующему обработчику событий для обработки.

Причина, по которой однопоточная модель Redis также эффективна:

  1. чистая работа с памятью
  2. На основе неблокирующего механизма мультиплексирования ввода-вывода
  3. Один поток позволяет избежать частых проблем с переключением контекста нескольких потоков.

Почему Redis использует однопоточную модель?

Если принята многопоточная модель, процессор должен выполнять переключение контекста.Предполагая, что 1 МБ данных считывается 1000 раз несколькими потоками, тогда имеется 1000 переключений контекста, затем 1500 нс * 1000 = 1500 мкс, и однократно многопоточное чтение Для обработки 1 МБ данных требуется всего 250 мкс, поэтому нет необходимости использовать многопоточность вообще.

Когда уместно использовать многопоточное решение?

Для медленных устройств: дисковых, сетевых, SSD и т. д. запрос не привязан к потоку обработки, запрашивающий поток ставит запрос в бафф, после чего ждет заполнения баффа, а поток обработки обрабатывает баф . Потом бафф равномерно записывается на диск, либо читается с диска, что наиболее эффективно.

Является ли Redis потокобезопасным?

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

2 Метод развертывания Redis

2.1 Режим одного узла

Одноузловой режим имеет только один узел, который обычно используется для тестирования.

2.2 Режим ведущий-ведомый

Режим ведущий-ведомый включает в себя главный узел и несколько подчиненных узлов.Вообще говоря, главный узел используется для операций чтения и записи, а подчиненный узел — операции чтения пользователем.Данные главного узла могут быть синхронизированы с подчиненным узлом, поэтому даже если подчиненный узел поддерживает операции записи, он не работает.

2.3 Сторожевой режим

![Опыт архитектора Netease: яма, на которую наступил Redis под Springboot](https://p1-tt.byteimg.com/origin/pgc-image/418ef7116c464c428bfd6879c44ea748?from=pc)

Режим Sentinel основан на режиме ведущий-ведомый.Sentinel отслеживает состояние узлов ведущий-ведомый для достижения высокой доступности в режиме ведущий-ведомый.Когда часовой обнаруживает, что главный узел не работает, часовой повторно выбирает ведущий из раб.

Вообще говоря, через кластер Sentinel можно управлять несколькими master-slave redis.Sentinel не следует развертывать на той же машине, что и Redis.В противном случае, после зависания сервера Redis, Sentinel также зависает. Использование дозорного кластера также необходимо для обеспечения высокой доступности Redis и предотвращения влияния на использование Redis после зависания дозорного узла.

При использовании режима Sentinel клиент не подключается к Redis напрямую, а подключается к IP-адресу и порту Sentinel.Sentinel предоставляет конкретную реализацию Redis, которая может предоставлять услуги, поэтому, когда главный узел зависает, Sentinel воспринимает и предоставляет новый главный узел для потребителей.

Режим Sentinel может в основном удовлетворить потребности общего производства и имеет высокую доступность. Однако, когда объем данных слишком велик для хранения на одном сервере, режим «ведущий-ведомый» или режим «дозорный» не могут удовлетворить спрос, поэтому сохраненные данные необходимо разбить на сегменты и хранить в нескольких экземплярах Redis.

2.4 Кластерный режим:

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p1-tt.byteimg.com/origin/pgc-image/3cecb10dab164cfa980cb7fb4c910b9f?from=pc)

Появление кластера должно решить проблему ограниченной мощности Redis на одной машине и распределить данные Redis на несколько машин по определенным правилам.

Можно сказать, что кластер представляет собой комбинацию часового режима и режима ведущий-ведомый.Функции главного-ведомого и повторного выбора главного могут быть реализованы через кластер, поэтому, если вы настроите две реплики и три сегмента, вам потребуется шесть экземпляров Redis.

Как показано на рисунке, развернут кластер Redis с тремя ведущими и тремя подчиненными. Кластер Redis имеет фиксированные хеш-слоты 16384. Значение CRC16 вычисляется для каждого ключа, а затем по модулю 16384 для получения хеш-слота, соответствующего ключу. , Хранится в соответствующем слоте.

3 Springboot использует сводку Redis

spring-boot-starter-data-redis поддерживает два клиента Redis: jedis и lettuce

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p3-tt.byteimg.com/origin/pgc-image/90e381e478b74dc3aa7089e59cd8a234?from=pc)

Клиентом по умолчанию, используемым Springboot 2.0, является lettuce. Lettuce используется в качестве клиента по умолчанию. Давайте отследим исходный код, чтобы проанализировать, как Springboot добавляется к клиенту lettuce. Сначала найдите связанный с Redis класс конфигурации загрузки RedisAutoConfiguration в пакете jar автоматически. загружается спрингбутом.

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p6-tt.byteimg.com/origin/pgc-image/e12b2305e3dd44748a044cbf2816ef97?from=pc)

Здесь метод @Configuration @bean используется для внедрения RedisTemplate и StringRedisTemplate в контейнер. Метод внедрения обоих должен быть передан в RedisConnectionFactory. RedisConnectionFactory генерируется LettuceConnectionConfiguration, а JedisConnectionConfiguration импортируется @Import

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p6-tt.byteimg.com/origin/pgc-image/8a26a755349e47efb60941c82d5201c5?from=pc)
![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p3-tt.byteimg.com/origin/pgc-image/380d0aa2f11e4e9c85a05bc1acfee4c7?from=pc)

Видно, что при отсутствии RedisConnectionFactory LettuceConnectionFactory по умолчанию будет внедряться в контейнер Spring.Если вы хотите использовать клиент jedis, вам нужно только вручную настроить JedisConnectionFactory и внедрить его в контейнер.

3.1 Разница между джедаями и салатом

  • Jedis — это реализуемый сервер Redis с прямым подключением. Если он не является потокобезопасным в многопоточной среде, в настоящее время для добавления физических подключений к каждому экземпляру Jedis используется только пул соединений.
  • Соединение Lettuce основано на Netty. К экземпляру соединения (StatefulRedisConnection) можно обращаться одновременно между несколькими потоками. StatefulRedisConnection является потокобезопасным, поэтому экземпляр соединения (StatefulRedisConnection) может удовлетворять одновременный доступ в многопоточной среде. Конечно, это также возможно.Масштабирование дизайна, если одного экземпляра подключения недостаточно, вы можете добавить экземпляры подключения по мере необходимости.

3.2 jedis анализ безопасности без потоков:

Проанализируйте процесс выполнения клиентом jedis каждой команды с точки зрения исходного кода.

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p1-tt.byteimg.com/origin/pgc-image/4213190a7240423389c52e3334d3aff9?from=pc)

Сначала выполните команду с помощью соответствующего метода класса Client

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p6-tt.byteimg.com/origin/pgc-image/d710c432546f4782bcd66f1b46a8b814?from=pc)

Затем выполнить с помощью метода sendCommand класса Connection

![Опыт архитектора Netease: яма, на которую наступил Redis под Springboot](https://p3-tt.byteimg.com/origin/pgc-image/9b383959d0444b55950c08306df92f9d?from=pc)

Метод connect вызывается каждый раз, когда выполняется метод sendCommand.

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p3-tt.byteimg.com/origin/pgc-image/6d1eda17b8a64557bba50ccd8f29bdc9?from=pc)

Как видно из метода подключения, сокет является общей переменной.Если два потока совместно используют экземпляр jedis, соединение сокета еще не установлено, и два потока входят, чтобы установить соединение сокета одновременно.

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p6-tt.byteimg.com/origin/pgc-image/ca386a73ca5b48ad88a94c8849b19ce8?from=pc)

После того, как поток 1 установит сокетное соединение, он начнет получать входной и выходной потоки, в то же время поток 2 повторно инициализирует сокет и не выполняет установку сокетного соединения, в это время поток 1 не сможет получить входной и выходной потоки. входные и выходные потоки, потому что сокет в это время не подключен.

Jedis сам по себе не является многопоточным. Это не ошибка jedis, но дизайн jedis связан с тем, что сам redis является однопоточным. Абстракция экземпляров jedis связана с отправкой команд. Экземпляр jedis использует один поток и использует 100 потоков для отправки Существенной разницы между командами нет, поэтому нет необходимости делать их потокобезопасными. Но что, если вам нужно получить многопоточный доступ к серверу Redis? Затем используйте несколько экземпляров jedis, каждый поток соответствует экземпляру jedis, а не экземпляру jedis, совместно используемому несколькими потоками. Джедис связан с клиентом, что эквивалентно клиенту.Клиент наследует соединение, а соединение поддерживает соединение сокета.Для дорогостоящего соединения сокета оно обычно объединяется.Джедис предоставляет JedisPool.

3.3 Некоторые ямы, используемые джедаями и салатом в кластерном режиме

1. Главный узел Lettuce отключен в режиме кластера, а подчиненный узел обновлен до главного узла Как салат обновляет топологию кластера

Каждый узел в кластере отвечает только за часть слота, и слот может мигрировать с одного узла на другой, в результате чего клиент инициирует запрос к неправильному узлу. Следовательно, должен быть механизм для обнаружения и исправления, которым является перенаправление запроса.

Обновление топологии кластера выполняется в ClusterTopologyRefreshScheduler.Давайте перейдем к классу, чтобы узнать.

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p3-tt.byteimg.com/origin/pgc-image/8e13b9f8135f4c899e16200d86471019?from=pc)
![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p3-tt.byteimg.com/origin/pgc-image/ed42c38f1b9b427d91181574d7204e60?from=pc)

Класс ClusterTopologyRefreshScheduler реализует интерфейс ClusterEventListener для мониторинга событий кластера Redis, включая перенаправление запроса, перенаправление перемещения и повторное подключение.

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p1-tt.byteimg.com/origin/pgc-image/ff0d027024024f12bcdb734893aa03cd?from=pc)

В методе перенаправления сначала вызовите метод isEnabled, чтобы определить, следует ли включить обновление топологии кластера, а затем вызовите метод visibleTopologyRefreshSignal, чтобы обновить топологию кластера.

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p1-tt.byteimg.com/origin/pgc-image/ae9671ebe1cc4ffa9ef0927de1d5b3f1?from=pc)

Чтобы определить, включена ли топология обновления кластера, в зависимости от того, содержит ли триггер адаптивного обновления в ClusterTopologyRefreshOptions указанный триггер перенаправления в конфигурации по умолчанию, как выглядит этот триггер?

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p3-tt.byteimg.com/origin/pgc-image/ba4aee2f4bee44ecb4d91f13adc10ac9?from=pc)

Видно, что триггер для адаптивного обновления по умолчанию пуст, поэтому в режиме кластера, с использованием конфигурации салата по умолчанию, если мастер-нода выйдет из строя, топология кластера не будет обновлена, то есть соединение redis завершится ошибкой.

![Опыт архитектора Netease: яма, на которую наступил Redis под Springboot](https://p1-tt.byteimg.com/origin/pgc-image/753a9441bb7340158a7a5fd212ecf544?from=pc)

Адаптивное обновление топологии кластера можно включить в методе enableAllAdaptiveRefreshTriggers. Как обновить топологию кластера после включения адаптивного обновления?

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p6-tt.byteimg.com/origin/pgc-image/3a55d9c2955f46ce8887977692190ae9?from=pc)

Отправьте clusterTopologyRefreshTask, который обновляет топологию кластера в методе selectedTopologyRefreshSignal.

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p3-tt.byteimg.com/origin/pgc-image/bffc5c6d11a145b291163ed4973f57f7?from=pc)

Вызовите метод reloadPartitions класса RedisClusterClient в задаче, чтобы перезагрузить информацию о топологии кластера для достижения эффекта обновления.

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

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p1-tt.byteimg.com/origin/pgc-image/cf1450853b1a42a086c8d19e73d06094?from=pc)

После включения периодического обновления топологии кластера при инициализации топологии кластера будет вызываться активацияTopologyRefreshIfNeeded для запуска периодического обновления задачи топологии кластера.

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p6-tt.byteimg.com/origin/pgc-image/629b7cbac700444bbc0cecc72f9943b8?from=pc)
![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p6-tt.byteimg.com/origin/pgc-image/dc2fef6cfd814f63b2a790474fe1542d?from=pc)

Здесь будет принято решение о включении периодического обновления, и запланированная задача будет отправлена ​​только после ее включения.

Сравнение периодического обновления и адаптивного обновления: существует два метода периодического обновления и адаптивного обновления. Лучше использовать адаптивное обновление, поскольку необходимо установить цикл периодического обновления. Если параметр слишком длинный, служба может быть недоступна. в течение определенного периода времени. Если параметр слишком короткий, ресурсы являются пустой тратой ресурсов, а адаптивное обновление обновляет топологию кластера на основе ответа сервера.

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

2. Яма, где клиент Jedis выполняет lua-скрипт

Преимущества Redis с использованием lua-скриптов:

  • Уменьшите нагрузку на сеть. Несколько запросов могут быть отправлены в виде сценариев одновременно, чтобы уменьшить задержку в сети.
  • Атомная операция. Redis выполнит весь скрипт как единое целое и не будет вставлен другими командами посередине. Таким образом, нет необходимости беспокоиться об условиях гонки во время написания сценариев и нет необходимости использовать транзакции.
  • повторное использование. Сценарий, отправленный клиентом, постоянно хранится в Redis, поэтому другие клиенты могут повторно использовать сценарий без использования кода для выполнения той же логики.

Как клиент Jedis поддерживает сценарии lua?

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p1-tt.byteimg.com/origin/pgc-image/c329997eb76341c39e1632656259f93b?from=pc)

Jedis выполняет сценарий lua через метод execute класса ScriptExecutor и далее вызывает метод eval в методе

![Опыт архитектора Netease: яма, на которую наступил Redis под Springboot](https://p6-tt.byteimg.com/origin/pgc-image/720ac6755ee244ae86bb1512833c862e?from=pc)

Далее вызовите метод eval класса RedisScriptingCommands, поскольку клиент jedis используется в кластерном режиме, поэтому вызовите метод eval класса реализации JedisClusterScriptingCommands

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p1-tt.byteimg.com/origin/pgc-image/515bc169ff354de9b6d9913c1e22f5b8?from=pc)

Глядя на метод eval класса реализации JedisClusterScriptingCommands, сразу создается исключение, а сценарии не поддерживаются в режиме кластера.

Обходной путь заключается в использовании клиента салата, метод eval в классе LettuceScriptingCommands поддерживает сценарии.

![Опыт архитектора Netease: яма, на которую наступил redis под Springboot](https://p6-tt.byteimg.com/origin/pgc-image/e085c7ea5ce54ca38ed7e782da5faa0a?from=pc)

Смотрите здесь маленьких друзей, если вам понравилась эта статья, не забудьтеВперед, в избранное, взаимодействие с помощью сообщений!

Если у вас есть какие-либо вопросы по статье, пожалуйста, свяжитесь со мной в области сообщений~

Я недавно собрал кое-что новоеДанные Java, в том числе личный обмен, пробные тестовые вопросы и видеогалантерея, если вам это нужно, пришлите мне личное сообщение!

Кроме того, следуйте за мной! Подписывайтесь на меня! Подписывайтесь на меня!