Является ли модель взаимодействия центра конфигурации Nacos принудительной или принудительной? ты должен ответить

Java
Является ли модель взаимодействия центра конфигурации Nacos принудительной или принудительной? ты должен ответить

Этот случай входит вGitHub.com/Программист-NDS…

Всем привет, я Сяофу~

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

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

Тут кину ответ первым: клиент его активно дергает!

Далее давайте посмотримNacos, давайте посмотрим, как это реализовано?

Центр конфигурации

чатNacosПрежде чем кратко рассмотреть происхождение конфигурационного центра.

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

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

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

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

配置中心

толкать и тянуть модель

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

толкать модель

Установка клиента и сервераTCPДлинное соединение, когда данные конфигурации сервера изменяются, данные немедленно передаются клиенту через установленное длинное соединение.

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

Недостатки: длительные соединения могут быть недоступны из-за проблем с сетью.假死. Состояние соединения нормальное, но связь на самом деле невозможна, поэтому должен быть механизм сердцебиения.KeepAliveЧтобы обеспечить доступность соединения, данные конфигурации могут быть успешно отправлены.

Вытяните модель

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

Преимущество опроса в том, что его проще реализовать. Но очевидны и недостатки: опрос не может гарантировать передачу данных в реальном времени. Когда запрашивать? Как часто нужно делать запрос? Это все вопросы, которые необходимо учитывать, и метод опроса также оказывает большую нагрузку на сервер.

долгий опрос

Мы дали ответ в начале,nacosКлиент активно тянетpullмодель, применяющая длительный опрос (Long Polling) для получения данных конфигурации.

Лоб? Раньше я только слышал об опросе, что, черт возьми, за долгий опрос? Чем он отличается от опроса в традиционном смысле (для сравнения назовем его коротким опросом)?

короткий опрос

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

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

Чтобы решить проблему короткого опроса, существует схема длинного опроса.

долгий опрос

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

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

Первое знакомство с Накосом

Для удобства последующих демонстрационных операций я настроил локальныйNacos.Уведомление:Небольшая яма встречается во время выполнения, потому чтоNacosПо умолчаниюclusterЗапускается кластерный режим, а локальное построение обычно является автономным режимом.standalone, здесь нужно вручную изменить скрипт запускаstartup.Xрежим загрузки в .

прямое исполнение/bin/startup.XВот и все, пароль пользователя по умолчаниюnacos.

несколько концепций

NacosНесколько основных концепций центра конфигурации:dataId,group,namespace, их иерархическая взаимосвязь следующая:

dataId: это самая основная единица в центре конфигурации, это своего родаkey-valueструктура,keyОбычно имя нашего конфигурационного файла, например:application.yml,mybatis.xml,а такжеvalueявляется содержимым всего файла.

В настоящее время поддерживаетсяJSON,XML,YAMLи другие форматы конфигурации.

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

namespace: В процессе разработки проекта обязательно будутdev,test,proмного разных сред,namespaceЭто сделано для того, чтобы изолировать разные среды. По умолчанию все конфигурации находятся вpublicвнутри.

Архитектурный дизайн

Рисунок ниже кратко описываетnacosПоток инфраструктуры центра конфигурации.

Клиент и консоль регистрируют данные конфигурации на сервере, отправляя запросы Http, а сервер сохраняет данные в Mysql.

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

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

Ниже мы анализируем исходный код версии Nacos 2.0.1.В версиях после 2.0 есть много изменений, которые немного отличаются от многих сведений в Интернете.

адрес:GitHub.com/alibaba/вывезти…

Анализ исходного кода клиента

NacosИсходный код клиента центра конфигурации находится вnacos-clientпроект, которыйNacosConfigServiceКласс реализации является основной точкой входа для всех операций.

Прежде чем говорить об этом, давайте разберемся со структурой данных клиента.cacheMap, здесь мы сосредоточимся на том, чтобы запомнить его, потому что он проходит через почти все операции клиента Nacos из-за наличия многопоточных сценариев для обеспечения согласованности данных,cacheMapУсыновленныйAtomicReferenceРеализация атомарной переменной.

/**
 * groupKey -> cacheData.
 */
private final AtomicReference<Map<String, CacheData>> cacheMap = new AtomicReference<Map<String, CacheData>>(new HashMap<>());

cacheMapпредставляет собой структуру Map, ключgroupKey, это строка, объединенная по dataId, group, tenant (арендатору); значениеCacheDataобъект, каждый dataId будет содержать объект CacheData.

получить конфигурацию

NacosЛогика получения конфигурационных данных относительно проста.Сначала берем конфигурацию в локальном файле моментального снимка.Если локальный файл не существует или содержимое пустое, то вытаскиваем соответствующие конфигурационные данные dataId с удаленного конца через HTTP-запрос и сохраните его в локальный снимок. По умолчанию запрос повторяет попытку 3 раза с тайм-аутом 3 с.

