使用 r2dbc 的基于多租户模式的应用程序

Arn*_*d42 8 multi-tenant spring-data-r2dbc r2dbc

我正在开发一个多租户反应式应用程序,使用 Spring-Webflux + Spring-data-r2dbc 和 r2dbc 驱动程序连接到 Postgresql 数据库。多租户部分是基于模式的:每个租户一个模式。因此,根据上下文(例如用户登录),请求将命中数据库的特定架构。

我正在努力研究如何在 r2dbc 中实现这一点。理想情况下,这将是 Hibernate 使用MultiTenantConnectionProvider 的方式(参见示例 16.3)。

到目前为止我发现了什么以及我做了什么:

我可能需要找到一种方法来覆盖它,以便从上下文而不是配置中动态获取模式?

  • 否则,我可以尝试将请求中的架构指定为表前缀:

        String s = "tenant-1";
        databaseClient.execute("SELECT * FROM \"" + s + "\".\"city\"")
                .as(City.class)
                .fetch()
                .all()
    
    Run Code Online (Sandbox Code Playgroud)

但我不能再使用 SpringData,或者我需要重写每个请求以将租户作为参数传递。

任何提示/帮助表示赞赏:)

cha*_*ver 1

我也遇到了这个。

这是我目前正在做的事情:

  • 将PostgresqlConnectionConfigurationBuilderPostgresqlConnectionFactory发布为 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 来完成这项工作