多租户体系实现

文章目录

  • 核心思路
    • 方案选择
    • 设计考量
      • 安全性
      • 扩展性
      • 通用性
      • 易用性
  • 具体实现
    • 租户信息透传
      • 透传变量名命名规范
      • 应用内透传
      • 应用间透传
    • 数据层租户隔离
      • MySQL存储方案:多租户Mybatis插件
        • Mybatis插件特点
        • 使用多租户Mybatis插件的优势
        • 参考文档
  • 应用场景

经过工作中的一处场景启发,我进行了深入学习并总结出本篇文章。

核心思路

方案选择

多租户模型基本有以下三种(具体可以见这里),第一种形式隔离做的最彻底(isolated),多租户之间只在硬件层面进行共享;第三种形式共享做的最彻底(shared),在硬件、应用和DB层面都做到了共享。
多租户应用
一个应用要多租户化,选择何种形式,需要考虑具体的业务场景,同时也要考虑投入产出比,不同的形式投入成本不同,可以参考下面的图。上面的三种形式最左边可以认为是isolated approach,最右边是shared approach,第二种介于中间。越靠右的方式从初期来看投入成本更高,但从长久来看投入成本会更低。
在这里插入图片描述

设计考量

框架层面考量,如果从单纯项目实现层面也需要思考这些方面

安全性

对于一个多租户应用来说,任何业务处理、数据读写都需要有明确的租户上下文,这涉及到方方面面:

  • 当前用户是否有特定租户的权限?
  • 当前的web请求、RPC服务调用、任务启动、消息处理是否有特定的租户?
  • 当前的数据读写是否有明确的租户参数?

一旦发现没有权限、没有租户的程序在执行,保护机制及时进行检验、发现并予以熔断报错。

扩展性

不同的租户,往往会有不同程度的差异,差异可能来自:

  • 业务逻辑
  • 数据模型

通用性

不同的租户有不同的需求和配置。如果多租户系统不能适应不同租户的需求,那么它就无法满足市场需求,也无法提供一致且可靠的服务。多租户的实现需要一次很好的抽象,系统化的思考和沉淀,把通用性做的好将极大发挥价值。

易用性

很多框架的建设因为太重最后被人放弃,多租户框架想走轻量级路线,以达到非常容易使用的目的。这要考虑很多的设计模式,比如让springboot红于一时的“约定胜于配置”(Convention Over Configuration),让使用者在80%的场景下使用起来非常简单,不用关心配置。多租户框架也可以做成一个jar包,要多租户化的应用只要引入即可。

具体实现

租户信息透传

专指各个代码执行链路上,租户信息的传递。为什么采用透传,而不是通过修改原有的函数签名,使用参数传递的方式完成租户信息传递?答案是显而易见的,租户隔离是面向整套系统的解决方案,全局的方法签名修改意味着巨大的改造成本。
租户信息透传就意味着需要封装一个全局的上下文对象,这个对象中需要包含应用需要用到的所有租户相关的必要信息,举例可以将这个对象命名为Context。同时国际化技术方案也有着同样的透传变量需求,再加上国际化相关的租户变量,Context中的变量及其命名规范如下。

透传变量名命名规范

变量 \ 作用域cookieheader(请求头)应用内
租户标识ak_regionRegionregion
租户ID\Region-IdregionId
租户名\Region-NameregionName
语言ak_user_localeUser-Localelocale
时区ak_user_timezoneUser-Timezonetimezone
用户工号ak_user_staff_idUser-Staff-IdstaffId

要实现全链路的租户信息透传,就要解决各种各样的情形下,租户变量的传递问题。全链路租户信息透传,又分为应用内透传应用间透传

应用内透传

各自的应用有自己的上下文,也就是登陆态session,可以将租户的信息保存在应用上下文中。那么如果启用异步任务,其中需要上下文信息怎么办。
java应用中,参考trace类型应用的方案,采用ThreadLocal保存租户信息。为了防止异步线程丢失租户信息,我们封装了统一的线程池工具类,对jdk提供的原生线程池进行了封装,增加了租户信息透传特性。这里大致实现方式可以看我这一篇文章。

应用间透传

对于应用间的http接口调用,在client端,我们可以封装统一的http client,将Context中的内容放到请求的header中;在server端,我们提供了实现java-servlet-api标准的filter,实现从请求头中提取Context变量的功能。

对于各种消息中间件,如Kafka,RocketMQ等,也可以制定了租户变量透传的标准,以保证消息链路租户信息的完整。
流程

数据层租户隔离

由于我们采用的是上文所说的“共享应用程序、共享数据库”的多租户模型,数据层的租户隔离是整个租户隔离技术体系中最重要的一环。对于每种数据源,我们需要向其schema中增加一个租户标识符,并在对该数据源的所有读写中,增加租户标识符的过滤。
对于MySQL这种存储介质,我们需要向每张表和视图中添加一个租户ID字段,并重写每一个SQL查询,向原有的过滤条件中增加租户ID的过滤,限制每一行数据只能被其所属的租户访问到,以保证数据安全性。

