Что такое распределенный кэш

Обновлено: 03.07.2024

Я запутался в концепции распределенного кэша. Я точно знаю, что это из поиска Google. Распределенный кеш может охватывать несколько серверов, чтобы он мог увеличиваться по размеру и в транзакционной емкости. Однако я не понимаю, как это работает или как он распространяет данные.

Например, скажем, у нас есть Data 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 и 2 сервера кеша A и B. Если мы используем распределенный кеш, то одним из возможных решений является то, что данные 1, 3, 5, 7, 9 хранятся в кеш-сервере A, а 2, 4, 6, 8, 10 хранятся в кеш-сервере B.

Так это правильно или я неправильно понял?

Второй вопрос заключается в том, что я обычно слышал слово сервер node. Что это? В приведенном выше примере сервер A является сервером node, правильно?

Третий вопрос, если сервер (скажем, сервер А) опустился, что мы можем с этим поделать? Я имею в виду, что если мой пример выше, мы не можем получить данные 1, 3, 5, 7, 9 из кеша, когда сервер A не работает, то что может сделать сервер Cache Server в этом случае?

ОТВЕТЫ

Ответ 1

Да, половина данных на сервере a, а половина на сервере b будет распределенным кешем. Существует множество способов распространения данных, хотя, по-видимому, наиболее популярен какой-то хэширование ключей.

Термины server и node, как правило, взаимозаменяемы. A node, как правило, является одной единицей некоторой коллекции, которую часто называют кластером. Сервер, как правило, представляет собой единое целое. В erlang вы можете запускать несколько экземпляров среды выполнения erlang на одном сервере и, следовательно, у вас будет несколько узлов erlang. но обычно вы хотите иметь один node на сервер для более оптимального планирования. (Для нераспределенных языков и платформ вам необходимо управлять своими процессами на основе ваших потребностей.)

Если сервер опускается, и он является сервером кэш-памяти, тогда данные должны будут поступать из исходного источника. EG: Кэш обычно представляет собой базу данных на основе памяти, предназначенную для быстрого поиска. Данные в кеше сохраняются только до тех пор, пока они будут использоваться регулярно и в конечном итоге будут очищены. Но для распределенных систем, где вам требуется настойчивость, общий метод состоит в том, чтобы иметь несколько копий. EG: у вас есть серверы A, B, C, D, E и F. Для данных 1 вы должны поместить их в A, а затем сделать копию на B и C. Couchbase и Riak. Для данных 2 он может быть на B, а затем копируется на C и D. Таким образом, если какой-либо один сервер опускается, у вас все еще есть две копии.

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

например, предположим, что у нас есть сведения 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 и 2 сервера кэша A и B. Если мы используем распределенный кэш, то один из возможных решение состоит в том, что данные 1, 3, 5, 7, 9 хранятся на сервере кэша A, а 2, 4, 6, 8, 10-на сервере кэша B.

Так это правильно или я неправильно понял?

второй вопрос в том, что я обычно слышал слово сервер. Что это? В приведенном выше примере сервер-это сервер, верно?

третий вопрос, если сервер (скажем, сервер A) идет вниз, что мы можем сделать с этим? Я имею в виду, если мой пример выше верен, мы не можем получите данные 1, 3, 5, 7, 9 из кэша, когда сервер a не работает, тогда что может сделать сервер кэша в этом случае?

да, половина данных на сервере a и половина на сервере b будут распределенным кэшем. Существует много способов распространения данных, хотя наиболее популярным кажется какое-то хеширование ключей.

термины сервер и узлов взаимозаменяемы. Узел, как правило, является единицей некоторой коллекции, часто называемой кластером. Сервер-это, как правило, единое аппаратное обеспечение. В erlang можно запустить несколько экземпляров Erlang runtime на одном сервере, и, таким образом, у вас будет несколько узлов erlang. но обычно вы хотите иметь один узел на сервер для более оптимального планирования. (Для не-распределенных языков и платформ вы должны управлять процессами на основе ваших потребностей.)

если сервер идет вниз, и это сервер кэша, то данные должны были бы исходить из его исходного источника. Например: кэш обычно представляет собой базу данных на основе памяти, предназначенную для быстрого извлечения. Данные в кэш остается только до тех пор, пока он используется регулярно, и в конечном итоге будет очищен. Но для распределенных систем, где вам нужно постоянство, общий метод должен иметь несколько копий. Например: у вас есть серверы A, B, C, D, E и F. Для данных 1 Вы бы поместили его на A, а затем копию на B и C. Couchbase и Riak делают это. Для данных 2 это может быть на B, а затем копии на C и D. Таким образом, если какой-либо один сервер идет вниз, у вас все еще есть две копии.

