SaaS(Software-as-a-Service,软件即服务)是一种基于云计算的软件交付模式,用户可以通过互联网按需使用软件应用,多租户系统它是一种软件架构模式。
传统模式与多租户模式系统架构的区别:
第一、多租户数据存储方式
多租户数据存储在数据库中时,有几种不同的方案可供选择,这些方案具有不同的特点和适用场景。
第二、实现多租户动态添加租户库以及访问库问题
问题1、租户如何动态切换访问数据库
问题2、如何动态添加租户数据库
问题1:针对不同租户如何动态切换数据库的问题,可以参考如下解决方案:
问题2:针对如何动态添加租户数据库的问题,可以参考如下解决方案进行处理:
动态添加数据库,可以使用mybatis-plus框架来实现,官网提供了两种方式实现多数据库切换的框架,随着租户的体量越来越大,需要延伸多数据源的扩展,官方目前提供了两种框架来实现,具体如下介绍(如下摘自官网):
这里使用dynamic-datasource框架,具体实现过程可参考下一节。
项目实现采用独立数据库模式实现多租户,即一个租户一个数据库,技术栈采用springboot2.7.6集成mybatis-plus和dynamic-datasoruce框架来实现,数据库采用mysql进行数据存储。
第一步、准备租户数据表
CREATE TABLE `sys_tenant` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`tenant_name` varchar(255) DEFAULT NULL COMMENT '租户名称',
`datasource_schema` varchar(500) DEFAULT NULL COMMENT 'schema',
`datasource_url` varchar(500) DEFAULT NULL COMMENT '数据库url,添加后不允许修改',
`datasource_username` varchar(255) DEFAULT NULL COMMENT '数据库用户名',
`datasource_password` varchar(255) DEFAULT NULL COMMENT '数据库密码',
`datasource_driver` varchar(255) DEFAULT NULL COMMENT '数据库驱动,默认为mysql驱动',
`status` tinyint(1) DEFAULT NULL COMMENT '租户状态,1-正常,2-停用',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='租户';
第二步、系统搭建
步骤一:添加依赖
在你的项目中引入 dynamic-datasource 和 mybatis-plus 的相关依赖。可以在 pom.xml 文件中添加以下依赖:
com.alibaba
druid-spring-boot-starter
1.2.11
com.baomidou
mybatis-plus-boot-starter
3.3.1
com.baomidou
dynamic-datasource-spring-boot-starter
3.6.1
步骤二:配置数据源
在 application.yml 或者 application.properties 文件中配置你的数据源。例如:
spring:
autoconfigure:
# 排除原有的连接池
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
datasource:
type: com.alibaba.druid.pool.DruidDataSource
# 使用dynamicDatasource框架
dynamic:
#严格匹配数据源,默认false,true为匹配到指定数据源时抛出异常,false使用默认数据源
strict: true
datasource:
master:
url: jdbc:mysql://localhost:3306/crazy_saas?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
步骤三:动态创建多租户数据库
动态创建租户数据库,可以定义一个接口实现。例如:
package net.crazychina.app.controller;
@RestController
@Api(tags = "租户管理中心")
public class TenantController {
@Resource
private MultiTenantService multiTenantService;
@Resource
private DataSource dataSource;
@PostMapping("addTenant")
@ApiOperation("添加租户库")
public Result initTenantDb(@RequestBody DataSourceDTO dto) {
if (multiTenantService.getSingleTenant(dto.getDatasourceSchema()) > 0) {
return Result.error("数据源已经存在,无需重复创建!");
}
int result = multiTenantService.insertTenant(dto);
if (result > 0) {
tenantDatasource(dto);
}
return Result.success("新增租户数据源成功");
}
// 动态添加数据源
private void tenantDatasource(DataSourceDTO dto) {
DruidDataSource tmpdb = new DruidDataSource();
tmpdb.setUrl(dto.getDatasourceUrl());
tmpdb.setUsername(dto.getDatasourceUsername());
tmpdb.setPassword(dto.getDatasourcePassword());
tmpdb.setDriverClassName(dto.getDatasourceDriver());
DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
try {
ds.addDataSource(dto.getDatasourceSchema(), tmpdb);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Data
public class DataSourceDTO implements Serializable {
@NotBlank
private String tenantName;
@NotBlank
private String datasourceUrl;
@NotBlank
private String datasourceDriver;
@NotBlank
private String datasourceSchema;
@NotBlank
private String datasourceUsername;
@NotBlank
private String datasourcePassword;
}
步骤四:动态切换数据库
定义拦截器,对前端传过来的租户信息进行拦截处理,进而进行数据库的动态切换。可以如下参考:
动态切换租户库核心代码
DynamicDataSourceContextHolder.push("数据源名称"); //动态切换租户库代码
@Slf4j
@Configuration
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
@Resource
private TenantInterceptor tenantInterceptor;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.tenantInterceptor);
}
}
@Slf4j
@Component
public class TenantInterceptor implements HandlerInterceptor {
final static ThreadLocal threadLocal=new ThreadLocal<>();
/**
* 在请求处理前调用
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(!(handler instanceof HandlerMethod)){
// 不是http request请求直接放行
return true;
}
String tenantCode = request.getHeader("tenantCode");
//如果tenantCode为空,则使用默认数据源
if (StringUtils.isNotEmpty(tenantCode)){
DynamicDataSourceContextHolder.push(tenantCode);
threadLocal.set(true);
}
return true;
}
/**
* 在整个请求结束之后被调用
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 方法执行完毕或者执行异常后,移除数据源
if (null != threadLocal.get() && threadLocal.get()) {
DynamicDataSourceContextHolder.clear();
}
threadLocal.remove();
}
}
如果你也有这方面的需求,实现一个Saas多租户系统,又不想使用比较重的分库分表插件,可以采用Mybatis-Plus集成Dynamic-datasource插件来实现多租户系统,该插件满足大部分Saas场景功能实现,可以满足不同的租户创建独立的数据库,也支持跨库查询、分布式事务等功能,是一个实现Saas应用不错的选择。
页面更新:2024-05-22
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号