Получить конфигурацию сgetConfig()а такжеgetConfigAndSignListener()Эти два интерфейса, ноgetConfig()просто отправьте обычный HTTP-запрос, аgetConfigAndSignListener()Затем есть еще операции для инициирования длительного опроса и отслеживания регистрации изменений данных dataId.addTenantListenersWithContent().

@Override
public String getConfig(String dataId, String group, long timeoutMs) throws NacosException {
    return getConfigInner(namespace, dataId, group, timeoutMs);
}

@Override
public String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener)
        throws NacosException {
    String content = getConfig(dataId, group, timeoutMs);
    worker.addTenantListenersWithContent(dataId, group, content, Arrays.asList(listener));
    return content;
}

зарегистрировать слушателя

Клиент регистрируется для мониторинга, начните сcacheMapЗалезайdataIdсоответствующийCacheDataобъект.

public void addTenantListenersWithContent(String dataId, String group, String content,
                                          List<? extends Listener> listeners) throws NacosException {
    group = blank2defaultGroup(group);
    String tenant = agent.getTenant();
    // 1、获取dataId对应的CacheData,如没有则向服务端发起长轮询请求获取配置
    CacheData cache = addCacheDataIfAbsent(dataId, group, tenant);
    synchronized (cache) {
        // 2、注册对dataId的数据变更监听
        cache.setContent(content);
        for (Listener listener : listeners) {
            cache.addListener(listener);
        }
        cache.setSyncWithServer(false);
        agent.notifyListenConfig();
    }
}

Если нет, инициируйте длинный запрос на сервер, чтобы получить конфигурацию, по умолчаниюTimeoutВремя составляет 30 секунд, и возвращенные данные конфигурации записываются обратно вCacheDataПоле содержимого объекта и используйте содержимое для создания значения MD5; затем передайтеaddListener()Зарегистрируйте слушателя.

CacheDataЭто также класс с очень высокой частотой появления.Мы видим, что в дополнение к связанным базовым атрибутам, таким как dataId, group, tenant и content, есть еще несколько важных атрибутов, таких как:listeners,md5(значение md5, рассчитанное на основе реальных данных конфигурации контента), а также мониторинг регистрации, сравнение данных и операции уведомления об изменении данных на стороне сервера — все это здесь.

вlistenersэто набор всех слушателей, зарегистрированных для dataId, из которыхManagerListenerWrapобъект в дополнение к владениюListenerкласс слушателя иlastCallMd5Поле, этот атрибут очень критичен, это важное условие для суждения о том, изменились ли данные сервера.

При добавлении монитораCacheDataТекущее последнее значение md5 объекта присваиваетсяManagerListenerWrapобъектlastCallMd5Атрибуты.

public void addListener(Listener listener) {
    ManagerListenerWrap wrap =
        (listener instanceof AbstractConfigChangeListener) ? new ManagerListenerWrap(listener, md5, content)
            : new ManagerListenerWrap(listener, md5);
}

Видя, что эта пара настроек мониторинга dataId выполнена? Мы обнаружили, что все операции вращаются вокругcacheMapв структуреCacheDataобъекта, то смело догадывайтесь, что будет специальная задача по работе с этой структурой данных.

Уведомление об изменении

Как клиент воспринимает изменение данных сервера?

Начнем с самого начала,NacosConfigServiceКонструктор класса инициализируетClientWorker, пока вClientWorkerПул потоков запускается в конструкторе класса для опросаcacheMap.

пока вexecuteConfigListen()В методе есть такая логика, проверьтеcacheMapв идентификаторе данныхCacheDataВ объекте поле MD5 и зарегистрированный слушательlistenerвнутриlastCallMd5值, если они разные, он сработает при изменении данных конфигурации.safeNotifyListenerспособ отправки уведомления об изменении данных.

void checkListenerMd5() {
    for (ManagerListenerWrap wrap : listeners) {
        if (!md5.equals(wrap.lastCallMd5)) {
            safeNotifyListener(dataId, group, content, type, md5, encryptedDataKey, wrap);
        }
    }
}

safeNotifyListener()Метод запускает отдельный поток и отправляет всем парамdataIdКлиент, который зарегистрировался для мониторинга, отправляет измененное содержимое данных.

Клиент получает уведомление и реализует его напрямуюreceiveConfigInfo()Метод получает данные обратного вызова и обрабатывает свой собственный бизнес.

configService.addListener(dataId, group, new Listener() {
    @Override
    public void receiveConfigInfo(String configInfo) {
        System.out.println("receive:" + configInfo);
    }

    @Override
    public Executor getExecutor() {
        return null;
    }
});

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

public static void main(String[] args) throws NacosException, InterruptedException {
    String serverAddr = "localhost";
    String dataId = "test";
    String group = "DEFAULT_GROUP";
    Properties properties = new Properties();
    properties.put("serverAddr", serverAddr);
    ConfigService configService = NacosFactory.createConfigService(properties);
    String content = configService.getConfig(dataId, group, 5000);
    System.out.println(content);
    configService.addListener(dataId, group, new Listener() {
        @Override
        public void receiveConfigInfo(String configInfo) {
            System.out.println("数据变更 receive:" + configInfo);
        }
        @Override
        public Executor getExecutor() {
            return null;
        }
    });

    boolean isPublishOk = configService.publishConfig(dataId, group, "我是新配置内容~");
    System.out.println(isPublishOk);

    Thread.sleep(3000);
    content = configService.getConfig(dataId, group, 5000);
    System.out.println(content);
}

