互联网厂商在进行公有云saas服务部署的时候,往往会面对多租户的场 景,多租户场景的设计,在架构上一般分为二个层次:
1、计算集群的多租户
计算集群的多租户顾名思义,就是针对不同的租户提供单独的服务集群,不同租户之间cpu相互独立,个别租户的流量洪峰不会影响其它租户正常服务。
计算集群的多租户实现有很多种方案,一般做法是,根据不同租户辟出独立集群,对外暴露统一的CDN域名或BGP域名,经流量调度到计算中心内部,由Nginx集群或网关根据URL中的租户参数,向租户所在集群转发请求。
由于多租户的场景的普遍性,最近几年新涌现的新框架和平台在设计伊始就考虑了多租户的场景,并集成了相关技术,比如k8的namespace,结合calico,可以实现非常灵活的多租户计算分组。
2、数据层的多租户
拿Mysql来举例,数据层的多租户一般有三种做法:
1)一个租户一个数据库
2)一个租户一个schema
3)多个租户同一个schema
其中区别,可以查询相关资料,网上很多,一般Saas服务从成本等因素考虑,大多数采用第3种方案,其主要标志为在元数据设计中,有多租户字段tenant_id。
3、多租户的数据层访问拦截
这里以java+mybatis来说明。
Mybatis plugin插件支持拦截所有提交到Dao层的SQL,并支持对提交的SQL进行改写,原理类似于Spring的Interceptor,多租户Mybatis插件能够在运行时,动态获取到应用上下文中的租户变量,在执行CRUD操作时,自动将多租户字段(tenant_id)条件附加到sql语句之中,如where条件之后。
1)使用mybatis插件的优点
使用mybatis插件,可以大大简化业务代码在多租户改造过程中的开发成本,对比硬编码方式,mapper和dao层无须改造,service层的代码改造量也可以大大降低。
2)原理
通过mybatis plugin拦截sql处理步骤和result处理步骤。sql预处理,自动增加tenant_id相关语句,result预处理,验证数据tenant_id字段符合要求。包括:
- insert into,增加写入tenant_id数据
- select 自动在where之后附加条件tenant_id = ?
- update 在where之后附加条件tenant_id
- delete 在where之后附加条件tenant_id
- left join
- 子查询
等等。
3)实现方式
插件用的是责任链模式,由每一个对象对其下家的引用而连接起来形成一条链,请求在这个链上传递,直到链上的某一个对象决定处理此请求。
具体实现方式为,实现接口Interceptor.java 的3个方法,分别是plugin、intercept和setProperties。
- intercept:它将直接覆盖你所拦截的对象,有个参数Invocation对象,通过该对象,可以反射调度原来对象的方法;
- plugin:target是被拦截的对象,它的作用是给被拦截对象生成一个代理对象;
- setProperties:允许在plugin元素中配置所需参数,该方法在插件初始化的时候会被调用一次
实现类需要添加几个注解,来拦截对应的签名:
然后实现Interceptor接口的方法即可,通过Plugin工具类方便生成代理类,通过MetaObject工具类方便操作四大对象的属性,修改对应的值。
然后实现Plugin方法,生成代理类的方法是通过MyBatis提供的Plugin工具类,它实现了InvocationHandler接口(JDK动态代理的接口),看看它的2个方法:
Plugin提供了静态方法wrap方法,它会根据插件的签名配置,使用JDK动态代理的方法,生成一个代理类,当四大对象执行方法时,会调用Plugin的invoke方法,如果方法包含在声明的签名里,就会调用自定义插件的intercept方法,传入Invocation对象。
最后,插件的初始化时在MyBatis初始化的时候完成的,读入插件节点和配置的参数,使用反射技术生成插件实例,然后调用插件方法中的setProperties方法设置参数,并将插件实例保存到配置对象中.
4、最后分享一下很不错的mybatis多租户插件,当然,你也可以自己实现
https://github.com/Mearalu/mybatis-multi-tenancy
关注小编,小编会每天为你分享有趣的技术文章哦。 偷偷告诉你私信小编“学习”会有意想不到的惊喜哟~~!