工作流引擎租户

多租户

多租户考虑单个盘古BPM设施应为多个租户提供服务的情况。对于每个租户,都应确保一定的隔离性。例如,一个租户的流程实例不应干扰另一租户的流程实例。

多租户可以通过两种不同的方式来实现。一种方法是每个租户使用一个流程引擎。另一种方法是仅使用一个流程引擎并将数据与租户标识符关联。两种方式在数据隔离级别,维护工作量和可伸缩性方面彼此不同。两种方式的组合也是可能的。

具有租户标识符的单流程引擎

一个租户可以使用租户标识符(即,租户ID)来实现多租户。所有租户的数据都存储在一个表中(相同的数据库和架构)。通过存储在列中的租户标识符来提供隔离。

 

租户标识符在部署上指定,并传播到从部署创建的所有数据(例如,流程定义,流程实例,任务等)。为了访问特定租户的数据,过程引擎允许按租户标识符过滤查询或为命令指定租户标识符(例如,创建流程实例)。此外,流程引擎结合允许忽略租户标识符的身份服务,提供了透明的访问限制。

所有租户也可以共享相同的定义而无需为每个租户部署它们。在有大量租户的情况下,共享的定义可以简化部署的管理。

部署租户的定义

要为单个租户部署定义,必须在部署上设置租户标识符。给定的标识符将传播到部署的所有定义,以便它们属于租户。

如果未设置租户标识符,则部署及其定义属于所有租户。在这种情况下,所有租户都可以访问部署和定义。请参阅本节以了解有关如何使用共享定义的更多信息。

通过Java API指定租户标识符

使用存储库服务创建部署时,可以在DeploymentBuilder上设置租户标识符 。

repositoryService

  .createDeployment()

  .tenantId("tenant1")

  .addZipInputStream(inputStream)

  .deploy()

通过部署描述符指定租户标识符

如果是流程应用程序,则部署由processes.xml部署描述符指定。由于描述符可以包含多个流程档案(即部署),因此可以在每个流程档案上将租户标识符设置为tenantId属性。

<process-application

  xmlns="http://www.盘古BPM.org/schema/1.0/ProcessApplication"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <process-archive tenantId="tenant1">

    <process-engine>default</process-engine>

    <properties>

      <property name="isDeleteUponUndeploy">false</property>

      <property name="isScanForProcessDefinitions">true</property>

    </properties>

  </process-archive></process-application>

通过Spring配置指定租户标识符

当使用Spring Framework Integration 的自动资源部署时,可以在“ Process Engine配置”中将租户标识符指定为deploymentTenantId属性。

<bean id="processEngineConfiguration" class="org.盘古BPM.bpm.engine.spring.SpringProcessEngineConfiguration">

  <property name="deploymentResources">

    <array>

      <value>classpath*:/org/盘古BPM/bpm/engine/spring/test/autodeployment/autodeploy.*.cmmn</value>

      <value>classpath*:/org/盘古BPM/bpm/engine/spring/test/autodeployment/autodeploy.*.bpmn20.xml</value>

    </array>

  </property>

  <property name="deploymentTenantId" value="tenant1" /></bean>

特定于租户的定义的版本控制

为租户部署定义时,将为其分配一个版本,该版本与其他租户的定义无关。例如,如果为两个租户部署了新的流程定义,则两个定义都将被分配为version 1。一个租户内的版本控制的工作方式类似于不属于任何租户的定义版本控制

查询租户数据

租户特定数据的流程引擎查询(例如,部署查询,流程定义查询)允许按一个或多个租户标识符进行过滤。如果未设置标识符,那么结果将包含所有租户的数据。

请注意,如果不允许用户查看租户的数据,租户的透明访问限制可能会影响查询结果。

查询租户的部署

为了找到特定租户的部署,租户标识符必须传递给tenantIdIn上DeploymentQuery。

List<Deployment> deployments = repositoryService

  .createDeploymentQuery()

  .tenantIdIn("tenant1", "tenant2")

  .orderByTenantId()

  .asc()

  .list();

共享定义的情况下,通过调用按不属于任何租户的部署进行筛选可能很有用withoutTenantId()。

List<Deployment> deployments = repositoryService

  .createDeploymentQuery()

  .withoutTenantId()

  .list();

也可以通过调用按属于特定租户或没有租户的部署进行筛选includeDeploymentsWithoutTenantId()。

