Анализ | Говоря о модульном принципе SOFABoot

JVM Spring Открытый исходный код Эксплуатация и обслуживание

SOFA (масштабируемая открытая финансовая архитектура)

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


SOFABoot — это среда разработки на основе Spring Boot, исходный код которой открыт группой Ant Financial Middleware. Начиная с версии 2.4.0 SOFABoot поддерживает возможности модульной разработки на основе изоляции контекста Spring. Модуль SOFABoot включает не только код Java, но и конфигурацию Spring. files. , каждый модуль SOFABoot представляет собой отдельный контекст Spring.


Адрес SOFABoot на Github:

https://github.com/alipay/sofa-boot

Ловушки традиционной модульности

Прежде чем представить модульность SOFABoot, давайте еще раз рассмотрим недостатки традиционной модульности.Модульность бизнес-системы Ant Financial ---- Модульное решение для изоляции.


В простой системе Spring/SpringBoot мы часто видим, что модули системы располагаются следующим образом: как показано в левой части рисунка ниже, система просто делится на веб-уровень и сервисный уровень. слой ДАЛ.



Когда эта система предоставляет больше услуг, система может развиваться так, как показано справа на рисунке выше. В правой части приведенного выше рисунка система несет две службы, одна из которых — Кассир (кассир), а другая — Плата (оплата).Эти две службы могут иметь некоторые зависимости, и Кассиру необходимо вызвать Pay, чтобы предоставить возможность совершать платежи. .

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



Cashier Service ошибочно назвал внутренний bean-компонент в Pay Service, что привело к тесной связи между двумя модулями.


Проблема с этой традиционной модульностью заключается в том, что модульность не является полной. Хотя во время разработки путем разделения модулей и помещения классов с определенными обязанностями в конкретные модули достигается связность «физического расположения» классов. Однако во время выполнения, поскольку нет средств изоляции, разработчик модуля не может четко знать, что такое внешний интерфейс, предоставляемый другим модулем, какие bean-компоненты я могу внедрить напрямую, а какие bean-компоненты — это ваш внутренний Bean-компонент. , я не могу его использовать. В долгосрочной перспективе связь между модулями будет становиться все более и более серьезной, и первоначальное разделение модулей бесполезно. Когда система становится все больше и больше, и, наконец, нужно выполнить разделение сервисов, ей нужно потратить много энергии, чтобы разобраться в отношениях между модулями.

Введение в модульность SOFABoot

Чтобы решить проблему неполной модульности в традиционной схеме модульности, SOFABoot поддерживает возможность модульности на основе изоляции контекста Spring, начиная с версии 2.4.0.Каждый модуль SOFABoot использует независимый контекст Spring, каждый модуль автономен, а модули Общаются через службу JVM, чтобы избежать тесной связи между модулями:

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

  • Модуль SOFABoot: модуль SOFABoot представляет собой общий пакет Jar, включающий код Java, файл конфигурации Spring, идентификатор модуля SOFABoot и другую информацию. Каждый модуль SOFABoot представляет собой независимый контекст Spring.

  • Служба JVM: после изоляции контекста bean-компоненты между модулями не могут быть внедрены напрямую.Служба JVM используется для реализации межмодульной связи, а также для публикации и ссылки на службы модуля.

  • Контекст корневого приложения: Контекст Spring, сгенерированный приложением SOFABoot, вызывающим метод SpringApplication.run(args), является родителем всех модулей SOFABoot.


В следующей части этой статьи будут представлены поиск и обновление модуля SOFABoot, служба JVM и управление компонентами, а также основные концепции корневого контекста приложения, а затем кратко представлены Require-Module, Spring-Parent и параллельный запуск модуля SOFABoot, а также наконец Дает практические советы по модульной разработке.

Найдите и обновите модуль SOFABoot.

Схема последовательности поиска и обновления модуля SOFABoot выглядит следующим образом:



В SOFABoot мы определяем SofaModuleContextRefreshedListener, который прослушивает событие ContextRefreshedEvent, отправленное корневым контекстом приложения, и выбирает реакцию на это событие по следующим двум соображениям:

  1. ContextRefreshedEvent — это стандартное событие Spring, которое носит более общий характер.

  2. Для обновления модуля SOFABoot необходимо дождаться обновления контекста корневого приложения, поскольку модуль SOFABoot может зависеть от определений bean-компонентов в контексте корневого приложения.


После того, как SofaModuleContextRefreshedListener прослушает событие ContextRefreshedEvent, он создаст объект PipelineContext, и в PipelineContext будут добавлены три этапа: ModelCreatingStage, SpringContextInstallStage и ModuleLogOutputStage. Функции трех этапов следующие:

  • ModelCreatingStage: найти все действительные модули SOFABoot в текущем ClassPath;

  • SpringContextInstallStage: SOFABoot для каждого модуля, чтобы найти новый контекст Spring, модуль загрузки файла конфигурации SOFABoot Spring;

  • MODULOGOUTPUTSTAGE: выводит все модули Sofaboot, которые были обновлены и обновлены, чтобы пользователи могли быстро найти проблемы.


