Распределенный (1) Получение регистрации и обнаружения службы

Java задняя часть алгоритм ZooKeeper
Распределенный (1) Получение регистрации и обнаружения службы

задний план

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

Впрочем, впереди тоже много интересного, и я поделюсь им в будущем, а сегодня сосредоточусь на обсуждении сервиса.Зарегистрируйтесь и откройте для себя.

Распределенные проблемы

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

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

Но это будет иметь очень серьезную проблему:

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

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

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

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

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

Как показано ниже:

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

Но будут и новые проблемы: если узел поставщика услуг будет добавлен или удален, потребитель вообще не будет знать о ситуации.

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

Вышеприведенная схема определенно не идеальна и не элегантна. Основные моменты заключаются в следующем:

  • На основе запланированных задач может привести ко многим недопустимым обновлениям.
  • Временные задачи являются периодическими и не могут быть реализованы в режиме реального времени, поэтому могут возникать аномальные запросы.
  • Если служба принудительно убита, Redis не может быть вовремя очищен, поэтому эта, казалось бы, доступная служба никогда не будет доступна!

Так что нам нужно более надежное решение.Этот сценарий на самом деле очень похож на Dubbo.

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

Цитата из ДуббоОфициальный сайт

Одним из основных элементов (выделено красным) является регистрация и обнаружение сервисов.

Вообще говоря, потребители должны знать сетевой адрес (ip + порт) поставщика услуг, чтобы инициировать удаленный вызов.Это содержание на самом деле очень похоже на мои требования выше.

Dubbo использует Zookeeper для решения проблемы.

Что может зоопарк

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

Zookeeper реализует древовидную структуру, похожую на файловую систему:

Эти узлы называются znodes (неважно, как называется), и каждый узел может хранить определенный объем данных.

Главное, что есть четыре вида знодов:

  • Постоянный узел (если узел не удален вручную, узел существует вечно)
  • Постоянно упорядоченные узлы (в соответствии с порядком создания в конец каждого узла будет добавлен порядковый номер, например:root-1)
  • Мгновенные узлы (узлы существуют при создании клиента для поддержания соединения с Zookeeper и будут удалены при отключении с соответствующими уведомлениями)
  • Мгновенные упорядоченные узлы (добавление порядка к переходным узлам)

Какая самая большая проблема с использованием Redis, учитывая вышеизложенное?

На самом деле информация поставщика услуг не может обновляться в режиме реального времени.

Как это реализовано с помощью Zookeeper?

В основном обратите внимание на третью функцию:мгновенный узел

Zookeeper — типичный шаблон наблюдателя.

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

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

И независимо от причины, информация будет удалена в Zookeeper после того, как нода выйдет из строя.

Демонстрация эффекта

Так происходит реализация.

Для этой цели я создал новое приложение для демонстрации:

GitHub.com/crossover J я…

Это простое приложение SpringBoot, которое делает всего несколько вещей.

  • При запуске приложения открывается новый поток для регистрации службы в Zookeeper.
  • Также прослушивайте узел для обновления списка локальных служб.
  • Предоставляет интерфейс для возврата доступного сервисного узла.

Я запустил два приложения локально:127.0.0.1:8083,127.0.0.1:8084. Давайте посмотрим на рендеры.

Два приложения запущены и завершены:


Визуальная древовидная структура текущего Zookeeper:


Если вы хотите узнать всю информацию об узле обслуживания:


Если вы хотите получить доступный сервисный узел:

Вот простой опрос.


Когда узел не работает: приложение будет уведомлено об обновлении локального кеша. При этом узлы в Zookeeper будут автоматически удалены.


При повторном получении последнего узла:


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

Реализация кодирования

Это относительно просто реализовать, в основном используя API ZKClient.

Опубликуйте еще несколько важных абзацев.

регистр

Запустите зарегистрированный Zookeeper.

Вся основная логика в этой теме.

  • Сначала создайте родительский узел. Как показано в узле Zookeeper выше, его необходимо создать первым./routeКорневой узел при создании определяет, существует ли он уже.
  • Затем вам нужно решить, нужно ли вам регистрироваться в Zookeeper, потому что некоторые узлы используются только для обнаружения сервисов, и им не нужно выполнять бизнес-функции (требования моего собственного проекта).
  • Пропишите ip и порт текущего приложения, и вам нужно следить за корневым узлом/route, чтобы вы могли получать уведомления, когда другие службы отключаются.

Согласно локальному кешу

Следите за изменениями в сервисе

    public void subscribeEvent(String path) {
        zkClient.subscribeChildChanges(path, new IZkChildListener() {
            @Override
            public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
                logger.info("清除/更新本地缓存 parentPath=【{}】,currentChilds=【{}】", parentPath,currentChilds.toString());

                //更新所有缓存/先删除 再新增
                serverCache.updateCache(currentChilds) ;
            }
        });


    }

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

    /**
     * 更新所有缓存/先删除 再新增
     *
     * @param currentChilds
     */
    public void updateCache(List<String> currentChilds) {
        cache.invalidateAll();
        for (String currentChild : currentChilds) {
            String key = currentChild.split("-")[1];
            addCache(key);
        }
    }

клиентская нагрузка

При этом на стороне клиента предусмотрен алгоритм загрузки.

По сути, это реализация опроса:

    /**
     * 选取服务器
     *
     * @return
     */
    public String selectServer() {
        List<String> all = getAll();
        if (all.size() == 0) {
            throw new RuntimeException("路由列表为空");
        }
        Long position = index.incrementAndGet() % all.size();
        if (position < 0) {
            position = 0L;
        }

        return all.get(position.intValue());
    }

Конечно, больше таких, как веса, случайность,LRUалгоритм.

Zookeeper другие преимущества и проблемы

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

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

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

Поскольку Zookeeper выбирает CP (согласованность, отказоустойчивость раздела) в теории CAP, невозможно получить данные, когда половина узлов в кластере Zookeeper недоступна.

С согласованностью проблем нет, но больше рекомендуется в сценариях регистрации и обнаружения.Eureka, который был проверен в SpringCloud. Подробности в этой статье не обсуждаются.

Но, учитывая мой вариант использования, Zookeeper смог выполнить эту работу.

Суммировать

Весь полный код для этой статьи размещен на GitHub.

GitHub.com/crossover J я….

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

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

Ваши лайки и ретвиты — лучшая поддержка.