List<Deployment> deployments = repositoryService

  .createDeploymentQuery()

  .tenantIdIn("tenant1")

  .includeDeploymentsWithoutTenantId()

  .list();

查询租户的定义

与相似DeploymentQuery,定义查询允许按一个或多个租户以及不属于任何租户的定义进行过滤。

List<ProcessDefinition> processDefinitions = repositoryService

  .createProcessDefinitionQuery()

  .tenantIdIn("tenant1")

  .includeProcessDefinitionsWithoutTenantId();

  .list();

为租户运行命令

当为多个租户部署定义时,命令可能会模棱两可(例如,通过键启动流程实例)。如果执行了这样的命令,ProcessEngineException则抛出a。要成功运行命令,必须将租户标识符传递给命令。

请注意,如果只允许用户查看其中一个定义,则租户的透明访问限制可以省略租户标识符。

创建一个流程实例

要通过为多个租户部署的流程定义的键创建实例,必须将租户标识符传递给ProcessInstantiationBuilder 。

runtimeService

  .createProcessInstanceByKey("key")

  .processDefinitionTenantId("tenant1")

  .execute();

关联消息

关联消息API可用于一个消息给一个或所有租户相关。如果消息可以与多个租户的定义或执行相关,则必须将租户标识符传递给MessageCorrelationBuilder 。否则,将MismatchingMessageCorrelationException引发。

runtimeService

  .createMessageCorrelation("messageName")

  .tenantId("tenant1")

  .correlate();

要将消息与所有租户相关联,请不要将租户标识符传递给构建器并调用correlateAll()。

runtimeService

  .createMessageCorrelation("messageName")

  .correlateAll();

发送信号

信号API可用于传递信号给一个或所有租户。将租户标识符传递给SignalEventReceivedBuilder, 以将信号传递给特定的租户。如果没有传递标识符,则信号将传递给所有租户。

runtimeService

  .createSignalEvent("signalName")

  .tenantId("tenant1")

  .send();

当在流程(例如,中间信号事件或信号结束事件)中引发信号时,该信号将传递到与调用执行属于同一租户或没有租户的定义和执行。

创建案例实例

要通过为多个租户部署的案例定义关键字创建实例,必须将租户标识符传递给CaseInstanceBuilder 。

caseService

  .withCaseDefinitionByKey("key")

  .caseDefinitionTenantId("tenant1")

  .execute();

评估决策表

要通过为多个租户部署的键评估决策表,必须将租户标识符传递给DecisionEvaluationBuilder 。

decisionService

  .evaluateDecisionTableByKey("key")

  .decisionDefinitionTenantId("tenant1")

  .evaluate();

租户的透明访问限制

将盘古BPM集成到应用程序中时,将租户ID传递给每个盘古BPM API调用可能很麻烦。由于此类应用程序通常也具有“已认证用户”的概念,因此可以在设置认证时设置租户ID列表:

try {

  identityService.setAuthentication("mary", asList("accounting"), asList("tenant1"));

  // All API calls executed here have "tenant1" transparently set as tenantId}finally {

  identityService.clearAuthentication();}

在上面的示例中,在setAuthentication(...)和之间执行的所有API调用clearAuthentication()都是通过提供的租户ID透明地执行的。

查询范例

以下查询

try {

  identityService.setAuthentication("mary", asList("accounting"), asList("tenant1"));

  repositoryService.createProcessDefinitionQuery().list();}finally {

  identityService.clearAuthentication();}

相当于

repositoryService.createProcessDefinitionQuery()

  .tenantIdIn("tenant1")

  .includeProcessDefinitionsWithoutTenantId()

  .list();

任务访问示例

对于其他命令,例如complete(),透明访问检查可确保通过身份验证的用户不会访问其他租户的资源:

try {

  identityService.setAuthentication("mary", asList("accounting"), asList("tenant1"));

  // throws an exception if task has tenant id other than "tenant1"

  taskService.complete("someTaskId");}finally {

  identityService.clearAuthentication();}

从身份服务获取用户的租户ID

流程引擎的身份服务可用于管理用户,组和租户及其关系。以下示例显示如何获取给定用户的组和租户列表,然后在设置身份验证时使用这些列表:

List<Tenant> groups = identityService.createGroupQuery()

  .userMember(userId)

  .list();

