我正在开发一个多租户反应式应用程序,使用带有r2dbc驱动程序的Spring-Webflow Spring-data-r2dbc连接到Postgresql数据库。多租户部分是基于模式的:每个租户一个模式。因此,根据上下文(例如登录的用户),请求将访问数据库的特定模式。
我正在努力研究如何在r2dbc中实现这一点。理想情况下,这将是Hibernate处理MultiTenantConnectionProvider的方式(参见示例16.3)。
到目前为止,我发现了什么,我做了什么:
>
我查看了PostgresqlConnectionFactory。这里有趣的是,在准备连接
上有一个对setSchema(连接)
的调用:
private Mono<Void> setSchema(PostgresqlConnection connection) {
if (this.configuration.getSchema() == null) {
return Mono.empty();
}
return connection.createStatement(String.format("SET SCHEMA '%s'", this.configuration.getSchema()))
.execute()
.then();
}
我可能需要找到一种方法来覆盖它,以便从上下文而不是从配置中动态获取模式?
>
否则,我可以尝试将请求中的架构指定为表前缀:
String s = "tenant-1";
databaseClient.execute("SELECT * FROM \"" + s + "\".\"city\"")
.as(City.class)
.fetch()
.all()
但是我不能再使用SpringData了,或者我需要重写每个请求,以将租户作为参数传递。
任何提示/帮助赞赏:)
我为r2dbc创建了一个多租户示例,但使用了每个数据库策略。
在此处检查完整的示例代码。
在某些数据库中,模式和数据库概念是等效的。如果您坚持使用每模式策略,请在获取连接时添加SQL以选择模式(请研究您正在使用的数据库,并确定设置模式的正确子句)。
谢谢你的回答。我最终得到了这个解决方案:
按租户/架构构建ConnectionFactory:
public class CloudSpringUtilsConnectionFactoryBuilder implements ConnectionFactoryBuilder {
@Override
public ConnectionFactory buildConnectionFactory(String schema) {
PostgresqlConnectionConfiguration configuration = getPostgresqlConnectionConfigurationBuilder(schema)
.build();
return new PostgresqlConnectionFactory(configuration);
}
@Override
public ConnectionFactory buildSimpleConnectionFactory() {
PostgresqlConnectionConfiguration configuration = getPostgresqlConnectionConfigurationBuilder(null)
.build();
return new PostgresqlConnectionFactory(configuration);
}
protected PostgresqlConnectionConfiguration.Builder getPostgresqlConnectionConfigurationBuilder(String schema) {
return PostgresqlConnectionConfiguration
.builder()
.username(dbUser)
.password(dbPassword)
.host(dbHost)
.port(dbPort)
.database(dbName)
.schema(schema);
}
创建一个TenantTroutingConnectionFactory,以根据租户获得正确的ConnectionFactory。在我们的示例中,租户是从身份验证主体(将令牌转换为用户配置文件)提取的:
public class TenantRoutingConnectionFactory extends AbstractRoutingConnectionFactory {
private final DatabaseMigrationService databaseMigrationService;
private final ConnectionFactoryBuilder connectionFactoryBuilder;
private final Map<String, ConnectionFactory> targetConnectionFactories = new ConcurrentHashMap<>();
@PostConstruct
private void init() {
setLenientFallback(false);
setTargetConnectionFactories(new HashMap<>());
setDefaultTargetConnectionFactory(connectionFactoryBuilder.buildConnectionFactory());
}
@Override
protected Mono<Object> determineCurrentLookupKey() {
return ReactiveSecurityContextHolder.getContext()
.map(this::getTenantFromContext)
.flatMap(tenant -> databaseMigrationService.migrateTenantIfNeeded(tenant)
.thenReturn(tenant));
}
private String getTenantFromContext(SecurityContext securityContext) {
String tenant = null;
Object principal = securityContext.getAuthentication().getPrincipal();
if (principal instanceof UserProfile) {
UserProfile userProfile = (UserProfile) principal;
tenant = userProfile.getTenant();
}
...
log.debug("Tenant resolved: " + tenant);
return tenant;
}
@Override
protected Mono<ConnectionFactory> determineTargetConnectionFactory() {
return determineCurrentLookupKey().map(k -> {
String key = (String) k;
if (!targetConnectionFactories.containsKey(key)) {
targetConnectionFactories.put(key, connectionFactoryBuilder.buildConnectionFactory(key));
}
return targetConnectionFactories.get(key);
});
}
请注意,我们在DatabaseMigrationService中使用Flyway为我们获得的每个租户创建和迁移模式。
我也遇到了这个。
以下是我目前正在做的事情:
>
将PostgresqlConnectionConfigurationBuilder和PostgresqlConnectionFactory发布为Bean:
@Bean
public PostgresqlConnectionConfiguration.Builder postgresqlConnectionConfiguration() {
return PostgresqlConnectionConfiguration.builder()
.host("localhost")
.port(5432)
.applicationName("team-toplist-service")
.database("db")
.username("user")
.password("password");
}
@Bean
@Override
public PostgresqlConnectionFactory connectionFactory() {
return new PostgresqlConnectionFactory(postgresqlConnectionConfiguration()
.build());
}
因此,我可以稍后(在我的业务方法中)使用注入的PostgresqlConnectionConfigurationBuilder实例创建一个新的PostgresqlConnectionFactory,但现在也可以使用在生成器上调用的“schema”setter(在从传入的org.springframework.web.reactive.function.server.ServerRequest中提取租户信息后,我从路由bean传递了该请求。
我的db模式遵循appname\u tenantId模式,因此我们将“appname”静态配置为ie“app\u name”,因此我最终使用类似“app\u name\u foo\u bar123”的模式名称
接下来,我们有一个租户标识符,在我的例子中,它来自一个请求标头,该标头保证由位于上游的apache服务器设置(为传入请求传递一个X-Trenter-Id标头,以便不依赖URL进行租户特定路由)
所以我的“逻辑”现在看起来有点像这样:
public Flux<TopTeam> getTopTeams(ServerRequest request) {
List<String> tenantHeader = request.headers().header("X-Tenant-Id");
// resolve relevant schema name on the fly
String schema = (appName+ "_" + tenantHeader.iterator().next()).replace("-", "_");
System.out.println("Using schema: " + schema);
// configure connfactory with schema set on the builder
PostgresqlConnectionFactory cf = new PostgresqlConnectionFactory(postgresqlConnectionConfiguration.schema(schema).build());
// init new DatabaseClient with tenant specific connection
DatabaseClient cli = DatabaseClient.create(cf);
return cli
.execute("select * from top_teams ").fetch().all()
.flatMap(map -> {
...
});
});
}
当然,这个逻辑可以抽象出来,但不确定放在哪里,也许可以将它移动到MethodArgumentResolver,这样我们就可以注入一个已经配置好的DatabaseClient
ps:这只解决了使用数据库客户端时的多租户问题。我不确定如何使用R2dbcRepositories使其工作
我正在学习多租户应用程序,以及如何使用PostgreSQL的模式来实现这一点。 在研究这个主题时,我发现了一篇文章,作者描述了在多租户应用程序中使用PostgreSQL模式时的糟糕体验。主要问题是迁移性能差和数据库资源使用率高。 似乎只有一个模式(在租户之间共享表)会比每个租户有一个单独的模式带来更好的性能。但我觉得很奇怪。我的想法正好相反,因为较小表上的索引往往比较大表上的索引轻。 为什么在许多
我想使用Flask SQLAlchemy构建一个多租户应用程序。官方SQLAlchemy文档建议,要使用多租户,表应该分布在每个租户的1个方案中,并在引擎级别处理不同的租户。 对我来说,维护多个方案似乎有点臃肿,我想知道,如果设计正确,使用相同表格的方法对所有租户是否可行,如果没有,为什么不可行: 那些包含租户所有记录的表有一个不可为空的列,该列指示哪一个租户“拥有”该行 我几乎找不到关于这种方法
我正在将当前的应用程序迁移到多租户体系结构。由于只有一个代码库,我需要解决多个租户的问题。我使用的是单数据库、多模式的方法。每个租户将被分配一个单独的模式,其中元数据保存在默认模式中。 应用程序是用ASP构建的。NET MVC。我使用Dapper连接到我的SQL Server。我有50个函数,使用直接查询和存储过程调用数据库。当为每个租户初始化dapper时,是否有任何方法可以在不改变函数的情况下
情景: 我们有一个多租户应用程序,其中每个租户都有自己的模式。有一个公共模式,其中存在一个包含每个租户记录的表。因此,有一个超级管理员可以创建租户,并将管理员分配给新创建的租户。 为了实现RBAC(基于角色的访问控制),我计划将每个角色表放入租户模式,并实现一些中间件来检查授权。在孤立的模式环境中,这是一个好的体系结构吗?
我目前正试图找出为我的系统设置多租户的最佳方法。我面临的问题是,租户并不总是必须是子域,但可以作为子域的一部分进行设置,子域可以有多个租户。我似乎在网上找不到任何东西可以帮助我在Laravel 6中进行设置。 系统要求: 一台服务器可以有许多子域 系统必须设置一个数据库,该数据库将使用tenant_id来确定哪些数据属于租户。 我目前正在以以下结构将所有子域数据存储在“subdomains”表中:
我必须在j2ee中开发一个多租户SaaS应用程序,从Iaas和PaaS开始实现三种云模型,我选择了openstack和openshift origin。SaaS应用程序的第一个标准是多租户,我知道有三种方法来实现它——单独的数据库——共享数据库,单独的模式——共享数据库,共享模式。我在这里迷失了方向,因为许多框架,比如ATHENA,ORM,比如hibernate,还有TOPLINK。我需要帮助了解