В прошлом мы слышали одну особенность, касающуюся запуска механизма Activiti мультитенантным способом, когда данные арендатора изолированы от других. Конечно, в определенных облачных / SaaS-средах это обязательно.
Пару месяцев назад ко мне подошел Рафаэль Гилен, который учится в Боннском университете и работает над магистерской диссертацией о мультитенантности в Activiti. Мы собрались в совместном кафе-баре пару недель назад, поделились идеями и собрали первый прототип с изоляцией схемы базы данных для арендаторов. Очень весело :-).
В любом случае, мы уточняли и полировали этот код и добавили его в кодовую базу Activiti. Давайте рассмотрим существующие способы многопользовательской аренды с Activiti в первых двух разделах ниже. В третьем разделе мы углубимся в новую мультитенантную мультисхемную функцию, дополненную несколькими реально работающими примерами кода!
Общая база данных с несколькими арендаторами
Activiti уже давно может работать с несколькими арендаторами (начиная с версии 5.15). Был использован подход с общей базой данных : есть один (или более) движок Activiti, и все они обращаются к одной и той же базе данных. Каждая запись в таблице базы данных имеет идентификатор арендатора , который лучше всего понимать как своего рода тег для этих данных. Затем модуль Activiti и API считывают и используют этот идентификатор арендатора для выполнения своих различных операций в контексте арендатора.
Например, как показано на рисунке ниже, два разных клиента могут иметь определение процесса с одним и тем же ключом. Движок и API-интерфейсы гарантируют, что нет смешения данных.
Преимущество этого подхода заключается в простоте развертывания, поскольку нет никакой разницы с настройкой «обычного» движка Activiti. Недостатком является то, что вы должны помнить, чтобы использовать правильные вызовы API (то есть те, которые принимают во внимание идентификатор арендатора). Кроме того, она имеет ту же проблему, что и любая система с общими ресурсами: всегда будет конкуренция за ресурсы между арендаторами. В большинстве случаев это нормально, но есть варианты, которые не могут быть выполнены таким образом, например, предоставление определенным арендаторам более или менее системных ресурсов.
Multi-Engine Multi-Tenancy
Другой подход, который стал возможен с самой первой версии Activiti, — это просто иметь один экземпляр движка для каждого арендатора:
В этой настройке каждый клиент может иметь разные конфигурации ресурсов или даже работать на разных физических серверах. Каждый движок на этом рисунке может, конечно, состоять из нескольких движков для большей производительности / отработки отказа / и т.д. Теперь выгода заключается в том, что ресурсы предназначены для арендатора. Недостатком является более сложная настройка (несколько схем баз данных, разные файлы конфигурации для каждого арендатора и т. Д.). Каждый экземпляр движка будет занимать память (но это очень мало для Activiti ). Кроме того, вам нужно написать какой-нибудь компонент маршрутизации, который каким-то образом знает текущий контекст клиента и направляет к нужному движку.
Мульти-Схема Мульти-Аренда
Последнее дополнение к истории мультитенантности Activiti было добавлено две недели назад (вот коммит ), одновременно в версиях 5 и 6. Здесь есть база данных (схема) для каждого арендатора, но только один экземпляр механизма. Опять же, на практике может быть несколько примеров производительности / отработки отказа / и т. Д., Но концепция та же:
Выгода очевидна: есть только один экземпляр механизма для управления и настройки, и API точно такие же, как и для не-мультитенантного механизма. Но в первую очередь данные арендатора полностью отделены от данных других арендаторов. Недостатком (похожим на многоядерный многопользовательский подход) является то, что кому-то нужно управлять и настраивать разные базы данных. Но сложное управление двигателем ушло.
Коммит, на который я ссылался выше, также содержит модульный тест, показывающий, как работает многопользовательский движок Multi-Schema .
Построить механизм процесса легко, так как есть MultiSchemaMultiTenantProcessEngineConfiguration, которая абстрагирует большинство деталей:
01
02
03
04
05
06
07
08
09
10
|
config = new MultiSchemaMultiTenantProcessEngineConfiguration(tenantInfoHolder); config.setDatabaseType(MultiSchemaMultiTenantProcessEngineConfiguration.DATABASE_TYPE_H2); config.setDatabaseSchemaUpdate(MultiSchemaMultiTenantProcessEngineConfiguration.DB_SCHEMA_UPDATE_DROP_CREATE); config.registerTenant( "alfresco" , createDataSource( "jdbc:h2:mem:activiti-mt-alfresco;DB_CLOSE_DELAY=1000" , "sa" , "" )); config.registerTenant( "acme" , createDataSource( "jdbc:h2:mem:activiti-mt-acme;DB_CLOSE_DELAY=1000" , "sa" , "" )); config.registerTenant( "starkindustries" , createDataSource( "jdbc:h2:mem:activiti-mt-stark;DB_CLOSE_DELAY=1000" , "sa" , "" )); processEngine = config.buildProcessEngine(); |
Это похоже на загрузку обычного экземпляра обработчика процессов Activiti. Основное отличие состоит в том, что мы регистрируем арендаторов с двигателем. Каждый арендатор должен быть добавлен со своим уникальным идентификатором арендатора и реализацией источника данных. Конечно, реализация источника данных должна иметь свой собственный пул соединений. Это означает, что вы можете эффективно предоставлять определенным арендаторам различную конфигурацию пула соединений в зависимости от их варианта использования. Движок Activiti гарантирует, что каждая схема базы данных была либо создана, либо проверена на правильность.
Волшебство, чтобы заставить все это работать, является TenantAwareDataSource . Это реализация javax.sql.DataSource, которая делегирует правильный источник данных в зависимости от текущего идентификатора клиента . Идея этого класса находилась под сильным влиянием SpringR AbstractRoutingDataSource (стоящий на плечах других проектов с открытым исходным кодом!).
Маршрутизация к правильному источнику данных выполняется путем получения текущего идентификатора клиента из экземпляра TenantInfoHolder . Как видно из приведенного выше фрагмента кода, это также является обязательным аргументом при создании MultiSchemaMultiTenantProcessEngineConfiguration . TenantInfoHolder — это интерфейс, который вам нужно реализовать, в зависимости от того, как пользователи и арендаторы управляются в вашей среде. Обычно вы используете ThreadLocal для хранения информации о текущем пользователе / арендаторе (как это делает Spring Security), которая заполняется каким-либо фильтром безопасности. Этот класс эффективно действует как « компонент маршрутизации» на рисунке ниже:
В примере с модульным тестом мы действительно используем ThreadLocal для хранения текущего идентификатора клиента и заполняем его некоторыми демонстрационными данными:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
private void setupTenantInfoHolder() { DummyTenantInfoHolder tenantInfoHolder = new DummyTenantInfoHolder(); tenantInfoHolder.addTenant( "alfresco" ); tenantInfoHolder.addUser( "alfresco" , "joram" ); tenantInfoHolder.addUser( "alfresco" , "tijs" ); tenantInfoHolder.addUser( "alfresco" , "paul" ); tenantInfoHolder.addUser( "alfresco" , "yvo" ); tenantInfoHolder.addTenant( "acme" ); tenantInfoHolder.addUser( "acme" , "raphael" ); tenantInfoHolder.addUser( "acme" , "john" ); tenantInfoHolder.addTenant( "starkindustries" ); tenantInfoHolder.addUser( "starkindustries" , "tony" ); this .tenantInfoHolder = tenantInfoHolder; } |
Теперь мы запускаем некоторый экземпляр процесса, а также переключаем текущий идентификатор клиента. На практике вы должны представить, что несколько потоков приходят с запросами, и они устанавливают текущий идентификатор клиента на основе зарегистрированного пользователя:
1
2
3
4
|
startProcessInstances( "joram" ); startProcessInstances( "joram" ); startProcessInstances( "raphael" ); completeTasks( "raphael" ); |
Приведенный выше метод startProcessInstances устанавливает идентификатор текущего пользователя и арендатора и запускает несколько экземпляров процесса, используя стандартный API Activiti, как если бы не было многопользовательского режима (метод completeTasks аналогичным образом выполняет несколько задач).
Также очень здорово, что вы можете динамически регистрировать (и удалять) новых арендаторов , используя тот же метод, который использовался при сборке механизма процесса. Движок Activiti удостоверится, что схема базы данных либо создана, либо проверена.
1
|
config.registerTenant( "dailyplanet" , createDataSource( "jdbc:h2:mem:activiti-mt-daily;DB_CLOSE_DELAY=1000" , "sa" , "" )); |
Вот фильм, показывающий выполнение модульного теста и эффективную изоляцию данных:
Мультитенант
Последний кусочек головоломки — исполнитель работы. Обычный API Activiti вызывает «заимствовать» текущий поток для выполнения своих операций и, таким образом, может использовать любой контекст пользователя / арендатора, который был установлен ранее в потоке.
Исполнитель задания, однако, работает с использованием фонового пула потоков и не имеет такого контекста. Поскольку AsyncExecutor в Activiti является интерфейсом, нетрудно реализовать многосетевой мультитенантный исполнитель заданий. В настоящее время мы добавили две реализации. Первая реализация называется SharedExecutorServiceAsyncExecutor :
1
2
3
|
config.setAsyncExecutorEnabled( true ); config.setAsyncExecutorActivate( true ); config.setAsyncExecutor( new SharedExecutorServiceAsyncExecutor(tenantInfoHolder)); |
Эта реализация (как следует из названия) использует один пул потоков для всех арендаторов. У каждого арендатора есть свои потоки получения работы, но как только работа получена, она помещается в общий пул потоков. Преимущество этой системы заключается в том, что количество потоков, используемых Activiti, ограничено.
Вторая реализация называется ExecutorPerTenantAsyncExecutor :
1
2
3
|
config.setAsyncExecutorEnabled( true ); config.setAsyncExecutorActivate( true ); config.setAsyncExecutor( new ExecutorPerTenantAsyncExecutor(tenantInfoHolder)); |
Как следует из названия, этот класс действует как прокси AsyncExecutor. Для каждого зарегистрированного арендатора загружается полный AsyncExecutor по умолчанию. Каждый со своими собственными потоками сбора и выполнения пула потоков. «Прокси» просто делегирует нужный экземпляр AsyncExecutor. Преимущество такого подхода состоит в том, что каждый арендатор может иметь детальную конфигурацию исполнителя, адаптированную к потребностям арендатора.
Вывод
Как всегда, все отзывы более чем приветствуются. Попробуйте мульти-арендную схему с несколькими схемами и дайте нам знать, что вы думаете и что можно улучшить в будущем!
Ссылка: | Multi-Tenancy с отдельными схемами базы данных в Activiti от нашего партнера JCG Йорама Барреса в блоге « Маленькие шаги с большими ногами» . |