List<Tenant> tenants = identityService.createTenantQuery()

  .userMember(userId)

  .includingGroupsOfUser(true)

  .list();try {

  identityService.setAuthentication(userId, groups, tenants);

  // get all tasks visible to user.

  taskService.createTaskQuery().list();

  }finally {

  identityService.clearAuthentication();}

LDAP身份服务

上面的示例仅适用于数据库身份服务(即默认实现)。该LDAP身份服务不支持租户。

盘古BPM Rest API和Web应用程序

盘古BPM Rest API和Web应用程序的Cockpit和Tasklist支持透明访问限制。当用户登录时,他只能看到并且只能访问属于他的一个租户的数据(例如,流程定义)。

可以在Admin Web应用程序中管理租户及其成员身份。

禁用透明访问限制

默认情况下启用透明访问限制。要禁用这些限制,请将ProcessEngineConfiguration中的tenantCheckEnabled属性设置为。false

另外,也可以禁用单个命令的限制(例如,维护任务)。使用CommandContext禁用和启用当前命令的限制。

commandContext.disableTenantCheck();// e.g., do maintenance tasks over all tenants

commandContext.enableTenantCheck();

请注意,如果在中禁用了命令限制,则无法启用这些限制ProcessEngineConfiguration。

以管理员身份访问所有租户

属于该组成员的用户盘古BPM-admin可以访问所有租户的数据,即使他们不属于这些租户。这对于多租户应用程序的管理员很有用,因为他必须管理所有租户上的数据。

所有租户的共享定义

在“ 为租户部署定义”部分中,说明了如何为特定租户部署过程定义或决策定义。结果是该定义仅对部署它的租户可见,而对其他租户不可见。如果租户有不同的流程和决定,这将很有用。但是,在许多情况下,所有租户都应使用相同的定义。在这种情况下,希望以对所有租户可见的方式仅部署一次定义。然后,当特定租户创建新实例时,该实例仅应对该租户(当然还有管理员)可见。这可以通过我们称为“共享定义”的使用模式来实现。按术语使用模式 我们的意思是,它本身并不是盘古BPM的功能,而是一种使用它来实现所需行为的特定方式。

部署共享定义

部署共享定义只是“常规”部署,没有为部署分配租户ID:

repositoryService

  .createDeployment()

  .addClasspathResource("processes/default/mainProcess.bpmn")

  .addClasspathResource("processes/default/subProcess.bpmn")

  .deploy();

在查询中包括共享定义

通常,在应用程序中,我们希望向用户提供“可用”流程定义的列表。在具有共享资源的多租户上下文中,我们希望列表包含具有以下属性的定义:

1、租户ID是当前用户的租户ID,

2、租户ID为null=>进程是共享资源。

要通过查询实现此目的,查询需要限制用户的租户ID列表(通过调用tenantIdIn(...)),并包括不包含租户ID的定义(includeProcessDefinitionsWithoutTenantId())。或者,以另一种方式查看它:排除所有具有与当前用户的租户ID不同的租户ID的定义。

例:

repositoryService.createProcessDefinitionQuery()

  .tenantIdIn("someTenantId")

  .includeProcessDefinitionsWithoutTenantId()

  .list();

实例化共享定义

创建(启动)新流程实例时,流程定义的租户ID会传播到流程实例。共享资源没有租户ID,这意味着不会自动传播任何租户ID。为了将启动流程实例的用户的租户ID分配给流程实例, 需要提供TenantIdProvider SPI 的实现。

TenantIdProvider创建流程定义,案例定义或决策定义的实例时,receives 将接收回调。然后,它可以将租户ID分配给新创建的实例(或不分配)。

以下示例显示如何基于当前身份验证为实例分配租户ID:

public class CustomTenantIdProvider implements TenantIdProvider {

  @Override

  public String provideTenantIdForProcessInstance(TenantIdProviderProcessInstanceContext ctx) {

    return getTenantIdOfCurrentAuthentication();

  }

  @Override

  public String provideTenantIdForCaseInstance(TenantIdProviderCaseInstanceContext ctx) {

    return getTenantIdOfCurrentAuthentication();

  }

  @Override

  public String provideTenantIdForHistoricDecisionInstance(TenantIdProviderHistoricDecisionInstanceContext ctx) {

    return getTenantIdOfCurrentAuthentication();

  }