Я уже довольно давно использую решения распределенного кэширования (NCache , AppFabric и т. д.), и я собираюсь ответить на все три вопроса, основанные на моем опыте с распределенным кэшированием.

1: Решение распределенного кэширования позволяет хранить данные на всех серверах путем создания кластера кэша. Допустим, у вас есть 2 сервера кэш(серверные узлы) и вы добавили 10 элементов в кэше. В идеале 5 элементов должны присутствовать на обоих узлах сервера с момента загрузки данных получает распределение между количеством серверов в кластере кэша. Обычно это достигается с помощью алгоритмов хэширования и интеллектуального распределения данных. В результате нагрузка на запрос данных также разделяется между всеми серверами кэша и достигается линейный рост транснациональной емкости по мере увеличения количества серверов в кластере кэша.

2: кластер кэша может содержать много серверных машин, которые также называются серверными узлами. Да, сервер это сервер или сервер в вашем примере.

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

в вашем примере, данные серверу(1, 3, 5, 7, 9) реплицируется на сервер B(2, 4, 6, 8, 10) и наоборот. Если сервер A отключится, данные сервера A, присутствующие на сервере B, будут доступны и использованы оттуда, чтобы не произошло потери данных. Поэтому, если сервер A отключается и приложение запрашивает данные (1), эти данные будут получены с сервера B, поскольку сервер B содержит резервную копию всех данных сервера A. Это бесшовные для ваших приложений и управляется автоматически системой кэширования.

 1

Общий процесс интернет-приложений (веб-сайт / приложение) можно резюмировать, как показано на рисунке:

Пользователь запрашивает из интерфейса (браузер / приложение) пересылку по сети, службы приложений в хранилище (базу данных или файловую систему), а затем возвращается к интерфейсу для представления контента.

Как показано на рисунке 1, использование кэширования может появляться в каждой ссылке от 1 до 4. Схема кэширования и использование каждой ссылки имеют свои особенности.

1. Характеристики кеша

Кэш является объектом модели данных и имеет некоторые из его характеристик.

1.1 Скорость попадания

Частота совпадений = количество возвращенных правильных результатов / количество запросов к кешу
Проблема частоты совпадений - очень важная проблема в кэше, и это важный индикатор для измерения эффективности кеша. Чем выше частота попаданий, тем выше коэффициент использования кеша.

1.2 Самый большой элемент (или самое большое пространство)

Максимальное количество элементов, которые могут быть сохранены в кеше. Когда количество элементов в кэше превышает это значение (или пространство, занимаемое кэшируемыми данными, превышает максимальное поддерживаемое пространство), кеш будет запущен. Пустая стратегия , Разумная установка максимального значения элемента в соответствии с различными сценариями часто может в определенной степени улучшить частоту попаданий в кэш, чтобы использовать кеш более эффективно.

1.3 Клиринговая стратегия

Объем кеш-памяти ограничен.Когда пространство кеш-памяти заполнено, как обеспечить стабилизацию службы и эффективное увеличение частоты обращений?
Это обрабатывается стратегией очистки кеша.
Общие общие стратегии:

FIFO(first in first out)
Первые данные, которые попадают в кэш, будут очищены первыми, если места в кэше недостаточно (превышает максимальный предел элемента), чтобы освободить место для новых данных.
Алгоритм стратегии в основном сравнивает время создания элементов кеша. в Требования к эффективности данных Вы можете выбрать этот тип стратегии, чтобы отдать приоритет доступности последних данных.

LFU(less frequently used)
Независимо от того, истек срок его действия или нет, он оценивается по количеству использований элемента, а менее используемые элементы очищаются, чтобы освободить место.
Алгоритм стратегии в основном сравнивает hitCount (количество совпадений) элементов. Гарантия Сценарии высокочастотной достоверности данных , Можете выбрать такую ​​стратегию.

LRU(least recently used)
Независимо от того, истекает ли срок его действия, в соответствии с меткой времени, когда элемент использовался в последний раз, очистите элемент с самой дальней используемой меткой времени, чтобы освободить место.
Алгоритм стратегии в основном сравнивает время, когда элемент последний раз использовался функцией get. в Сценарий горячих данных Более применимо, приоритет отдается обеспечению достоверности данных о точках доступа.

