Arn*_*d42 8 multi-tenant spring-data-r2dbc r2dbc
我正在开发一个多租户反应式应用程序,使用 Spring-Webflux + Spring-data-r2dbc 和 r2dbc 驱动程序连接到 Postgresql 数据库。多租户部分是基于模式的:每个租户一个模式。因此,根据上下文(例如用户登录),请求将命中数据库的特定架构。
我正在努力研究如何在 r2dbc 中实现这一点。理想情况下,这将是 Hibernate 使用MultiTenantConnectionProvider 的方式(参见示例 16.3)。
到目前为止我发现了什么以及我做了什么:
我查看了PostgresqlConnectionFactory。有趣的是,有prepareConnection一个电话setSchema(connection):
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();
}
Run Code Online (Sandbox Code Playgroud)我可能需要找到一种方法来覆盖它,以便从上下文而不是配置中动态获取模式?
否则,我可以尝试将请求中的架构指定为表前缀:
String s = "tenant-1";
databaseClient.execute("SELECT * FROM \"" + s + "\".\"city\"")
.as(City.class)
.fetch()
.all()
Run Code Online (Sandbox Code Playgroud)但我不能再使用 SpringData,或者我需要重写每个请求以将租户作为参数传递。
任何提示/帮助表示赞赏:)
我也遇到了这个。
这是我目前正在做的事情:
将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());
}
Run Code Online (Sandbox Code Playgroud)这样我以后就可以(在我的业务方法中)使用注入的PostgresqlConnectionConfigurationBuilder实例创建一个新的PostgresqlConnectionFactory -但现在也可以在构建器上调用“模式”设置器(在从传入的org.springframework.web.reactive中提取租户信息之后) .function.server.ServerRequest是我从路由 bean 传递下来的。
我的数据库模式遵循 appname_tenantId 模式,因此我们有一个静态配置的“appName”,即“app_name”,所以我最终得到像“app_name_foo_bar123”这样的模式名称
接下来,我们有一个租户标识符,在我的例子中,它来自一个请求标头,该标头保证由位于上游的 apache 服务器设置(为传入请求传递 X-Tenant-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 -> {
...
});
});
}
Run Code Online (Sandbox Code Playgroud)
这个逻辑当然可以被抽象掉,但不确定将其放在哪里,也许可以将其移动到 MethodArgumentResolver 中,这样我们就可以注入一个已经配置的 DatabaseClient
ps:这仅解决使用DatabaseClient时的多租户问题。我不确定如何使用 R2dbcRepositories 来完成这项工作