JAVA二开工具开源 (三)–多租户实现方案

最近想了解如何Java对接微信平台,快速搭建完整项目开发,发现网上有很对开源的这类二开源码。https://gitee.com/luozijing123/JooLun-wx(Frok)就是其中一个,但是这里面并没有实现多租户的设计,后续在git上发现了一个又一个开源,是基于JooLun-wx的基础上继续封装组件,并且另外加了很多功能的开源项目,具体地址是https://github.com/YunaiV/ruoyi-vue-pro,具体了解下该项目是如何实现多租户的。

多租户的设计主要还是在数据隔离上,有以下隔离方式:

1.表行级别数据隔离,同一张表存储不同租户数据,加TenantID多租户的数据字段,优点:支持的租户数量最多、简单,节省资源 缺点:不适合数剧量大、数据备份和恢复最困难、数据不安全,一故障全部故障

2.同库不同表数据隔离,每一张表存储不同租户数据,利用分表实现,优点:增大了存储数据量、隔离性较好 缺点:支持的租户数量少、数据恢复和统计困难

3.不同库不同表数据隔离,每个库存储不同租户数据,利用分库分表实现,优点:存储数据量最大、隔离性最好 、故障恢复简单 缺点:支持的租户数量少、成本高

多租户与用户权限模型

租户应该是一个集体的概念,包含了用户、角色、组织等,并且能够实现数据隔离和资源隔离的特性,每个租户之间操作不影响且不相互可见。真正实现起来的该架构还是挺难的。首先是租户权限和角色模型的定义,这里介绍下ruoyi-vue-pro的租户业务模型。同一租户,这里可以理解统一公司ID下的用户或者员工具有角色、岗位、部门属性,不同的岗位对应相应角色,角色拥有相应的操作权限或者菜单可见权限,同时,在不同部门间的数据权限控制,不同部门是不可见的(mybatisPlus interceptor控制),也可以自定义其他数据权限,例如加上用户表的UserID字段,表示只有自己才能编辑改数据。

多租户方案实现方案–mybatisPlus方案

方案大致实现是利用threadLocal存储前端的放置在请求头中的租户ID,然后在springSecurity中的过滤器进行租户的验证。

mybatisPlus interceptor提供了一个拦截器,提供了TenantId 行级处理租户的功能,定义了一些获取租户 ID、获取租户字段名、忽略表的一些方法,这个接口TenantLineHandler需要用户自己去实现。