Кроме того, есть несколько простых стратегий, таких как:

  • По сроку годности убирать элементы с наибольшим сроком годности
  • В соответствии со сроком действия очистите элементы, срок действия которых истекает недавно.
  • Случайная уборка
  • Очистите в соответствии с длиной ключевых слов (или содержанием элемента) и т. Д.

2. Кэшировать медиа

От Аппаратная среда С точки зрения памяти и жесткого диска;
из технологии Можно разделить на память, файл жесткого диска, базу данных

Память: хранить кеш в памяти Самый быстрый s Выбор, Нет дополнительных накладных расходов ввода / вывода , Но недостатком памяти является Нет настойчивости Приземленный физический диск, после того, как приложение выйдет из строя и перезапустится, данные будет трудно или невозможно восстановить.

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

База данных: как упоминалось ранее, одна из целей увеличения стратегии кэширования - уменьшить нагрузку на ввод-вывод базы данных. Возвращается ли использование баз данных в качестве носителя кэш-памяти к старой проблеме? На самом деле существует множество типов баз данных, например, те специальные базы данных, которые не поддерживают SQL, но имеют простые структуры хранения значений ключа (например, BerkeleyDB и Redis). скорость ответа с участием Пропускная способность Они намного выше, чем наши обычно используемые реляционные базы данных.

3. Классификация кеша и сценарии приложений.

По степени связи между кешем и приложением он делится на локальный кеш с участием удаленный кеш (распределенный кеш) 。

Локальный кеш: Относится Кеширование компонентов в приложении , Его самым большим преимуществом является то, что приложение и кеш находятся в одном процессе, кэш запросов работает очень быстро и нет чрезмерных сетевых накладных расходов. Более целесообразно использовать локальный кеш в сценариях, когда отдельное приложение не требует поддержки кластера или когда узлам не нужно уведомлять друг друга. ; В то же время его недостатки Поскольку кеш связан с приложением, несколько приложений не могут напрямую совместно использовать кеш. Каждое приложение или узел кластера должны поддерживать свой собственный отдельный кеш, что является пустой тратой памяти. 。

Распределенный кеш: Относится к и Отдельный компонент или служба кэширования приложения , Его самым большим преимуществом является то, что это независимое приложение, Изолированные от локальных приложений, несколько приложений могут напрямую совместно использовать кеш 。

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

3.1 Локальный кеш

3.1.1 Программирование напрямую реализует кеш

В отдельных сценариях нам нужна только простая функция кэширования данных, не обращая внимания на такие подробные функции, как дополнительные стратегии доступа и опустошения.Прямое программирование для достижения кэширования является наиболее удобным и эффективным.

  • Реализация переменной-члена или локальной переменной
  • Реализация статической переменной
    Наиболее часто используемый синглтон реализует статическое кэширование ресурсов:

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

Чтобы решить проблему локально кэшированных данных в реальном времени, в настоящее время большое количество применений сочетается с механизмом автоматического обнаружения ZooKeeper для изменения кеша локальных статических переменных в реальном времени:

 2 Mtconfig

Компонент базовой конфигурации Meituan MtConfig использует аналогичный принцип, используя кэширование статических переменных в сочетании с унифицированным управлением ZooKeeper для автоматического динамического обновления кеша.

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

3.1.2 Ehcache

Ehcache - самая популярная среда кэширования с открытым исходным кодом на чистом Java. Она имеет простую конфигурацию, понятную структуру и мощные функции. Это очень легкая реализация кеширования. Наш обычно используемый Hibernate объединяет связанные функции кэширования.

 3 Ehcache


Основное определение Ehcache в основном включает:

диспетчер кеша: диспетчер кеша, ранее разрешался только синглтон, теперь это может быть несколько экземпляров

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

element: Составная единица единичных данных кэша.

Система записи (SOR): компонент, который может извлекать реальные данные, которые могут быть реальной бизнес-логикой, вызовами внешнего интерфейса, базой данных, хранящей реальные данные и т. Д. Кэш считывается из SOR или записывается в SOR.

Основные особенности:

Быстрый, для крупномасштабных сценариев системы с высоким уровнем параллелизма, многопоточный механизм Ehcache имеет соответствующую оптимизацию и улучшение.

