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

问题描述 投票:1回答:1

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

我正在努力在r2dbc中实现这一目标。理想情况下,它将是Hibernate使用MultiTenantConnectionProvider的方式(请参见示例16.3)。

[我发现并到目前为止所做的事情:

  • 可以如AbstractRoutingConnectionFactory所述使用here。但是我被租户/模式强迫创建一个ConnectionFactory。在我看来,这远非高效/可扩展的,我宁愿使用r2dbc-pool
  • 这样的连接池
  • 我看着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();
    }
    

也许我需要找到一种方法来重写此方法,以便从上下文而不是配置中动态获取模式?

  • 否则,我可以尝试在请求中将模式指定为表前缀:

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

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

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

multi-tenant spring-data-r2dbc r2dbc
1个回答
0
投票

我也遇到了这个问题。

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

  • 作为一个Bean发布PostgresqlConnectionConfigurationBuilderPostgresqlConnectionFactory

    @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”设置器(but)(从传入的[[org.springframework.web.reactive.function.server.ServerRequest中提取租户信息后,我从路由bean传下来。

我的数据库模式遵循appname_tenantId模式,所以我们有一个静态配置为“ app_name”的“ appName”,所以我最终得到了像“ 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 -> { ... }); }); }

当然可以抽象出这种逻辑,不确定该放在哪里,也许可以将其移到MethodArgumentResolver以便我们可以注入一个已经配置的DatabaseClient
© www.soinside.com 2019 - 2024. All rights reserved.