/*** 基于 MyBatis Plus 多租户的功能,实现 DB 层面的多租户的功能** @author 芋道源码*/
@AllArgsConstructor
public class TenantDatabaseInterceptor implements TenantLineHandler {private final TenantProperties properties;@Overridepublic Expression getTenantId() {return new LongValue( TenantContextHolder.getRequiredTenantId());}@Overridepublic boolean ignoreTable(String tableName) {return TenantContextHolder.isIgnore() // 情况一,全局忽略多租户|| CollUtil.contains(properties.getIgnoreTables(), tableName); // 情况二,忽略多租户的表}}

TenantLineInnerInterceptor就是实现多租户的插件了,其继承了JsqlParserSupport类,实现了InnerInterceptor接口。对sql进行处理,在查询和更新sqk中家伙是那个where TenantId = ? 条件,以select sql为例

protected void processPlainSelect(PlainSelect plainSelect) {FromItem fromItem = plainSelect.getFromItem();Expression where = plainSelect.getWhere();this.processWhereSubSelect(where);if (fromItem instanceof Table) {Table fromTable = (Table)fromItem;if (!this.tenantLineHandler.ignoreTable(fromTable.getName())) {plainSelect.setWhere(this.builderExpression(where, fromTable));}} else {this.processFromItem(fromItem);}List<SelectItem> selectItems = plainSelect.getSelectItems();if (CollectionUtils.isNotEmpty(selectItems)) {selectItems.forEach(this::processSelectItem);}List<Join> joins = plainSelect.getJoins();if (CollectionUtils.isNotEmpty(joins)) {this.processJoins(joins);}}protected Expression builderExpression(Expression currentExpression, Table table) {EqualsTo equalsTo = new EqualsTo();equalsTo.setLeftExpression(this.getAliasColumn(table));equalsTo.setRightExpression(this.tenantLineHandler.getTenantId());// 上面的TenantDatabaseInterceptor,获取TenantId拼接if (currentExpression == null) {return equalsTo;} else {return currentExpression instanceof OrExpression ? new AndExpression(new Parenthesis(currentExpression), equalsTo) : new AndExpression(currentExpression, equalsTo);}}

具体的方案可以在 https://doc.iocoder.cn/saas-tenant/页面看到。另外,数据权限的方案也是利用mybatisPlus的拦截器实现, 对sql进行拦截,根据用户相关权限拼接sql,做到数据访问的控制。

多租户方案实现方案–shardingJDBC方案

shardingJDBC可以具备下面能力,

  1. 读写分离 配置主从库

  2. 分库分表 配置多主库 多主表 路由规则(例如根据主键%2路由,分别配置两个主库,两个主表 2X2)

  3. 配置分库分表或者同库同表 进行读写分离

考虑部署可能没有那么多数据库实例来,简单可以选择同库分表方案来实现,根据租户ID来实现分表策略,配置文件如下,这里租户ID的生成是根据%2来判断入哪张表,这里真正实施的时候需要将所有租户的关联表,例如用户表、业务表等都需要创建分表来存储数据。这样适合数据量不多、租户表不多的情况。

server.port=8900#只有主库
spring.shardingsphere.datasource.names=masterspring.shardingsphere.datasource.master.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master.jdbc-url=jdbc:mysql://${dbIp}:3306/tenant_common?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=123456#配置路由策略,分配数据落的表
spring.shardingsphere.sharding.tables.sys_user.actual-data-nodes=master0.sys_user$->{0..1}
spring.shardingsphere.sharding.tables.sys_user.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.sys_user.table-strategy.inline.algorithm-expression=sys_user$->{id % 2}#\u6253\u5370sql
spring.shardingsphere.props.sql.show=true
spring.main.allow-bean-definition-overriding=true#\u8C03\u6574\u65E5\u5FD7\u4E3Adebug
logging.level.com.yisu= debug

也可以采用同一数据实例,多个数据源库来分租户,可以解决租户业务表隔离和数据量存储少的问题,这样就要配多个数据库源连接实例。

server.port=8900#数据源定义
spring.shardingsphere.datasource.names=master0,slave0,master1,slave1# 数据源 主库0
spring.shardingsphere.datasource.master0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master0.jdbc-url=jdbc:mysql://${dbIp}:3306/master0?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.shardingsphere.datasource.master0.username=root
spring.shardingsphere.datasource.master0.password=123456# 数据源 主库1
spring.shardingsphere.datasource.master1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master1.jdbc-url=jdbc:mysql://${dbIp}:3306/master1?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.shardingsphere.datasource.master1.username=root
spring.shardingsphere.datasource.master1.password=123456# 数据源 从库0
spring.shardingsphere.datasource.slave0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave0.jdbc-url=jdbc:mysql://${dbIp}:3306/slave0?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.shardingsphere.datasource.slave0.username=root
spring.shardingsphere.datasource.slave0.password=123456# 数据源 从库1
spring.shardingsphere.datasource.slave1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave1.jdbc-url=jdbc:mysql://${dbIp}:3306/slave1?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.shardingsphere.datasource.slave1.username=root
spring.shardingsphere.datasource.slave1.password=123456#设置master0为主库,slave0为它的从库
spring.shardingsphere.sharding.master-slave-rules.master0.master-data-source-name=master0
spring.shardingsphere.sharding.master-slave-rules.master0.slave-data-source-names=slave0
#设置master1为主库,slave1为它的从库
spring.shardingsphere.sharding.master-slave-rules.master1.master-data-source-name=master1
spring.shardingsphere.sharding.master-slave-rules.master1.slave-data-source-names=slave1#根据id分库
spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=master$->{id % 2}
#根据id分表
spring.shardingsphere.sharding.tables.sys_user.actual-data-nodes=master$->{0..1}.sys_user$->{0..1}
spring.shardingsphere.sharding.tables.sys_user.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.sys_user.table-strategy.inline.algorithm-expression=sys_user$->{id % 2}#打印sql
spring.shardingsphere.props.sql.show=true
spring.main.allow-bean-definition-overriding=true#调整日志为debug
logging.level.com.yisu= debug

总结

实际多租户方案感觉第一种可行性更高,只需要在同一张表里面即可存储上千万租户数据,采用第二种或者第三种,成本太高,尤其是租户数量上来的时候,需要维护上千个数据库实例,维护成本相当高。sharding-JDBC的读写分离或者分库分表,主要还是用来切割大表数据,优化数据库读写效率。

JAVA二开工具开源 (三)--多租户实现方案相关推荐

  1. 抖音seo源码·源代码搭建·支持二开(开源)系统

    抖音seo源码搭建,抖音seo源码,抖音seo源码搭建的基础底层框架语言是后台语言PHP:python来编程开发的,抖音seo这套源码,是围绕了ai视频创意制作,六大账号矩阵发布,智能客户回复,企业号 ...

  2. Java剑开天门(二)

    Java知识复习二 相关导航 Java剑开天门(一) Java剑开天门(二) Java剑开天门(三) Java剑开天门(四) Java剑开天门(五) Java导航 Java知识复习二 前言 一.数组与 ...

  3. Java剑开天门(四)

    Java知识复习四 相关导航 Java剑开天门(一) Java剑开天门(二) Java剑开天门(三) Java剑开天门(四) Java剑开天门(五) Java导航 Java知识复习四 前言 一.反射机 ...

  4. JAVA 单商户商城系统 成熟源码 支持二开

    三勾商城是开发友好的微信小程序商城,框架支持SAAS,支持发布 iOS + Android + 公众号 + H5 + 各种小程序(微信/支付宝/百度/头条/QQ/钉钉/淘宝)等多个平台,不可多得的二开 ...

  5. 开源java性能分析工具_Java性能监控:您应该知道的5个开源工具

    开源java性能分析工具 鲜为人知但有用:开源应用程序性能监视的状态 对于任何应用程序来说,最重要的事情之一就是性能. 我们要确保用户获得他们能获得的最佳体验,并想知道我们的应用已启动并正在运行. 这 ...

  6. 开始研究WEKA,一个开源java的数据挖掘工具

    开始研究WEKA,一个开源java的数据挖掘工具. HS沉寂这么多天,谁知道偏偏在我申请离职的时候给我安排了个任务,哎,无语. 于是,今天看了一天的Weka. 主要是看了HS提供的三个文章(E文,在g ...

  7. Docker学习总结(36)——利用Google开源Java容器化工具Jib构建镜像

    一.前言 容器的出现让Java开发人员比以往任何时候都更接近"编写一次,到处运行"的工作流程,但要对Java应用程序进行容器化并非易事:你必须编写Dockerfile,以root身 ...

  8. java二维码生成-谷歌(Google.zxing)开源二维码生成学习及实例

    java二维码生成-谷歌(Google.zxing)开源二维码生成的实例及介绍  这里我们使用比特矩阵(位矩阵)的QR码编码在缓冲图片上画出二维码 实例有以下一个传入参数 OutputStream o ...

  9. 西门子三开接线图解_西门子二开三控开关接线最好有图

    西门子二开三控开关接线最好有图 个开关很简单嘛,一根进线,四根出线,不就是5个孔吗,当然就是四开了?你不是电工 xi men zi er kai san kong kai guan jie xian ...

最新文章

  1. 比特币核心(BCE)或许并没有你想象的强大
  2. es6结构赋值--数组
  3. 读javascript高级程序设计10-DOM
  4. Linux学习之服务器搭建——基础网络配置
  5. 全球英文经典演讲100篇_日语演讲100问(1)即兴演讲不即兴!(理论篇)
  6. 启动虚拟机报错VMware Workstation cannot connect to the virtual machine
  7. linux oracle异常,Linux上oracle常见安装异常总结
  8. Git 基础(八)—— Github 的使用(账号管理)
  9. linux+vi+注掉代码,VI编辑器之删除操作(示例代码)
  10. Frida 代码提示
  11. 对接微信支付之网页支付详解
  12. python视频压缩算法_深度学习之图像视频压缩技术
  13. 人人网移动开发架构及相关服务器架构
  14. SOM-TL437x是基于TI Sitara系列AM4376/AM4379 ARM Cortex-A9高性能低功耗处理器设计的工业级核心板
  15. CIO如何推广ERP系统
  16. CVPR 2021 | Involution:超越卷积和自注意力的神经网络新算子
  17. 在root目录下npm install报错Error: EACCES: permission denied, mkdir ‘/root/ttt/web/node_modul
  18. 【mysql进阶】MTS主从同步原理及实操指南(七)
  19. 用“仁义”,重新设计计算机操作系统。
  20. 软件开发到底是在做什么?

热门文章

  1. python将pdf转成excel_如何将pdf版的表格高效的转换成excel形式?
  2. 蔚来资深Linux嵌入式软件开发工程师面试
  3. lora和lorawan区别是什么?lora和lorawan可以转换吗?-东胜物联
  4. Pandas 学习笔记一
  5. QQ可以传输多大的文件
  6. VScode - 打字炫酷特效震动光效插件(Power Mode)
  7. SEO站群网站关键词挖掘
  8. java正则匹配公司名称_【已解决】Java的正则表达式java.util.regex中的命名的组(named group)...
  9. 并查集--武林盟主版
  10. 神武服务器物品开放,《神武4》电脑版庭院装饰游乐·对弈棋盘限服开放