Простой, небольшой пакет jar, простая конфигурация может использоваться напрямую, без слишком многих других зависимостей служб в автономном сценарии

Поддержка различных стратегий кеширования, гибкость

Существует два уровня кеширования данных: память и диск. По сравнению с обычным кешем локальной памяти, с дисковым пространством для хранения, он сможет поддерживать больший объем требований к кэшированию данных.

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

Поддерживает несколько экземпляров диспетчера кеша и несколько областей кеша одного экземпляра

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

Для получения дополнительных инструкций по Ehcache перейдите по ссылке.

3.1.3 Guava Cache

Guava Cache - это инструмент кэширования в Guava, библиотеке набора инструментов повторного использования Java с открытым исходным кодом от Google. Реализованные функции кеширования:

Автоматически загружать входной узел в структуру кеша

Когда кэшированные данные превышают установленное максимальное значение, используйте алгоритм LRU для удаления

У него есть механизм истечения срока для вычисления узла входа на основе последнего доступа или записи.

Кэшированный ключ инкапсулирован в справке WeakReference

Кэшированное значение инкапсулируется в ссылки WeakReference или SoftReference.

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

Архитектурный дизайн Guava Cache вдохновлен ConcurrentHashMap.
Как мы упоминали ранее, в простых сценариях вы можете закодировать небольшой объем данных с помощью хэш-карты для кеширования небольшого объема данных, но если результат может измениться со временем или в пространстве данных, вы хотите сохранить управляемо, необходимо реализовать эту структуру данных самостоятельно.

Guava Cache наследует идею ConcurrentHashMap и использует детализированные блокировки в нескольких сегментах для обеспечения безопасности потоков при поддержке сценариев с высоким уровнем параллелизма.
Кэш аналогичен Map, который представляет собой набор пар "ключ-значение". Разница в том, что он также должен обрабатывать логику алгоритма, такую ​​как выселение, истечение срока действия и динамическая загрузка, а также требует некоторой дополнительная информация для выполнения этих операций. В связи с этим, согласно объектно-ориентированному мышлению, необходимо связывать методы и инкапсуляцию данных.

На рисунке 5 показана модель данных кеш-памяти. Можно увидеть, что интерфейс ReferenceEntry используется для инкапсуляции Пара "ключ-значение" , И используйте ValueReference для инкапсуляции Ценность 。



ReferenceEntryЭто абстракция узла пары ключ-значение, который содержит абстрактный класс Key и Value ValueReference.
Кэш состоит из нескольких сегментов, каждый из которых содержит массив ReferenceEntry, а каждый элемент массива ReferenceEntry представляет собой цепочку ReferenceEntry. ReferenceEntry содержит поля key, hash, valueReference и next. В дополнение к цепочке, сформированной в элементах массива ReferenceEntry, в сегменте все ReferenceEntry также образуют цепочку доступа (accessQueue) и цепочку записи (writeQueue)
ReferenceEntry может быть ключом сильного ссылочного типа или ключом типа WeakReference. Чтобы уменьшить использование памяти, вы также можете определить, нужна ли вам цепочка записи, а цепочка доступа определяет конкретную ссылку, которая будет созданы: StrongEntry, StrongWriteEntry, StrongAccessEntry, StrongWriteAccessEntry и т. д.
Для некоторых других инструкций по Guava Cache, пожалуйста, перейдите по ссылке

Меня смущает концепция распределенного кэша. Я вроде как знаю, что это, из поиска Google. Распределенный кеш может охватывать несколько серверов, поэтому он может увеличиваться в размере и увеличивать объем транзакций. Однако я не совсем понимаю, как это работает и как распределяет данные.

Например, предположим, что у нас есть Data 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 и 2 кэш-сервера A и B . Если мы используем распределенный кеш, то одно из возможных решений состоит в том, что данные 1, 3, 5, 7, 9 хранятся на кэш-сервере A, а 2, 4, 6, 8, 10 хранятся на кэш-сервере B.

Так это правильно или я неправильно понял?

Второй вопрос: я обычно слышал слово серверный узел . Что это? В приведенном выше примере сервер A является серверным узлом, верно?

Третий вопрос: если сервер (скажем, сервер A) выйдет из строя, что мы можем с этим поделать? Я имею в виду, что если мой пример, приведенный выше, верен, мы не можем получить данные 1, 3, 5, 7, 9 из кеша, когда сервер A не работает, что тогда может сделать Cache Server в этом случае?

2 ответа