MySQL存储方案:多租户Mybatis插件

Mybatis插件特点

Mybatis插件支持拦截所有提交到dao层的SQL,并支持对提交的SQL进行改写,原理类似于Spring的Interceptor。
多租户Mybatis插件可以在运行时,动态获取到应用上下文中的租户变量,在执行查询时自动将region_id的筛选附加到SELCT语句的WHERE条件中;在执行写操作时,自动将region_id插入到数据表中。
在这里插入图片描述

使用多租户Mybatis插件的优势
  • 使用Mybatis插件进行数据层租户隔离,可以大大降低业务代码在租户改造过程中的开发成本。
  • 下面是使用Mybatis插件进行数据层隔离,与硬编码方式实现隔离的成本对比
使用Mybatis插件硬编码
mapper文件无需改造所有SQL都需要改造,增加region_id字段
dao接口无需改造所有dao层接口都需要增加region_id参数
service层代码大部分不需要改造所有dao层接口调用,都需要显式传递region_id参数
参考文档

Mybatis插件文档

应用场景

  1. 云计算服务:云服务提供商可以使用多租户体系来向多个客户提供虚拟化资源,如虚拟机、存储和网络。每个客户都有自己的隔离环境,可以独立管理自己的资源,而不会影响其他客户。

  2. 软件即服务(SaaS):SaaS提供商可以使用多租户体系来向多个客户提供相同的应用程序和服务。每个客户都有自己的数据和配置,但共享相同的应用程序代码和基础设施。

  3. 共享经济平台:共享经济平台,如共享办公空间、共享汽车和共享住宿等,可以使用多租户体系来向多个用户提供服务。每个用户都有自己的账户和权限,但共享相同的物理资源。

  4. 物联网设备管理:在物联网环境中,许多设备需要连接到云平台进行管理和监控。使用多租户体系,可以将多个设备的数据隔离开来,确保数据安全和隐私。

  5. 在线游戏:在线游戏可以使用多租户体系来支持多个玩家同时玩游戏。每个玩家都有自己的角色和进度,但共享相同的游戏世界和资源。

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

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

相关文章

机器学习周刊第五期:一个离谱的数据可视化Python库、可交互式动画学概率统计、机器学习最全文档、快速部署机器学习应用的开源项目、Redis 之父的最新文章

date: 2024/01/08 这个网站用可视化的方式讲解概率和统计基础知识,很多内容还是可交互的,非常生动形象。 大家好,欢迎收看第五期机器学习周刊 本期介绍7个内容,涉及Python、概率统计、机器学习、大模型等,目录如下: 一个离谱的Python库看见概率,看见统计2024机器学习最…

U盘提示未格式化解决方法超级简单

U盘提示未格式化是常见故障,主要原因有文件系统损坏、固件问题、物理故障等。解决方法包括格式化U盘、更新固件、恢复数据等,具体操作需根据故障原因选择。如无法解决,建议联系专业维修人员处理。 U盘提示未格式化解决方法超级简单 当U盘提示…

使用scipy处理图片——滚动图片

大纲 常规模式constant和grid-constant 交换模式wrap和grid-wrap 镜像reflect、mirror和grid-mirror 最近值nearest 代码 在《使用numpy处理图片——滚动图片》一文中,我们介绍了numpy的roll方法,它只能让超出区域的元素回到被移动的区域中,如…

置位复位寄存器的某一位(多工位多工站渗透线控制应用)

这里的置位复位特定寄存器位功能块可以应用在渗透线控制应用上,我们可以根据小车当前所在位置,对相关工作槽里的标志位进行置位复位等操作(某个槽有产品,某个槽没有产品等)。 下面我们看下对应的控制要求 1、置位复位特定的位 2、置位复位寄存器中的某一位 3、置位bit8 4…

【书生·浦语】大模型实战营——第五次课程作业

基础作业——使用LMDeploy 以本地对话、网页Gradio、API服务中的一种方式部署InternLM-Chat-7B模型,生成300字的小故事 环境准备 除了安装所需依赖之后,重要的是进行模型转化(转换成TurboMind格式),这里需要注意转化命…

NFS概念与应用

一、NFS的概念 NFS(Network File System 网络文件服务) 文件系统(软件)文件的权限; NFS 是一种基于 TCP/IP 传输的网络文件系统协议,最初由 Sun 公司开发; 通过使用 NFS 协议,客户…

人工智能 | 生成式 AI 如何重塑开发流程和开发工具?

生成式 AI 如何重塑开发流程和开发工具? 生成式人工智能(Generative Artificial Intelligence,GAI)是一种基于大规模数据训练学习,从而生成新的原创内容的人工智能。生成式人工智能可以生成各种形式的数据&#xff0c…

鸿蒙开发工程师会不会有很好的就业前景?

