Задающие вопросы ❓
Через центр конфигурации приложение может получать изменения конфигурации в режиме реального времени, однакоНекоторые бины в приложении управляются контейнером 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/Бретт Шерсть Доктор…
Цитата 2:GitHub.com/Бретт Шерсть Доктор…
...
HikariDataSource dataSource = ....
HikariConfigMXBean hikariConfigMXBean = dataSource.getHikariConfigMXBean();
hikariConfigMXBean.setPassword("...");
...
Автор не пробовал это решение тщательно (почему? Конечно, ниже есть лучшее решение~), но его документ Github описан таким образом, официальная информация из первых рук, вероятность ошибки относительно мала, если есть проблема Вы можете перейти на Github, чтобы сообщить о проблемах, связанных с файлом.
Преимущества этого решения: Простое и надежное использование собственного API для динамического изменения конфигурации. Недостатки: количество параметров, которые можно изменить, ограничено, а реализация DataSource сильно ограничена.Предполагая, что в будущем будет использоваться другой пул соединений с базой данных, эти собственные API-интерфейсы могут не предоставляться для изменения параметров.
Вариант 2. Динамическое изменение источника данных
Без лишних слов, давайте перейдем непосредственно к коду:
Идея программы отorg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
. Этот класс используется для решения проблемы нескольких источников данных, вы можете найти разные источники данных по разным ключам, а затем получить соответствующее соединение.
Так же,DynamicDataSource
ДостигнутоDataSource
интерфейс с переменной-членом внутри негоAtomicReference<DataSource> dataSourceReference
,Зависит отdataSourceReference
предоставлятьDataSource
. Когда программа изменяет параметры JDBC во время выполнения, ее можно создать, создав новуюDataSource
объект для заменыdataSourceReference
значение, в то время как выставленноеDynamicDataSource
объект, который не зависит от пользователя.
Так этого достаточно? Подумайте минуту.
Помните трудности, упомянутые ранее?Используя этот метод, нижний слой может спокойно заменить экземпляр объекта DataSource, как закрыть соединение замененного старого DataSource?
HikariCP предоставляет соответствующие методы для закрытия соединения. Если вы используете другой пул соединений с базой данных, вы сможете найти аналогичный метод.
Резюме: этот метод проходитDynamicDataSource
упаковать настоящийDataSource
Поставщик, который позволяет динамически заменять базовый объект экземпляра DataSource во время выполнения. При этом после замены не забудьте закрыть старый DataSource. По сравнению со схемой 1, эта схема может поддерживать изменение любых свойств JDBC без сильных зависимостей.DataSource
API реализатора является более общим и гибким.
Связанный кодовый адрес:GitHub.com/ Стоит может…
Динамическая модификация произвольных свойств бина 🚀
Идеальный вариант 2
После решения случая с DataSource можно ли дополнительно абстрагировать приведенную выше схему для поддержки динамической модификации любых свойств Bean?
На самом деле все далеко не так просто, как кажется, даже если абстрагироватьсяDynamicRefreshProxy
, остаются следующие трудности:
- Компоненты, которым необходимо динамически изменять конфигурацию, должны проходить
DynamicRefreshProxy
для создания прокси-объектов - После динамического модификации конфигурации вам нужно изменить
AtomicReference<Object> atomicReference
Опорное значение - Вам необходимо предоставить метод для закрытия ресурсов, связанных со старым объектом, и вызвать этот метод после замены старого объекта.
Здесь я могу предложить идею для решения этих проблем:
с помощью пользовательских аннотаций, таких как@DynamicRefreshable
, а затем предоставить BeanPostProcessor для создания прокси-объекта для замены исходного объекта и сохранения соответствующегоDynamicRefreshProxy
Объект, прослушав изменение соответствующего свойства, заменитьDynamicRefreshProxy
ОбъектыatomicReference
, а затем вызовите соответствующий метод исходного объекта, чтобы закрыть ресурс.
Spring Cloud Refresh Scope
В Spring Cloud доступен новый Scope: Refresh Scope
Обновить документы, связанные с Scope:
Компоненты, отмеченные @RefreshScope, повторно инициализируются при изменении конфигурации, но для этого требуется вызовContextRefresher#refresh
илиRefreshScope#refreshAll
. разница в том,ContextRefresher#refresh
Внутри метода не только вызовыRefreshScope#refreshAll
, также звонитContextRefresher#refreshEnvironment
.
Простая демонстрационная программа выглядит следующим образом:
Можно ли возобновить Дафа? ✌️
Помните вопросы и трудности, возникшие в начале?
Трудность 2: Где находится ссылка на DataSource Bean? Можно ли заменить начисто? Как отказаться от старой связи и закрыть ее?
Трудно проверить, был ли ресурс закрыт нормально, это будет связано с конкретным состоянием выполнения текущего проекта.В приведенном выше примере, несмотря на то, что после замены экземпляра DataSource соответствующий API вызывается для закрытия соединения, но,doShutdownDataSource
Метод будет опробован всего несколько раз.После определенного количества раз метод close будет вызываться непосредственно для закрытия базы данных. Если вы пытаетесьMAX_RETRY_TIMES
После этого соединение все равно не закрывается? Гарантированно ли метод close закрывает все связанные ресурсы? Или перезапустить Дафа? !
Небольшой опрос: каков ваш подход к горячим обновлениям Bean-компонентов? Или выбрать перезагрузку?
Приглашаем обратить внимание на личный публичный номер: