activiti 多租户_Activiti中具有独立数据库架构的多租户

activiti 多租户

我们过去听到的一项功能请求是以多租户方式运行Activiti引擎,使租户的数据与其他数据隔离。 当然,在某些云/ SaaS环境中,这是必须的。

几个月前,波恩大学的学生拉斐尔·吉伦(Raphael Gielen)与我接触,他正在撰写有关Activiti多租户的硕士学位论文。 几周前,我们在一个共同工作的咖啡馆聚会,反弹想法,并为租户一起破解了具有数据库模式隔离的第一个原型。 很有趣 :-)。

无论如何,我们一直在完善和完善该代码,并将其提交给Activiti代码库。 让我们在下面的前两个部分中了解使用Activiti进行多租户的现有方法。 在第三部分中,我们将深入研究新的多租户多模式功能,其中包含一些实际工作的代码示例!

共享数据库多租户

Activiti已经有一段时间可以支持多租户了(从5.15版本开始)。 所采用的方法是共享数据库 :拥有一个(或多个)Activiti引擎,并且它们都进入同一个数据库。 数据库表中的每个条目都有一个租户标识符 ,最好将其理解为该数据的一种标记。 然后,Activiti引擎和API会读取并使用该租户标识符在租户的上下文中执行其各种操作。

例如,如下图所示,两个不同的租户可以具有使用相同密钥的流程定义。 引擎和API确保没有数据混合。

截图-2015-10-06-12.57.00

这种方法的好处是部署简单,因为与设置“常规” Activiti引擎没有区别。 缺点是您必须记住使用正确的API调用(即那些考虑了租户标识符的调用)。 而且,它与任何具有共享资源的系统都存在相同的问题:租户之间总是存在对资源的竞争。 在大多数用例中,这很好,但是有些用例不能以这种方式完成,例如为某些租户提供更多或更少的系统资源。

多引擎多租户

自Activiti的第一个版本以来,另一种可能的方法就是为每个租户简单地拥有一个引擎实例:

截图-2015-10-06-13.12.56

在此设置中,每个租户可以具有不同的资源配置,甚至可以在不同的物理服务器上运行。 当然,此图中的每个引擎可以是多个引擎,以提高性能/故障转移/等等。 现在的好处是,资源是为租户量身定制的。 缺点是设置比较复杂(多个数据库架构,每个租户都有不同的配置文件,等等)。 每个引擎实例都将占用内存(但是Activiti会占用很少的内存)。 另外,您无需编写一些路由组件 ,就可以以某种方式知道当前租户的上下文并路由到正确的引擎。

截图-2015-10-06-13.18.36-300x206

多架构多租户

Activiti多租户故事的最新添加是在两周前添加的(这是commit ),同时是版本5和6的添加。这里,每个租户都有一个数据库(模式),但是只有一个引擎实例。 再次,在实践中,可能有多个用于性能/故障转移/等的实例,但是概念是相同的:

截图-2015-10-06-13.41.20

好处显而易见:只有一个引擎实例可以管理和配置,而且API与非多租户引擎完全相同。 但最重要的是,租户的数据与其他租户的数据完全分开。 缺点(类似于多引擎多租户方法)是有人需要管理和配置不同的数据库。 但是复杂的引擎管理已不复存在。

我上面链接到的提交还包含一个单元测试 ,该测试显示了多模式多租户引擎的工作方式。

构建流程引擎很容易,因为有一个MultiSchemaMultiTenantProcessEngineConfiguration可抽象出大多数细节:

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实现,它根据当前租户标识符委托给正确的数据源。 此类的思想在很大程度上受到Spring的AbstractRoutingDataSource (站在其他开源项目的肩膀上!)的影响。

通过从TenantInfoHolder实例获取当前的租户标识符来完成到正确数据源的路由。 如您在上面的代码片段中所看到的,在构造MultiSchemaMultiTenantProcessEngineConfiguration时,这也是必需的参数。 TenantInfoHolder是您需要实现的接口,具体取决于您环境中用户和租户的管理方式。 通常,您将使用ThreadLocal来存储由某些安全过滤器填充的当前用户/租户信息(类似于Spring Security)。 该类有效地充当下图中的路由组件”:

截图-2015-10-06-13.53.131

在单元测试示例中,我们确实使用了ThreadLocal来存储当前的租户标识符 ,并用一些演示数据填充它:

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;}

现在,我们开始一些流程实例,同时还要切换当前的租户标识符。 在实践中,您必须想象有多个线程随请求一起进入,它们将根据登录的用户设置当前的租户标识符:

startProcessInstances("joram");
startProcessInstances("joram");
startProcessInstances("raphael");
completeTasks("raphael");

上面的startProcessInstances方法将使用标准Activiti API设置当前用户和租户标识符,并启动几个流程实例, 就好像根本没有多租户一样completeTasks方法类似地完成了一些任务)。

同样很酷的是, 您可以使用与构建流程引擎时相同的方法来动态注册(和删除)新的租户 。 Activiti引擎将确保已创建或验证数据库架构。

config.registerTenant("dailyplanet", createDataSource("jdbc:h2:mem:activiti-mt-daily;DB_CLOSE_DELAY=1000", "sa", ""));

这是一部电影,显示正在运行的单元测试以及有效隔离的数据:

多租户工作执行者

最后一个难题是工作执行者。 常规Activiti API调用“借用”当前线程以执行其操作,因此可以使用之前在该线程上设置的任何用户/租户上下文。

但是,作业执行程序使用后台线程池运行,并且没有此类上下文。 由于Activiti中的AsyncExecutor是一个接口,因此实现多方案多租户作业执行器并不难。 当前,我们添加了两个实现。 第一个实现称为SharedExecutorServiceAsyncExecutor

config.setAsyncExecutorEnabled(true);
config.setAsyncExecutorActivate(true);
config.setAsyncExecutor(new SharedExecutorServiceAsyncExecutor(tenantInfoHolder));

此实现(顾名思义)对所有租户使用一个线程池。 每个租户确实都有其自己的作业获取线程,但是一旦获取了作业,便将其放到共享线程池中。 该系统的好处是Activiti使用的线程数受到限制。

第二种实现称为ExecutorPerTenantAsyncExecutor

config.setAsyncExecutorEnabled(true);
config.setAsyncExecutorActivate(true);
config.setAsyncExecutor(new ExecutorPerTenantAsyncExecutor(tenantInfoHolder));

顾名思义,该类充当“代理” AsyncExecutor。 对于每个注册的租户,将启动完整的默认AsyncExecutor。 每个都有自己的获取线程和执行线程池。 “代理”仅委托给正确的AsyncExecutor实例。 这种方法的好处是,每个租户都可以根据租户的需求进行细粒度的作业执行者配置。

结论

一如既往,欢迎所有反馈。 放手多方案多租户,让我们知道您的想法以及将来可以改进的地方!

翻译自: https://www.javacodegeeks.com/2015/10/multi-tenancy-with-separate-database-schemas-in-activiti.html

activiti 多租户

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/336855.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jigsaw kaggle_使用Project Jigsaw的JDK 9 Early Access上的Eclipse IDE

