织梦网站手机端,网站建设界面ppt演示,福州网站建设哪里有,广州做网站哪个好前言
别误会#xff0c;本篇【并不是】 要用mybatis-plus自身的多租户方案#xff1a;在表中加一个tenant_id字段来区分不同的租户数据。并不是的#xff01; 而是在假设业务系统已经使用mybatis-plus多数据源的前提下#xff0c;如何实现业务数据库隔开的多租户系统。 这…前言
别误会本篇【并不是】 要用mybatis-plus自身的多租户方案在表中加一个tenant_id字段来区分不同的租户数据。并不是的 而是在假设业务系统已经使用mybatis-plus多数据源的前提下如何实现业务数据库隔开的多租户系统。 这里面有点绕多数据源可以是一个系统本身的功能需求假设当前系统算做是个单租户它使用了两个数据库: master1和sys1那么做多租户改造后假设现在有了2个租户那么就要添加2个数据库master2和sys2 , 总共就是四个数据库数据源了… 咱们这里简单化处理假设一个业务系统只使用一个数据库。
大纲
在本篇我们可以
看到mybatis-plus底层多数据源的实现原理在不破坏多数据源的前提下实现多租户功能spring security结合jwt记录租户信息
代码版本
springboot: 2.7.0
dynamic-datasource-spring-boot-starter: 4.3.0
io.jsonwebtoken: 0.12.3回顾mybatis-plus多数据源使用
1.yaml配置: 2.serviceImpl: 或者使用切面动态设置crud对应的数据源。
改造需求
不要把所有租户信息都直接放在yaml等配置文件中可动态的添加删除数据源用户登录成功后把租户信息封装到jwt token中后续业务访问提取中租户信息动态切换数据源访问
方案
租户本身的信息可放在resources/tenants目录下一个租户使用一个单独的配置文件或者通过读取另外的数据库获取。本篇先使用前者。 修改某个租户的配置文件内容/数据库重启服务/通过controller接口触发数据源的变更。
正篇开始 yaml配置文件中只保留一个主数据库如上yaml截图所示。 另外的2个租户配置放classpath下tenant目录如下所示 新建多数据源配置类内容如下
Configuration(proxyBeanMethods false)
Slf4j
public class MultiDataSourceConfig {Resourceprivate DruidDataSourceCreator druidDataSourceCreator;Resourceprivate DynamicRoutingDataSource dataSource;PostConstructpublic void init() {ResourcePatternResolver resolver new PathMatchingResourcePatternResolver();try {org.springframework.core.io.Resource[] resources resolver.getResources(classpath:tenants/*.properties);for (org.springframework.core.io.Resource resource : resources) {Properties tenantProperties new Properties();tenantProperties.load(resource.getInputStream());String tenantId tenantProperties.getProperty(name);DataSourceProperty dataSourceProperty new DataSourceProperty();dataSourceProperty.setPoolName(tenantId);dataSourceProperty.setUrl(tenantProperties.getProperty(datasource.url));dataSourceProperty.setUsername(tenantProperties.getProperty(datasource.username));dataSourceProperty.setPassword(tenantProperties.getProperty(datasource.password));dataSourceProperty.setDriverClassName(tenantProperties.getProperty(datasource.driver-class-name));dataSource.addDataSource(tenantId, druidDataSourceCreator.createDataSource(dataSourceProperty));}} catch (IOException exp) {throw new RuntimeException(Problem in tenant datasource: exp);}}
}上述代码读取classpath:tenants/目录下的所有.properties配置文件内容组装并添加数据源。
登录
登录操作时查询此登录用户对应的租户信息并在生成jwt token时把租户信息也封装进去通过http响应头返回给用户。如下所示
public static void addToken(HttpServletResponse res, String username, String tenant) {String JwtToken Jwts.builder().subject(username).audience().add(tenant).and().issuedAt(new Date(System.currentTimeMillis())).expiration(new Date(System.currentTimeMillis() EXPIRATIONTIME)).signWith(SIGNINGKEY).compact();res.addHeader(Authorization, PREFIX JwtToken);}业务操作
在一个拦截器类中token校验并从中提取出租户信息如下所示
public static String getTenant(HttpServletRequest req) {String token req.getHeader(Authorization);if (token null) {return null;}String tenant Jwts.parser().setSigningKey(SIGNINGKEY).build().parseClaimsJws(token.replace(PREFIX, ).trim()).getBody().getAudience().iterator().next();return tenant;}动态切换数据源
得到一个请求所属租户信息后要访问数据库时切换源记住这里 记住它DynamicDataSourceContextHolder DynamicDataSourceContextHolder.push(tenant);try {chain.doFilter(request, response);} finally {DynamicDataSourceContextHolder.clear();}完工是的使用层面上就结束了。接下来是原理分析。
剖析
1.有个类名叫AbstractRoutingDataSourcemybatis-plus和spring-jdbc都有叫这个名的类 并且它们都继承了AbstractDataSource类但是这个父类也只是同名而已。但是它们的功能都说得很清楚抽象动态获取数据源它们都有个抽象方法抽象获取连接池如下所示 spring-jdbc下的源码 然后看看mybatis-plus的抽象方法实现 所以回顾我们业务代码的写法DynamicDataSourceContextHolder.push(tenant); 正是我们把当前请求对应的tenant作为数据源key 压栈了后面切换数据源时依据它去得到数据源。那么还记得这个租户key 是在哪里和数据源对应上的吗 正是在正篇开头的新建多数据源配置类中
dataSource.addDataSource(tenantId, druidDataSourceCreator.createDataSource(dataSourceProperty));/*** 添加数据源** param ds 数据源名称* param dataSource 数据源*/public synchronized void addDataSource(String ds, DataSource dataSource) {DataSource oldDataSource dataSourceMap.put(ds, dataSource);// 新数据源添加到分组this.addGroupDataSource(ds, dataSource);// 关闭老的数据源if (oldDataSource ! null) {closeDataSource(ds, oldDataSource, graceDestroy);}log.info(dynamic-datasource - add a datasource named [{}] success, ds);}如此就打通了流程。如果大家感兴趣可以看到com.baomidou.dynamic.datasource.DynamicRoutingDataSource类中有一些方法删除数据源还有数据源分组功能这可以用于主主(从如果业务场景都是只读的话)策略是轮询和随机