  protected String getTenantIdOfCurrentAuthentication() {

    IdentityService identityService = Context.getProcessEngineConfiguration().getIdentityService();

    Authentication currentAuthentication = identityService.getCurrentAuthentication();

    if (currentAuthentication != null) {

      List<String> tenantIds = currentAuthentication.getTenantIds();

      if (tenantIds.size() == 1) {

        return tenantIds.get(0);

      } else if (tenantIds.isEmpty()) {

        throw new IllegalStateException("no authenticated tenant");

      } else {

        throw new IllegalStateException("more than one authenticated tenant");

      }

    } else {

      throw new IllegalStateException("no authentication");

    }

  }}

要使用TenantIdProvider,必须在Process Engine配置中进行设置,例如使用盘古BPM.cfg.xml:

<beans>

  <bean id="processEngineConfiguration" class="org.盘古BPM.bpm.engine.impl.cfg.StandaloneProcessEngineConfiguration">

    <!-- ... -->

    

    <property name="tenantIdProvider" ref="tenantIdProvider" />

  </bean>

  

  <bean id="tenantIdProvider" class="org.盘古BPM.bpm.CustomTenantIdProvider"></beans>

在共享流程引擎的情况下,可以通过Process Engine Plugin设置提供程序

租户活动的特定于租户的行为

到目前为止,我们已经看到,如果租户具有相同的流程定义,则共享资源是一种有用的模式。好处是我们不必为每个租户部署相同的流程定义。然而,在许多实际应用中,情况有些介于两者之间:房客共用大部分相同的流程定义,但也有一些特定于租户的变化。

如何处理此问题的常见模式是在单独的过程中提取特定于租户的行为,然后使用调用活动来调用该行为。使用业务规则任务的特定于租户的决策逻辑(即决策表)也很常见。

为此,呼叫活动或业务规则任务需要根据当前流程实例的租户ID选择要调用的正确定义。该共享资源实例说明如何实现这一目标。

也可以看看:

1、共享资源示例

2、称为元素租户ID

3、呼叫活动的案例租户ID

4、业务规则任务的决策参考租户ID

每个租户一个流程引擎

多租户可以通过为每个租户提供一个流程引擎来实现。每个流程引擎都配置为使用连接租户数据的不同数据源。租户的数据可以存储在不同的数据库中,一个具有不同模式的数据库中或一个具有不同表的模式中。

 

流程引擎可以在同一台服务器上运行,以便所有引擎共享相同的计算资源,例如数据源(通过模式或表隔离时)或用于异步作业执行的线程池。

讲解

您可以看到该示例如何通过模式实现数据隔离的多租户。

配置流程引擎

可以在配置文件中或通过Java API配置流程引擎。每个引擎应具有与租户相关的名称,以便可以根据租户进行标识。例如,每个引擎都可以根据其服务的租户来命名。有关详细信息,请参见“ Process Engine引导程序”部分。

数据库隔离

如果不同的租户应在完全不同的数据库上工作,则他们必须使用不同的JDBC设置或不同的数据源。

模式或表隔离

对于基于模式或表的隔离,可以使用单个数据源,这意味着可以在多个引擎之间共享连接池之类的资源。为了达成这个,

1、配置选项databaseTablePrefix可用于配置数据库访问。

2、考虑打开设置useSharedSqlSessionFactory。该设置控制每个流程引擎实例是应该解析还是维护mybatis映射文件的本地副本,还是可以使用单个共享副本。由于映射需要大量堆(> 30MB),因此建议将其打开。这样,只需要分配一份副本。

useSharedSqlSessionFactory设置的注意事项

useSharedSqlSessionFactory一旦建立,该设置将在静态字段中缓存mybatis sql会话工厂。使用此配置设置时,您需要注意

1、仅当所有使用该设置的流程引擎共享相同的数据源和事务工厂时,才可以使用它

2、一旦设置,该字段中的引用就永远不会清除。通常这不是问题,但如果是这样,用户必须通过以下方式手动清除该字段:将其显式设置为null

ProcessEngineConfigurationImpl.cachedSqlSessionFactory = null

多流程引擎的作业执行器