Результат ожидаемый, когда серверpublishConfigПосле изменения данных клиент может сразу это почувствовать, но использовать активное извлечениеpullРежим делает эффект push в реальном времени на стороне сервера.

数据变更 receive:我是新配置内容~
true
我是新配置内容~

Анализ исходного кода на стороне сервера

NacosИсходный код сервера центра конфигурации в основном находится вnacos-configПроэктConfigControllerclass логика серверной части немного сложнее, чем клиентской, здесь мы сосредоточимся на ней.

Обработка длинных опросов

Адрес прослушивающего интерфейса, предоставленный сервером/v1/cs/configs/listener, этот метод не очень содержателен, следуйтеdoPollingConfigПосмотрите вниз.

сервер по запросуheaderсерединаLong-Pulling-Timeoutатрибут, чтобы различать, является ли запрос длинным опросом или коротким опросом, здесь мы сосредоточимся только на длинной части опроса, а затем посмотримLongPollingService(Помните, что эта служба имеет решающее значение) в классеaddLongPollingClient()Как метод обрабатывает запросы с длительным опросом от клиентов.

Время ожидания запроса по умолчанию для обычных клиентов равно30s, но здесь мы обнаруживаем, что сервер "тайно" уменьшил500msТеперь есть только тайм-аут.29.5s, то зачем это делать?

Используя официальное объяснение, причина, по которой на запрос следует отвечать за 500 мс заранее, заключается в том, чтобы гарантировать, что клиент не истечет время ожидания из-за сетевой задержки в наибольшей степени, учитывая, что запрос может занять некоторое время во время балансировки нагрузки, в конце концовNacosПервоначально он был разработан в соответствии с собственным бизнес-объемом Али!

В настоящее время передано клиентуgroupkeyMD5 сравнивается с текущим MD5 сервера, напримерmd5Если значение отличается, это означает, что элемент конфигурации сервера был изменен.groupkeyположить вchangedGroupKeysсбор и возврат клиенту.

MD5Util.compareMd5(req, rsp, clientMd5Map)

Если изменений нет, клиентский запрос приостанавливается, и этот процесс сначала создаетClientLongPollingпланирование задачRunnable, и отправить наschedulerВременная задержка пула потоков29.5sвоплощать в жизнь.

ConfigExecutor.executeLongPolling(
                new ClientLongPolling(asyncContext, clientMd5Map, ip, probeRequestSize, timeout, appName, tag));

Здесь каждая длинная задача опроса несетasyncContextобъект, чтобы на каждый запрос можно было отложить ответ, а когда задержка наступит или конфигурация изменится, вызовитеasyncContext.complete()Ответ завершен.

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

ClientLongPollingКогда задача отправляется в пул отложенных потоков для выполнения, сервер передаетallSubsОчередь содержит все ожидающие клиентские задачи запроса на длительный опрос, что представляет собой процесс регистрации и прослушивания клиента.

Если данные клиента не изменились в течение периода задержки, по истечении времени задержки эта длительная задача опроса будет изменена сallSubsОтбор очередей и ответ на запросыresponse,Это取消监听. После получения ответа клиент снова инициирует длительный опрос, и цикл повторяется.

处理长轮询

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

Как можно немедленно отменить соответствующую приостановленную задачу и вовремя уведомить клиента об изменении данных?

изменение данных

Платформа управления или клиент для изменения расположения элементов конфигурацииConfigControllerсерединаpublishConfigметод.

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

ConfigChangePublisher.notifyConfigChange(new ConfigDataChangeEvent(false, dataId, group, tenant, time.getTime()));

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

订阅数据变更事件

DataChangeTaskОсновная логика внутри заключается в обходеallSubsОчередь, как мы знаем выше, эта очередь поддерживает длинные задачи запроса на опрос всех клиентов, из этих задач найдите те, которые содержат текущие изменения.groupkeyизClientLongPollingзадача передать изменения данных клиенту, а отallSubsУдалите эту длинную задачу опроса из очереди.

DataChangeTask

И мы смотрим на ответ клиентаresponseпри звонкеasyncContext.complete()Асинхронный запрос завершен.

заключительные замечания

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

nacosЯ лично думаю, что исходный код все еще относительнопростоДа, код не слишком блещет, выглядит относительно легко. Не сопротивляйтесь просмотру исходного кода, это просто бизнес-код, написанный другими.just so so!


Я Сяофу~, если это работает для вассмотреть в,обрати внимание наС вашей поддержкой, увидимся в следующий раз~

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

адрес электронной книги

Личный общедоступный номер:Внутри программатора, добро пожаловать на обмен