jigsaw kaggle几周前,我写了关于在Java 9上运行Eclipse Neon的文章 (尽管,我在帖子标题中错误地且令人尴尬地留下了“火星”)。 值得注意的是,我列出的步骤也适用于带有Project Jigsaw (Java模块化&#xf…

win7 activemq_带有骆驼,ActiveMQ,Elasticsearch的关键HL7用例

win7 activemqML7上的HL7是可以说HL7协议格式的系统的一种非常常见的传输机制。 JBoss Fuse是一个非常强大的微服务样式集成平台,在为关键医疗保健提供者构建灵活,有弹性,高可用性的集成方案方面拥有良好的经验。 此外,在JCAPS上替…

jboss4 迁移_应用程序服务器迁移:从JBoss EE5到Wildfly EE7

jboss4 迁移几周前,我发布了一个有关从Java EE 5迁移到7的博客 。这主要是关于如何使用新的Java EE 7改进Java EE 5代码。 现在,在这篇文章中,我将对应用程序服务器端的迁移路径进行一些研究。 如果您使用的是Java EE 5,则很有可…

java xmlrpc2.0 实现_简单的java xmlrpc

简单的 XMLRPCjava---XmlRpcClientpackage com.company;import org.apache.xmlrpc.XmlRpcClient;import org.apache.xmlrpc.XmlRpcException;import java.net.MalformedURLException;import java.util.Hashtable;import java.util.Vector;public class…

操作方法:带有Thymeleaf和Spring Boot的Java 8日期和时间

如果您碰巧使用Spring Boot和Thymeleaf,并且需要在视图中格式化Java 8 Date&Time对象,则可以使用thymeleaf-extras-java8time –用于Java 8 Date&Time API的Thymeleaf模块。 向现有的基于Maven或Gradle的Spring Boot项目中添加th…

java boundary_上传文件multipart form-data boundary 说明

含义 ENCTYPE"multipart/form-data" 说明:通过 http 协议上传文件 rfc1867协议概述,客户端发送内容构造。概述在最初的 http 协议中,没有上传文件方面的功能。 rfc1867为 http 协议添加了这个功能。客户端的浏览器,如 M…

java 预处理语句_预处理语句PreparedStatement到底咋用啊

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼我自己试了一下直接查询id为1的语句,有一条结果出来,但是用了PreparedStatement后就没有结果了,有大佬知道为什么吗orzpackage test;import java.io.IOException;import java.io.PrintWriter;imp…

crud-table_我个人的CRUD故事-或我如何来到CUBA平台

crud-table在此博客文章中,我想介绍一下我如何使用CUBA平台以及该工具的好处。 在我年轻的“业务应用程序开发”历史上,我将深入探讨各个阶段,只是给您一些背景知识。 因此,让我们从如何进入典型的CRUD应用程序开始,以…

python api接口生成_Django 自动生成api接口文档教程

最近在写测试平台,需要实现一个节点服务器的api,正好在用django,准备使用djangorestframework插件实现。需求实现一个接口,在调用时,通过传递的参数,直接运行对应项目的自动化测试环境Python3.6 ,PyCharm,W7项目结构功…

eclipse clean_Clean Sheet – Windows 10的人体工程学Eclipse主题

eclipse cleanClean Sheet是适用于Windows 10的符合人体工程学的Eclipse主题。它基于干净,低眩光的外观和感觉,旨在减轻视觉疲劳和眼睛疲劳。 它融合了均衡的颜色选择,可以突出谐波语法并注重可读性。 除自定义滚动条外,它还努力满…

什么时候需要使用cqrs_在CQRS读取模型中使用Hibernate进行快速开发

什么时候需要使用cqrs在本文中,我将分享一些在CQRS读取模型中使用Hibernate工具进行快速开发的技巧。 为什么要Hibernate? Hibernate非常流行。 从外观上看,它也很容易,而从内部看,它却相当复杂。 它可以很容易地上手…

java面试题三_最全的java面试题目三

78、什么情况下调用doGet()和doPost()?答:Jsp页面中的form标签里的method属性为get时调用doGet(),为post时调用doPost()。79、servlet的生命周期答:web容器加载servlet,生命周期开始。通过调用servlet的init()方法进行…

python的内存管理机制及调优手段_Python的内存管理机制及调优手段

内存管理机制:引用计数、垃圾回收、内存池引用计数:引用计数是一种非常高效的内存管理手段,当一个Python对象引用时其引用计数加一,当其不再被一个变量引用时则减一。当引用计数等于0时对象被删除。1.引用计数:引用计数…

tcga癌症亚型获取_亚型多态性应用于元组的危险

tcga癌症亚型获取Java 8具有lambda和stream&#xff0c;但是没有元组&#xff0c;这真是令人遗憾 。 这就是为什么我们在jOOλ中实现了元组-Java 8的缺失部分 。 元组确实是无聊的值类型容器。 本质上&#xff0c;它们只是这些类型的枚举&#xff1a; public class Tuple2<…

java 隐藏了什么_JAVA程序中封装与隐藏是什么意思

qq_遁去的一_1隐藏就是封装吧。。。。封装是把过程和数据包围起来&#xff0c;对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念&#xff0c;即现实世界可以被描绘成一系列完全自治、封装的对象&#xff0c;这些对象通过一个受保护的接口访问其他对象。封装是一…

esb 和 开源esb_如果今天完成,ESB会是什么样子?

esb 和 开源esbJavaOne 2015即将结束&#xff0c;这又是一次很棒的社区活动。 我和Rafael进行了两次会议和一次HOL 。 我最喜欢的会议之一实际上是&#xff1a;谈论ESB&#xff0c;如果今天完成的话。 我以为那是过去&#xff1f; 我通常也倾向于这样说。 但是&#xff0c;系统…

log4j2 无日志记录_在Log4j2中更好地执行非日志记录器调用

log4j2 无日志记录使用Log4j 1.x并希望避免在某些情况下可能会造成额外的性能影响&#xff08;即使实际上未记录该消息&#xff09;时&#xff0c;通常使用日志记录防护 。 Java的简单日志记录外观 &#xff08; SLF4J &#xff09;带给Java日志记录的最吸引人的功能之一是能够…

JAVA8如何进行IDEA配置_IntelliJ Idea 配置jdk8

环境&#xff1a; 环境变量JAVA_HOME配置jdk1.8idea配置支持lambda&#xff1a;File -> Project Structure -> Modules -> Language level, 选择: 8-Lambdas, type annotations etc.File -> Project Structure -> Project -> Project language level, 选择…

spring 占位符默认值_Spring中的值注释中的占位符支持

spring 占位符默认值Value批注中的${...}占位符用于访问在PropertySource注册的属性。 这对于Spring应用程序中的Configuration bean非常有用&#xff0c;但不仅如此。 为确保这是可能的&#xff0c; PropertySourcesPlaceholderConfigurer必须存在于所有需要占位符解析的应用程…

java用户名检查数据库_登入界面账号密码是访问数据库,但登入问题时if判断时就是执行不了...

回复 2楼 林月儿我按着你的方法试了下&#xff0c;可还是这个判断不能执行 if(password.equals(drmm.getText())){view.setText("登入成功");完整代码如下&#xff1a;import java.awt.*;import java.awt.event.*;import *;import *;import java.…