# saas-tenant-router-starter **Repository Path**: DTSEDeveloper_admin/saas-tenant-router-starter ## Basic Information - **Project Name**: saas-tenant-router-starter - **Description**: SaaS:支持多种存储类型组件动态路由,如RDS、Redis、MQ、ES等; 支持不同存储组件动态加载,即根据后端资源动态开启或者关闭; 支持租户访问零信任安全策略,对访问请求多重校验。 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master-dev - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 28 - **Created**: 2022-06-21 - **Last Updated**: 2022-06-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # saas-tenant-router-starter ## 背景 支持多种存储类型组件动态路由,如RDS、Redis、MQ、ES等; 支持不同存储组件动态加载,即根据后端资源动态开启或者关闭; 支持租户访问零信任安全策略,对访问请求多重校验。 ## 概览图 ![租户路由原理图](image/租户路由概览图.png) ## 原理图 ![租户路由原理](image/租户路由原理图.png) ## 租户信息传递 ### Filter过滤器 过滤请求,从request的header中获取租户标识,所以我们在请求时应该将租户标识放在header中。 将获取的租户标识保存在HystrixRequestVariableDefault变量中,并使它在请求结束后移除。 ### HystrixRequestVariableDefault HystrixRequestVariableDefault可以让我们在跨服务调用时共享租户标识,以使得路由插件能更好地契合应用。 ## 配置结构 ``` spring: datasource: dynamic: enable: true #开关 active-pool-name: druid # 全局指定使用的连接池名称 default-source: multitenant1 # 默认的数据源组名称 pools: # 全局连接池配置,可配置等多种连接池 druid: # 连接池名称,与相关池策略中PoolStrategy.getPoolName()对应 name: globalDruidConfig initialSize: 10 maxActive: 20 minIdle: 30 c3p0: # 连接池名称,与相关池策略中PoolStrategy.getPoolName()对应 name: globalDruidConfig data-source-map: multitenant1: #数据源组名,其下包含一主多从库配置 load-balance-strategy: random # 指定负载均衡策略 active-pool-name: druid # 组指定使用的连接池名称 pools: # 组内连接池配置,可配置等多种连接池,未配置的属性使用全局配置覆盖 druid: name: dataGroup initialSize: 2 maxActive: 3 minIdle: 1 master: # 主库,目前支持单主模式 url: jdbc:mysql://1.1.1.1:3306/housekeeping?useUnicode=true&characterEncoding=utf-8 active-pool-name: druid # 库指定使用的连接池名称 username: xxx password: xxx driver-class-name: com.mysql.cj.jdbc.Driver pools: # 库级别连接池配置,未配置的使用组级别配置覆盖,组级别未配置的使用全局配置覆盖 druid: name: master slave: # 从库,目前支持多从模式 - url: jdbc:mysql://1.1.1.2:3306/housekeeping?useUnicode=true&characterEncoding=utf-8 # 未配置的数据库连接属性,如用户名,密码,driver,未配置时,使用同组内主库配置 pools: # 库级别连接池配置,未配置的使用组级别配置覆盖,组级别未配置的使用全局配置覆盖 druid: name: slave - url: jdbc:mysql://1.1.1.2:3306/housekeeping?useUnicode=true&characterEncoding=utf-8 active-pool-name: c3p0 # 库级别,指定连接池类型,选择路径pools下的连接池名,与相关池策略中PoolStrategy.getPoolName()对应 pools: druid: name: slave binding-map: # 租户绑定信息 0bc92b8e542a4ba9ab35b1de8b1794eb: # 租户标识 schema: tenant_0bc92b8e542a4ba9ab35b1de8b1794eb # 指定租户绑定schema, 未配置则采用SchemaAdapter所使用策略,未自行扩展时,使用DefaultSchemaAdapter默认选取 group-name: multitenant1 # 指定数据源组名,选择 data-source-map 配置的组,未指定时,采用DataSourceAdapter所使用策略,未自行扩展时,使用DefaultDataSourceAdapter默认选取 ``` 您的场景如果有使用到cloud-config配置租户信息,只需在信息动态变更时使用bus总线通知即可 ## 如何使用 ### 1.装配路由插件 ``` 目前暂时未发布稳定版本,暂时需要下载源码包使用,发布稳定版本后更新依赖。 ``` ### 2.开启插件 ``` spring: datasource: dynamic: enable: true #开关 ``` ### 3.配置数据源组 ``` spring: datasource: dynamic: enable: true #开关 data-source-map: multitenant1: #数据源组名,其下包含一主多从库配置 master: # 主库,目前支持单主模式 url: jdbc:mysql://1.1.1.1:3306/housekeeping?useUnicode=true&characterEncoding=utf-8 username: xxx password: xxx driver-class-name: com.mysql.cj.jdbc.Driver slave: # 从库,目前支持多从模式 - url: jdbc:mysql://1.1.1.2:3306/housekeeping?useUnicode=true&characterEncoding=utf-8 # 未配置的数据库连接属性,如用户名,密码,driver,未配置时,使用同组内主库配置 - url: jdbc:mysql://1.1.1.3:3306/housekeeping?useUnicode=true&characterEncoding=utf-8 ``` 需要在data-source-map路径之下配置数据源组,key=数据源组名称, 此名称不可重复。组内可配置一主多从,主库路径为master,从库路径为slave,可配多个从库。主库或者从库可配置数据库连接信息如连接地址url,用户名username,密码password,驱动driver-class-name。当从库未配置username,password,driver-class-name时,会默认使用同组中主库的对应配置。url必须配置,为防止误配,此项未作为缺省配置。 ### 4.配置连接池 ``` spring: datasource: dynamic: pools: # 全局连接池配置,可配置等多种连接池 druid: # 连接池名称,与相关池策略中PoolStrategy.getPoolName()对应 name: globalDruidConfig initialSize: 10 maxActive: 20 minIdle: 30 c3p0: # 连接池名称,与相关池策略中PoolStrategy.getPoolName()对应 name: globalDruidConfig data-source-map: multitenant1: #数据源组名,其下包含一主多从库配置 pools: # 组内连接池配置,可配置等多种连接池,未配置的属性使用全局配置覆盖 druid: name: dataGroup initialSize: 2 maxActive: 3 minIdle: 1 master: # 主库,目前支持单主模式 pools: # 库级别连接池配置,未配置的使用组级别配置覆盖,组级别未配置的使用全局配置覆盖 druid: name: master slave: # 从库,目前支持多从模式 - url: jdbc:mysql://1.1.1.2:3306/housekeeping?useUnicode=true&characterEncoding=utf-8 # 未配置的数据库连接属性,如用户名,密码,driver,未配置时,使用同组内主库配置 pools: # 库级别连接池配置,未配置的使用组级别配置覆盖,组级别未配置的使用全局配置覆盖 druid: name: slave ``` 在配置中的多个层级中都支持配置连接池属性,配置路径如下: 路径spring.datasource.dynamic.pools下的连接池配置为全局配置,会被更细粒度的配置覆盖。 路径spring.datasource.dynamic.data-source-map.*.pools下的配置为数据源组级别的配置,路径中的 * 代表数据源组名称, 需要使用全小写, 名称中 - 将不会转换为首字母大写。 路径spring.datasource.dynamic.data-source-map.*.master.pools下的配置为主库级别的配置 路径spring.datasource.dynamic.data-source-map.*.slave.pools下的配置为从库级别的配置 当主库或者从库有属性未配置时,此属性将会使用其所属数据源组的同名称的连接池的配置,所属数据源组仍未配置时,此属性继续向上使用全局连接池配置,仍未找到,则为空。 pools之下配置的key为连接池名称,或可以称为类型,从下至上未配置属性覆盖时,按照连接池名称查找。此名称对应代码中PoolStrategy.getPoolName()获取的值。目前已实现druid连接池,每一个连接池类型需要新增两个java类,均属于PoolStrategy的子类。 用于承载连接池属性的实体需要实现 JdbcPool接口,此接口继承PoolStrategy。抽象类AbstractJdbcPool实现JdbcPool接口,实现了其中的merge() 方法,此方法实现逻辑用于连接池属性覆盖,如果AbstractJdbcPool的实现无法满足要求,可以自行重写。 用于实现数据源创建功能的类需要实现DataSourceCreator接口,此接口继承PoolStrategy。目前已有druid的实现,可参考。 如果有需要扩展自定义连接池,需要做到三点:1.新增承载属性配置的实体类实现JdbcPool接口。2.新增数据源创建业务类实现DataSourceCreator接口。3.以上两个实现的getPoolName() 返回的值与pools之下的某个key一致。 主库和从库之间的连接池配置没有缺省继承的逻辑。 ### 5.指定连接池 ``` spring: datasource: dynamic: enable: true #开关 active-pool-name: druid # 全局指定使用的连接池名称 data-source-map: multitenant1: #数据源组名,其下包含一主多从库配置 active-pool-name: druid # 组指定使用的连接池名称 master: # 主库,目前支持单主模式 url: jdbc:mysql://1.1.1.1:3306/housekeeping?useUnicode=true&characterEncoding=utf-8 active-pool-name: druid # 库指定使用的连接池名称 slave: # 从库,目前支持多从模式 - url: jdbc:mysql://1.1.1.2:3306/housekeeping?useUnicode=true&characterEncoding=utf-8 active-pool-name: c3p0 # 库级别,指定连接池类型,选择路径pools下的连接池名,与相关池策略中PoolStrategy.getPoolName()对应 ``` 指定连接池的属性与连接池配置类似, spring.datasource.dynamic.active-pool-name 为全局配置 spring.datasource.dynamic.data-source-map.*.active-pool-name 为数据源组级别配置 spring.datasource.dynamic.data-source-map.*.master.active-pool-name 为主库配置 spring.datasource.dynamic.data-source-map.*.slave.active-pool-name 为从库配置 当主库或者从库未配置时,使用所属数据源组的组级别配置,组未配置时,使用全局配置,如果全局配置仍未配置,则默认使用druid连接池。 ### 6.指定负载均衡策略 ``` spring: datasource: dynamic: data-source-map: multitenant1: #数据源组名,其下包含一主多从库配置 load-balance-strategy: random # 指定负载均衡策略 ``` 负载均衡策略配置路径spring.datasource.dynamic.data-source-map.*.load-balance-strategy, 各组单独配置,如未配置,则默认使用随机算法进行负载均衡。 目前只实现了随机算法,如有需要扩展,可以实现LoadBalanceStrategy接口扩展,算法类型为LoadBalanceStrategy.getStrategyType(), 如果使用扩展算法,必须在配置中显示指定算法类型,否则使用随机算法。 ### 7.租户绑定数据源 ``` spring: datasource: dynamic: default-source: multiTenant1 # 默认的数据源组名称 binding-map: # 租户绑定信息 0bc92b8e542a4ba9ab35b1de8b1794eb: # 租户标识 group-name: multitenant1 # 指定数据源组名,选择 data-source-map 配置的组,未指定时,采用DataSourceAdapter所使用策略,未自行扩展时,使用DefaultDataSourceAdapter默认选取 ``` 绑定路径为spring.datasource.dynamic.binding-map.*.group-name 指定数据源组名称, * 为租户标识。当某个租户标识未在此绑定数据源时,将会优先使用DataSourceAdapter的自定义扩展实现,如果未添加自定义扩展,将会采用默认的数据源适配器DefaultDataSourceAdapter获取数据源,获取逻辑为优先使用spring.datasource.dynamic.default-source 指定的数据源组,如果default-source未配置,则判断已配置的数据源组列表中是否只有一组数据源,是则选择这一个组作为数据源使用,否则获取的数据源为空,选择数据源失败。 ### 8.租户绑定schema ``` spring: datasource: dynamic: enable: true #开关 binding-map: # 租户绑定信息 0bc92b8e542a4ba9ab35b1de8b1794eb: # 租户标识 schema: tenant_0bc92b8e542a4ba9ab35b1de8b1794eb # 指定租户绑定schema, 未配置则采用SchemaAdapter所使用策略,未自行扩展时,使用DefaultSchemaAdapter默认选取 ``` 此配置仅针对使用schema隔离的场景使用,绑定路径为spring.datasource.dynamic.binding-map.*.schema 指定的schema, *为租户标识,当某个租户标识未在此绑定schema时,将会使用SchemaAdapter的自定义扩展实现来获取租户对应的schema,如果未自定义扩展,则使用DefaultSchemaAdapter来获取schema,当获取的schema为空时,使用连接字符串中默认的schema。 自定义扩展中,可以使用按既定规则拼接,数据库表存储,缓存或者其他任意方式根据租户标识获取。