Вызов метода процесса PipelineContext приведет к последовательному выполнению этих трех этапов и обновлению всех модулей SOFABoot, содержащихся в текущем ClassPath.

Служба JVM и управление компонентами

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

Чтобы обеспечить межмодульное обнаружение сервисов, мы определяем интерфейс управления компонентами ComponentManager внутри SOFABoot:

public interface ComponentManager {
    /**
     * register component in this manager
     *
     * @param componentInfo component that should be registered
     */
    void register(ComponentInfo componentInfo);

    /**
     * get concrete component by component name
     *
     * @param name component name
     * @return concrete component
     */
    ComponentInfo getComponentInfo(ComponentName name);
}

ComponentInfo — это интерфейс, используемый для представления информации о компонентах.В настоящее время он содержит две конкретные реализации, ServiceComponent и ReferenceComponent, которые представляют службы и ссылки соответственно. ComponentName — это уникальный идентификатор ComponentInfo, используемый для различения различных реализаций ComponentInfo.

Класс реализации ComponentManager содержит Map с ComponentName в качестве ключа и ComponentInfo в качестве значения для хранения всех зарегистрированных ComponentInfo:

public class ComponentManagerImpl implements ComponentManager {
    /** container for all components */
    protected ConcurrentMap<ComponentName, ComponentInfo> registry;
    
    // other definition

}

При публикации службы JVM мы будем вызывать метод register для регистрации службы JVM в ComponentManager.Когда происходит вызов службы, мы будем вызывать метод getComponentInfo для поиска и вызова служб, опубликованных другими модулями в ComponentManager.

Root Application Context

SOFABoot является расширением Spring Boot. Приложение SOFABoot запускается с помощью метода SpringApplication.run(args).После вызова этого метода приложение сгенерирует контекст Spring, который мы называем корневым контекстом приложения. Контекст корневого приложения — это особый элемент модульной структуры SOFABoot. Он является родительским для каждого контекста модуля SOFABoot. bean-компоненты определены, и эти bean-компоненты будут действовать только в корневом контексте приложения по умолчанию. Готов к использованию.


В дополнение к определению некоторых bean-компонентов Starter может также определить некоторые BeanPostProcessor и BeanFactoryPostProcessor.Для этих двух типов специальных определений bean-компонентов подконтексту недостаточно найти определения bean-компонентов.Определения этих двух типов bean-компонентов должны быть скопированы в текущий контекст, чтобы он вступил в силу, поэтому при обновлении контекста, соответствующего модулю SOFABoot, мы скопируем все BeanPostProcessors и BeanFactoryPostProcessors, определенные в корневом контексте приложения, в контекст модуля SOFABoot, чтобы некоторые процессоры, определенные Starter может быть непосредственно использован в контексте модуля SOFABoot, например, runtime-ServiceAnnotationBeanPostProcessor будет определен в Sofa-boot-starter. Этот класс в основном используется для реализации службы публикации аннотаций. После автоматического копирования, пока runtime-sofa-boot-starter добавлена ​​​​зависимость, она поддерживает службу публикации на основе аннотаций в модуле SOFABoot.

Require-Module, Spring-Parent и параллельные модули запуска

При обновлении модуля SOFABoot в модуле A может быть опубликована служба JVM, которую необходимо вызвать в методе init компонента в модуле B. Если предположить, что модуль B запускается до модуля A, то компонент модуля B будет запущен. be Инициализация завершится ошибкой, потому что служба JVM модуля A не выпущена, что приведет к сбою запуска контекста Spring. На этом этапе мы можем указать Require-Module в диван-модуле.properties, чтобы заставить модуль A запускаться раньше модуля B.


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


SOFABoot рассчитает дерево зависимостей модулей в соответствии с Require-Module и Spring-Parent. Например, следующее дерево зависимостей указывает, что модуль B и модуль C зависят от модуля A, модуль E зависит от модуля D, а модуль F зависит от модуля E. :

Дерево зависимостей гарантирует, что модуль A должен запускаться перед модулем B и модулем C, модуль D начинается перед модулем E, а модуль E начинается перед модулем F, но дерево зависимостей не определяет модуль B и модуль C, модули B, C и модули Последовательность запуска между D, E и F, эти модули могут запускаться последовательно или параллельно. SOFABoot по умолчанию запускает модули параллельно, что может значительно увеличить скорость запуска приложения.

Практические советы

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


Некоторые реализации не подходят для определения в модуле SOFABoot, например определение контроллера. Определение контроллера является реализацией уровня представления, а модуль SOFABoot относится к бизнес-уровню. Мы не рекомендуем и не поддерживаем определение контроллера компоненты в модуле SOFABoot. Определение компонента контроллера рекомендуется размещать в корневом контексте приложения. Если компоненту контроллера необходимо вызвать службу, опубликованную модулем SOFABoot, он может напрямую использовать аннотацию для ссылки на службу. Конкретные примеры см.Практика | Модульная разработка на базе SOFABootпример в .


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

Суммировать

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


В статье упоминается:



Нажмите и удерживайте кнопку «Подписаться», чтобы получить распределенную архитектуру сухих товаров.

Добро пожаловать на сборку SOFAStack вместе https://github.com/alipay