Рубрики
Без рубрики

Реализация распределенного кэша LRU с использованием ZooKeeper

В этом руководстве мы рассмотрим, как создать распределенный кэш (LRU: Наименее недавно используемый)… С тегами java, cache, docker, zookeeper.

В этом руководстве мы рассмотрим, как создать распределенный кэш ( LRU : Наименее недавно использовавшийся) с использованием Смотритель зоопарка для выборов лидера.

Перед началом работы вы можете ознакомиться с исходным кодом проекта по следующей ссылке https://github.com/zakariamaaraki/Distributed-LRU-Cache

Что такое смотритель зоопарка

Apache ZooKeeper – это волонтерский проект с открытым исходным кодом в рамках Apache Software Foundation. По сути, это сервис для распределенных систем, предлагающий иерархическое хранилище ключей и значений.

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

Чтобы создать наш распределенный кэш, мы собираемся использовать две функции, предоставляемые ZooKeeper:

  • Лидер-выборы чтобы избрать мастера (лидера) (нам нужен только один экземпляр для разговора. Почему? мы увидим это после)
  • Обнаружение службы/реестр , чтобы иметь возможность пересылать запросы ведущему (ведущему)

Почему выборы лидера?

Предположим, у нас есть два экземпляра (A и B), с которыми мы взаимодействуем, мы также предполагаем, что оба экземпляра всегда синхронизированы. Если мы отправим запрос 1 :Установить(x,1) в экземпляр A , затем к экземпляру B запрос 2 :Set(x,2), нет никакой гарантии, что запрос 1 выполняется перед запросом 2 , по нескольким причинам, возможно, например A сбой и перезапуск или, возможно, из-за задержки.

Таким образом, решение этой проблемы состоит в том, чтобы иметь только один экземпляр для общения, мы можем решить эту проблему, выбрав один экземпляр в качестве ведущего (лидера), и нам разрешено разговаривать только с ведущим.

Хорошо, но что произойдет, если этот экземпляр выйдет из строя (это единственная точка отказа), это нехорошо!! чтобы решить эту проблему, нам нужен автоматический механизм, который выбирает лидера всякий раз, когда экземпляр лидера выходит из строя.

Существует множество алгоритмов для проведения выборов лидера, например:

Выборы лидера с смотрителем зоопарка

Провести выборы лидера с помощью ZooKeeper очень просто. Простой способ сделать это – использовать флаги sequence & ephemeral при создании узлов, представляющих предложения |/клиентов. Идея состоит в том, чтобы иметь znode, скажем /cache-election , такой, чтобы каждый узел создавал дочерний znode cache-election/p-0000X С обоими флагами последовательность и эфемерный . С помощью флага последовательности ZooKeeper автоматически добавляет порядковый номер, который больше, чем любой ранее добавленный к дочернему элементу /cache-election . Экземпляр, создавший znode с наименьшим добавленным порядковым номером, является лидером.

Обнаружение службы

В то же время мы используем ZooKeeper для обнаружения служб с помощью Spring Cloud Zookeeper, который использует это расширение для регистрации и обнаружения служб.

Согласованность против доступности

Обратите внимание, что согласно теореме CAP , ZooKeeper – это система |/CP . Это означает, что он жертвует доступностью для достижения согласованности и устойчивости к разделению. Другими словами, если он не может гарантировать правильное поведение, он не будет отвечать на запросы. Использование обнаружения службы с системой CP – не очень хорошая идея, поэтому, возможно, было бы лучше, если бы мы использовали Eureka (который является AP system) в качестве службы обнаружения/реестра вместо ZooKeeper (чтобы гарантировать доступность), но в этом случае нам нужно будет настроить другой кластер.

Реализация

ГРУБЫЕ операции

Структура данных кэша

Java.util. Метод LinkedHashMap.removeEldestEntry() в Java используется для отслеживания того, удаляет ли карта какую-либо самую старую запись с карты. Таким образом, каждый раз, когда в LinkedHashMap добавляется новый элемент, самая старая запись удаляется с карты. Этот метод обычно вызывается после добавления элементов в карту с помощью методов put() и putall().

public class Cache {

    public boolean isLeader;

    public Long startUpTime;

    private Map map;

    public Cache(int capacity) {

        this.startUpTime = System.currentTimeMillis();

        map = new LinkedHashMap(capacity, 0.75f, true) {
            protected boolean removeEldestEntry(Map.Entry eldest) {
                return size() > capacity;
            }
        };
    }

    public V get(K key) {
        return map.get(key);
    }

    public boolean containsKey(K key) {
        return map.containsKey(key);
    }

    public V remove(K key) {
        return map.remove(key);
    }

    public V set(K key, V value) {
        return map.put(key,value);
    }

    public String toString() {
        return map.toString();
    }

}

Фрагмент кода выборов лидера

private void attemptForLeaderPosition() {

        final List childNodePaths = zooKeeperService.getChildren(LEADER_ELECTION_ROOT_NODE, false);

        Collections.sort(childNodePaths);

        int index = childNodePaths.indexOf(processNodePath.substring(processNodePath.lastIndexOf('/') + 1));
        if(index == 0) {
            log.info("[Process: " + nodeId + "] I am the new leader!");
            cache.isLeader = true;
        } else {
            final String watchedNodeShortPath = childNodePaths.get(index - 1);
            cache.isLeader = false;

            watchedNodePath = LEADER_ELECTION_ROOT_NODE + "/" + watchedNodeShortPath;

            log.info("[Process: " + nodeId + "] - Setting watch on node with path: " + watchedNodePath);
            zooKeeperService.watchNode(watchedNodePath, true);
        }
    }

Есть о чем поговорить, поэтому я предлагаю вам ознакомиться со всем кодом системы, перейдя по этой ссылке: https://github.com/zakariamaaraki/Distributed-LRU-Cache

Оригинал: “https://dev.to/zakariamaaraki/implementation-of-distributed-lru-cache-using-zookeeper-159b”