Да, половина данных на сервере a и половина данных на сервере b будет распределенным кешем. Существует множество методов распределения данных, но наиболее популярным представляется какое-то хеширование ключей.

Термины сервер и узел обычно взаимозаменяемы. Узел обычно представляет собой отдельную единицу некоторой коллекции, часто называемой кластером. Сервер обычно представляет собой единое оборудование. В erlang вы можете запускать несколько экземпляров среды выполнения erlang на одном сервере, и, таким образом, у вас будет несколько узлов erlang . но, как правило, вам нужно иметь по одному узлу на сервер для более оптимального планирования. (Для нераспределенных языков и платформ вы должны управлять своими процессами в соответствии с вашими потребностями.)

Если сервер выходит из строя, и это кэш-сервер, то данные должны поступать из исходного источника. EG: Кэш - это обычно база данных на основе памяти, предназначенная для быстрого поиска. Данные в кэше хранятся только до тех пор, пока они используются регулярно, и в конечном итоге будут очищены. Но для распределенных систем, где требуется постоянство, обычно используется несколько копий. EG: у вас есть серверы A, B, C, D, E и F. Для данных 1 вы поместите их на A, а затем копию на B и C. Couchbase и Riak делают это. Для данных 2 они могут быть на B, а затем копироваться на C и D. Таким образом, если какой-либо один сервер выходит из строя, у вас все еще есть две копии.

Я уже довольно давно использую решения для распределенного кэширования (NCache, AppFabric и т. Д.), И я собираюсь ответить на все три вопроса, основываясь на моем опыте работы с распределенным кешированием.

1: Решение с распределенным кэшированием позволяет хранить данные на всех серверах, создавая кластер кеша. Допустим, у вас есть 2 кеш-сервера (серверные узлы), и вы добавили 10 элементов в свой кеш. В идеале на обоих серверных узлах должно присутствовать 5 элементов, поскольку нагрузка данных распределяется между количеством серверов в вашем кластере кеша. Обычно это достигается с помощью алгоритмов хеширования и интеллектуального распределения данных. В результате нагрузка на ваши запросы данных также распределяется между всеми серверами кэширования, и вы достигаете линейного роста транснациональной емкости по мере увеличения количества серверов в кластере кеша.

2: Кластер кеша может содержать множество серверных машин, которые также называются серверными узлами. Да, Сервер A - это серверный узел или серверная машина в вашем примере.

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

В вашем примере данные сервера A (1, 3, 5, 7, 9) реплицируются на сервер B (2, 4, 6, 8, 10) и наоборот. Если сервер A выходит из строя, данные сервера A, который присутствует на сервере B, будут доступны и использоваться оттуда, чтобы не произошло потери данных. Таким образом, если сервер A выходит из строя и приложение запрашивает данные (1), эти данные будут извлечены с сервера B, поскольку сервер B содержит резервную копию всех данных сервера A. Это легко для ваших приложений и автоматически управляется кэшированием. система.

Обработка аутентификации и авторизации пользователей после балансировки нагрузки веб-приложения

Балансировка нагрузки необходима, если в приложении ожидается или уже есть большой объем трафика, но дело усложняется, когда доходит до разработки потока аутентификации и авторизации.

Зачем балансировать нагрузку?

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

Что такое балансировка нагрузки?

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


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


Балансировка нагрузки API

Как видите, даже если Боб отправляет два запроса к одному и тому же API за очень небольшой промежуток времени, ответ все равно обрабатывается разными экземплярами сервера. Значит, вы никак не можете предсказать, какой экземпляр в конечном итоге обработает запрос клиента. А даже если и можете, то это уже вряд ли получится, если балансировщик нагрузки будет делать свою работу как следует.

Аутентификация и авторизация

JWT, разумеется

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

И он работает. Причём быстро и вполне надёжно.


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

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

Как вы реализуете истечение доступа для вашего пользователя, не дожидаясь, пока он выйдет сам? Быстрый способ — изменить ключ шифрования. Но подождите, это приведет к истечению срока действия всех токенов ваших пользователей… Есть другая идея. Может быть, интегрировать базу данных, которая содержит все просроченные ключи, и проверять по ней? Или, может быть, в базе будут все действительные ключи? Как было бы просто: сначала проверьте, действителен ли сам JWT, а затем проверьте по базе данных, не занесен ли он в черный список.