一,鸿蒙带动IT开发和应用整体结构的变革 1月11日,以鸿蒙为首的华为概念股大幅走强,创业板创识科技拉升封板,传智教育、智度股份、高新发展、立达信、吉大正元等多股涨停,华亚电子、九联科技、软通动力、辰奕智能、芯海…

挂载mount、卸载umount,和rpm安装包

1.创建一个挂载目录dvd 2.把dev/cdrom 挂载到dvd 3.查看 4.挂载的格式 卸载挂载点 dvd 重新挂载到nsd30 rpm安装包的安装位置 可执行命令:一般安装到/usr/bin下 服务器程序,管理工具:一般安装到sbin下 配置文件:一般安装到etc下…

内存四区图练习

带着白卡去旅行 绘制图中三种情况的内存四区图 一个实参 一个形参 取地址 通过指针修改变量 返回 多级指针的训练 #define _CRT_SECURE_NO_WARNINGS #include<stdlib.h> #include<stdio.h> #include<string.h> #include<math.h>int getMem(char***p3,…

从零学Java 线程安全的集合

线程安全的集合 文章目录 线程安全的集合1 List 和 Set体系Collections中的工具方法1.1 CopyOnWriteArrayList1.2 CopyOnWriteArraySet1.3 ConcurrentHashMap 2 CAS算法3 Queue接口&#xff08;队列&#xff09;3.1 ConcurrentLinkedQueue3.2 BlockingQueue接口&#xff08;阻塞…

PyTorch Tutorial 2.0

这里是对于PyTorch Tutorial-CSDN博客的补充&#xff0c;但是与其相关的NLP内容无关&#xff0c;只是一些基础的PyTorch用法的记录&#xff0c;主要目的是能够自己生成一些模拟的数据集。先介绍随机数的目的是因为based on随机数方法。 当然在看随机数的方法的时候&#xff0c…

【开源】基于JAVA语言的固始鹅块销售系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 鹅块类型模块2.3 固始鹅块模块2.4 鹅块订单模块2.5 评论管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 鹅块类型表3.2.2 鹅块表3.2.3 鹅块订单表3.2.4 鹅块评论表 四、系统展示五、核心代码5.…

解决com.alibaba.fastjson.JSONException: default constructor not found的问题

1.问题描述 在进行JSON和对象互转时&#xff0c;发现有个报错&#xff1a; com.alibaba.fastjson.JSONException: default constructor not found. class com.hellobike.ph.match.service.taxi.model.message.DelayAddSkuMsg 2.原因和解决方案 通过其提示可以看出在利用fastJ…

Vue组件之间的通信方式都有哪些?

面试官&#xff1a;Vue组件之间的通信方式都有哪些&#xff1f; 一、组件间通信的概念 开始之前&#xff0c;我们把组件间通信这个词进行拆分 组件通信 都知道组件是vue最强大的功能之一&#xff0c;vue中每一个.vue我们都可以视之为一个组件通信指的是发送者通过某种媒体以…

详解toLowerCase(判断字符串相等)

一、toLowerCase 函数简介 toLowerCase() 是一个在多个编程语言中都存在的字符串方法&#xff0c;它的作用是将字符串中的所有大写字母转换为对应的小写字母。 常用于文本处理、搜索和比较等情况&#xff0c;以确保字符串的一致性和非大小写敏感的操作。 二、判断字符串相等 下…

Vim一键配置指南,打造高效率C++开发环境

文章目录 前言安装与卸载功能演示gcc/g升级问题 前言 Vim作为当下最受欢迎的文本编译器之一&#xff0c;不仅具有强大的文本编辑功能&#xff0c;还提供了高度的可定制性。用户可以根据自己的喜好自定义配置&#xff0c;并且通过自己编写插件或者使用现有的插件来扩展Vim的功能…

【STM32】STM32学习笔记-I2C通信外设(34)

00. 目录 文章目录 00. 目录01. I2C简介02. I2C外设简介03. I2C框图04. I2C基本结构05. 主机发送06. 主机接收07. 软件/硬件波形对比08. 预留09. 附录 01. I2C简介 I2C(Inter&#xff0d;Integrated Circuit)总线是一种由NXP&#xff08;原PHILIPS&#xff09;公司开发的两线式…

黑马程序员——javase基础——day02——运算符选择语句

目录&#xff1a; 运算符 算术运算符案例数值拆分操作的三种情况 数字相加(类型转换)字符相加字符串相加赋值运算符选择语句 顺序结构Debug的基本使用选择语句之if if语句格式1if语句格式2和格式3案例1(交通信号灯)关系运算符案例2(奇偶数)案例3(手机以旧换新)案例4(你是青年人…

OpenCV-Python(42):摄像机标定

目标 学习摄像机畸变以及摄像机的内部参数和外部参数根据摄像机相关参数对畸变图像进行修复 基础说明 今天的低价单孔摄像机(照相机)会给图像带来很多畸变。畸变主要有两种:径向畸变和切向畸变。如下图所示用红色直线将棋盘的两个边标注出来&#xff0c;但是你会发现棋盘的边…