对于流程和任务的后台执行,流程引擎具有一个称为job executor的组件。作业执行器定期从数据库中获取作业,并将其提交到线程池以执行。对于一台服务器上的所有流程应用程序,一个线程池用于执行作业。此外,可以在多个引擎之间共享获取线程。这样,即使使用大量的流程引擎,资源仍然是可管理的。有关详细信息,请参见“作业执行程序和多个流程引擎 ”部分。

架构隔离的示例配置

可以以各种方式配置流程引擎来应用多租户设置。以下是bpm-platform.xml文件的示例,该文件为共享同一数据库但在不同模式下工作的两个租户指定引擎:

<?xml version="1.0" encoding="UTF-8"?><bpm-platform xmlns="http://www.盘古BPM.org/schema/1.0/BpmPlatform"

              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

              xsi:schemaLocation="http://www.盘古BPM.org/schema/1.0/BpmPlatform http://www.盘古BPM.org/schema/1.0/BpmPlatform">

  <job-executor>

    <job-acquisition name="default" />

  </job-executor>

  <process-engine name="tenant1">

    <job-acquisition>default</job-acquisition>

    <configuration>org.盘古BPM.bpm.engine.impl.cfg.StandaloneProcessEngineConfiguration</configuration>

    <datasource>java:jdbc/ProcessEngine</datasource>

    <properties>

      <property name="databaseTablePrefix">TENANT_1.</property>

      <property name="history">full</property>

      <property name="databaseSchemaUpdate">true</property>

      <property name="authorizationEnabled">true</property>

      <property name="useSharedSqlSessionFactory">true</property>

    </properties>

  </process-engine>

  <process-engine name="tenant2">

    <job-acquisition>default</job-acquisition>

    <configuration>org.盘古BPM.bpm.engine.impl.cfg.StandaloneProcessEngineConfiguration</configuration>

    <datasource>java:jdbc/ProcessEngine</datasource>

    <properties>

      <property name="databaseTablePrefix">TENANT_2.</property>

      <property name="history">full</property>

      <property name="databaseSchemaUpdate">true</property>

      <property name="authorizationEnabled">true</property>

      <property name="useSharedSqlSessionFactory">true</property>

    </properties>

  </process-engine></bpm-platform>

部署租户的定义

在开发流程应用程序(即流程定义和补充代码)时,某些流程可能会部署到每个租户的引擎,而其他流程则是特定于租户的。作为每个流程应用程序一部分的process.xml部署描述符通过流程归档的概念提供了这种灵活性。一个应用程序可以包含任意数量的流程归档部署,每个部署都可以使用不同的资源部署到不同的流程引擎。有关详细信息,请参见processes.xml部署描述符部分。

以下是为两个租户部署不同流程定义的示例。它使用配置属性resourceRootPath,该属性指定部署中的路径,该路径包含要部署的流程定义。因此,processes/tenant1应用程序的类路径下的所有进程都部署到engine tenant1,而其下的所有进程processes/tenant2都部署到engine tenant2。

<process-application

  xmlns="http://www.盘古BPM.org/schema/1.0/ProcessApplication"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <process-archive name="tenant1-archive">

    <process-engine>tenant1</process-engine>

    <properties>

      <property name="resourceRootPath">classpath:processes/tenant1/</property>

      <property name="isDeleteUponUndeploy">false</property>

      <property name="isScanForProcessDefinitions">true</property>

    </properties>

  </process-archive>

  <process-archive name="tenant2-archive">

    <process-engine>tenant2</process-engine>

    <properties>

      <property name="resourceRootPath">classpath:processes/tenant2/</property>

      <property name="isDeleteUponUndeploy">false</property>

      <property name="isScanForProcessDefinitions">true</property>

    </properties>

  </process-archive></process-application>

访问租户的流程引擎

要在运行时访问特定租户的流程引擎,必须通过其名称进行标识。盘古BPM引擎可以访问各种编程模型中的命名引擎:

1、普通的Java API:通过ProcessEngineService可以访问任何命名的引擎。

2、CDI集成:可以直接输入命名的引擎bean。该内置CDI豆生产商可以专门用来动态地访问当前租户的引擎。

3、通过JBoss / Wildfly上的JNDI:在JBoss和Wildfly上,可以通过JNDI查找每个容器管理的流程引擎。

盘古BPM Web应用程序的Cockpit,Tasklist和Admin通过在不同的流程引擎之间进行切换,提供了特定于租户的视图。

 

 技术支持:盘古BPM工作流平台

相关教程