Но подождите, разве так не отправляются в мусор пресловутые преимущества JWT — “быстродействие, отсутствие необходимости обращения к базе данных, скорость проверок”? В некотором роде. Тогда, скажете вы, надо добавить кэширование, чтобы уменьшить серфинг по базе данных. Да, это сработает, но имейте в виду: кэш также необходимо разделять между несколькими экземплярами веб-сервера, и реализация должна быть отказоустойчивой, особенно для функций, связанных с безопасностью пользователя (к примеру, сброс пароля).

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

Sticky session

“Липкие сессии” пытаются решить эту проблему, заставляя балансировщик нагрузки отправлять все последующие запросы на один и тот же веб-сервер. Частично это срабатывает, но помимо вопиющей лени (в нормальных обстоятельствах) у этого подхода также есть несколько других недостатков:

  • Ваши серверы будут загружены неравномерно. Некоторые пользователи проведут на вашем сайте больше времени, чем другие.
  • Балансировщик нагрузки не станет читать и обрабатывать запрос, это не его работа. Определить, откуда поступает запрос, он может только по IP-адресу, и не думаю, что мне нужно говорить вам, сколько людей используют данные с мобильных телефонов, а затем переключаются на wi-fi в кафе. В результате сессия теряется. Одним словом: неконсистентность.

Распределенные сессии

Другой достойный участник этого конкурса — распределенный кэш + состояние сеанса = распределенные сессии. По факту, сейчас состязание идет между JWT и распределенным состоянием сессии, поскольку в среде с балансировкой нагрузки и тому, и другому необходим распределенный кэш.

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

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

Вы наверняка уже догадались, но всё-таки: что такое распределенный кэш? Распределенный кэш — это просто кэш, доступный нескольким сторонам. Он предоставляет слой согласованных данных между всеми вашими серверами. Применение простое: сервер ищет некоторую информацию, и если она кэширована, то забирает ее, и на том конец. Если это не так, то сервер получает информацию из постоянного источника (базы данных), кэширует, чтобы в будущем получить к ней скорейший доступ, а затем использует. Что особенно важно для хранения состояния сессии, срок действия данных в кэше истекает нелинейно, так что они не остаются там, когда не нужны. Если к кэшу не обращались в течение двадцати минут, то он удаляется.

Простой пример применения сессии с распределенным кэшированием для быстрого извлечения данных

Как это сочетается с сессиями? Очень просто: вы храните сессии пользователей в распределенном кэше, а не в локальном. Пользователи делают запрос, и пользовательские сессии создаются и хранятся внутри кэш-сервера. В случае, когда последующие запросы перенаправляются на другой экземпляр, веб-сервер получает доступ к сессии клиента, извлекая состояние из распределенного кэша. Когда пользователь выполняет какое-либо действие, влияющее на его данные, изменение записывается и обновляется как в базе данных, так и в кэше. Если такие изменения базы данных выполняются асинхронно, то конечный пользователь никак не пострадает в плане скорости загрузки страницы.

Можно возразить: метод кэширования черного списка JWT предлагает те же преимущества — меньше запросов к базе данных, меньше кэширования, и очень быстрые ответы как следствие. Отчего отдавать победу именно распределенным сессиям? Есть несколько причин.

Прежде всего, JWT создавался не для того. JWT-токены должны быть переносимыми, быстрыми и простыми в проверке. Добавление еще одного уровня проверки с помощью хранилища данных (черный список или белый список) — то же самое, что реализация распределенного состояния сессии, но вместо того, чтобы пользовательская информация хранилась красиво упакованной и скрытой внутри хранилища данных, вы оставляете ее открытой в файле cookie на стороне клиента, а также храните копию JWT у себя в хранилище данных. Зачем оставлять информацию о пользователе на стороне клиента, если она уже надежно скрыта от публичного доступа, даже если данные не предполагаются настолько тайными?

Кроме того, реализация состояния сессии на стороне сервера расширяет возможности для внедрения большего количества функций в дальнейшем. В частности, открывает путь к работе с более деликатными данными о сессии клиента для усиления безопасности (например, IP-адрес последней авторизации, местоположение и т. д.). Если у вас в приложении есть функция публикации заметок, хранение содержимого заметки внутри сессии — гораздо более быстрое решение в плане поиска, чем запрос к базе данных.

Заключение

Балансируете нагрузку огромного сервиса, который требует аутентификации и авторизации? Кэш — король. Не только в плане аутентификации/авторизации.

Читайте также: