Настроил горячее обновление, перезагружаться не хочет, как обновить статус бинов?

Spring Boot задняя часть

Задающие вопросы ❓

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

Например: если конфигурация параметров JDBC изменяется во время выполнения, перезапускается ли приложение в это время? Или изменить свойства соответствующего компонента DataSource? Если нужно изменить свойства bean-компонента, полезно ли изменять его напрямую? как нам это сделать?

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

Голос за кадром: Идеи важнее результатов.

Анализ сложности 👨🏻‍💻

Сложность 1: Динамическое изменение параметров JDBC, предполагая, что URL-адрес и пароль изменены, старое соединение по-прежнему использует старую конфигурацию, что мне делать в это время? Старое соединение прерывается сразу или через некоторое время? Что делать с потоками, использующими старые соединения?

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

Трудность 2: Где находится ссылка на DataSource Bean? Можно ли заменить начисто? Как отказаться от старой связи и закрыть ее?

После изменения параметров JDBC следующим шагом является поиск потребителя компонента DataSource и замена компонента DataSource, используемого потребителем, новой конфигурацией. Затем закройте старое соединение и позвольте потребителю использовать новое соединение.

попробуй решить 🤔

В тексте будет использоваться пул соединений HikariCP в качестве примера, чтобы попытаться решить эту проблему. HikariCP является пулом соединений с базой данных по умолчанию после SpringBoot 2.0, и он претендует на звание самого быстрого пула соединений с базой данных в текущей области Java.

Вариант 1: HikariCP поставляется с собственным API конфигурации динамической модификации.

HikariCP поставляется с некоторыми API для поддержки динамического изменения конфигурации базы данных.

Цитата 1:GitHub.com/Бретт Шерсть Доктор…

FQA

Цитата 2:GitHub.com/Бретт Шерсть Доктор…

使用jxm

...
HikariDataSource dataSource = ....
HikariConfigMXBean hikariConfigMXBean = dataSource.getHikariConfigMXBean();
hikariConfigMXBean.setPassword("...");
...

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

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

Вариант 2. Динамическое изменение источника данных

Без лишних слов, давайте перейдем непосредственно к коду:

DynamicDataSource

Идея программы отorg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource. Этот класс используется для решения проблемы нескольких источников данных, вы можете найти разные источники данных по разным ключам, а затем получить соответствующее соединение.

Так же,DynamicDataSourceДостигнутоDataSourceинтерфейс с переменной-членом внутри негоAtomicReference<DataSource> dataSourceReference,Зависит отdataSourceReferenceпредоставлятьDataSource. Когда программа изменяет параметры JDBC во время выполнения, ее можно создать, создав новуюDataSourceобъект для заменыdataSourceReferenceзначение, в то время как выставленноеDynamicDataSourceобъект, который не зависит от пользователя.

Так этого достаточно? Подумайте минуту.

Помните трудности, упомянутые ранее?Используя этот метод, нижний слой может спокойно заменить экземпляр объекта DataSource, как закрыть соединение замененного старого DataSource?

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

ShutdownDataSource

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

Связанный кодовый адрес:GitHub.com/ Стоит может…

Динамическая модификация произвольных свойств бина 🚀

Идеальный вариант 2

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

DynamicRefreshProxy

На самом деле все далеко не так просто, как кажется, даже если абстрагироватьсяDynamicRefreshProxy, остаются следующие трудности:

  1. Компоненты, которым необходимо динамически изменять конфигурацию, должны проходитьDynamicRefreshProxyдля создания прокси-объектов
  2. После динамического модификации конфигурации вам нужно изменитьAtomicReference<Object> atomicReferenceОпорное значение
  3. Вам необходимо предоставить метод для закрытия ресурсов, связанных со старым объектом, и вызвать этот метод после замены старого объекта.

Здесь я могу предложить идею для решения этих проблем:

с помощью пользовательских аннотаций, таких как@DynamicRefreshable, а затем предоставить BeanPostProcessor для создания прокси-объекта для замены исходного объекта и сохранения соответствующегоDynamicRefreshProxyОбъект, прослушав изменение соответствующего свойства, заменитьDynamicRefreshProxyОбъектыatomicReference, а затем вызовите соответствующий метод исходного объекта, чтобы закрыть ресурс.

Spring Cloud Refresh Scope

В Spring Cloud доступен новый Scope: Refresh Scope

Обновить документы, связанные с Scope:

cloud.spring.IO/spring - уродливый...

Refresh Scope

Компоненты, отмеченные @RefreshScope, повторно инициализируются при изменении конфигурации, но для этого требуется вызовContextRefresher#refreshилиRefreshScope#refreshAll. разница в том,ContextRefresher#refreshВнутри метода не только вызовыRefreshScope#refreshAll, также звонитContextRefresher#refreshEnvironment.

Простая демонстрационная программа выглядит следующим образом:

demo

Можно ли возобновить Дафа? ✌️

Помните вопросы и трудности, возникшие в начале?

Трудность 2: Где находится ссылка на DataSource Bean? Можно ли заменить начисто? Как отказаться от старой связи и закрыть ее?

Трудно проверить, был ли ресурс закрыт нормально, это будет связано с конкретным состоянием выполнения текущего проекта.В приведенном выше примере, несмотря на то, что после замены экземпляра DataSource соответствующий API вызывается для закрытия соединения, но,doShutdownDataSourceМетод будет опробован всего несколько раз.После определенного количества раз метод close будет вызываться непосредственно для закрытия базы данных. Если вы пытаетесьMAX_RETRY_TIMESПосле этого соединение все равно не закрывается? Гарантированно ли метод close закрывает все связанные ресурсы? Или перезапустить Дафа? !

Небольшой опрос: каков ваш подход к горячим обновлениям Bean-компонентов? Или выбрать перезагрузку?


Приглашаем обратить внимание на личный публичный номер:

Coder小黑