Spring Boot WebFlux Reactive MongoDB:如何根据每个请求切换数据库?

Spring Boot WebFlux Reactive MongoDB: how to switch the database on each request?

我正在使用 Spring WebFlux 和 Reactive MongoDB 开发一个 SaaS 项目。 它需要是一个多租户应用程序,每个租户必须使用自己的数据库。

至于现在,我只是将 Reactive MongoDB 依赖项添加到 pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

然后我扩展了 AbstractReactiveMongoConfiguration 以提供 MongoClient 和 DatabaseName:

import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration;

@Configuration
public class DatabaseConfiguration extends AbstractReactiveMongoConfiguration {
    @Override
    public MongoClient reactiveMongoClient() {
        System.out.println("ReactiveMongoClient");
        return MongoClients.create();
    }

    @Override
    protected String getDatabaseName() {
        System.out.println("DataBase name");
        return "gateway";
    }
}

整个应用程序是一个 OAuth 2.0 资源服务器,我能够从 Authentication 中检索 TenantID ReactiveSecurityContextHolder.

public Mono<String> tenantID() {
    return ReactiveSecurityContextHolder.getContext()
            .switchIfEmpty(Mono.empty())
            .flatMap((securityContext) -> {
                Authentication authentication = securityContext.getAuthentication();

                if (authentication instanceof CustomAuthenticationToken) {
                    CustomAuthenticationToken customAuthenticationToken = (customAuthenticationToken) authentication;
                    Jwt jwt = customAuthenticationToken.getToken();

                    String issuer = jwt.getIssuer().toString();
                    return Mono.justOrEmpty(issuer);
                }

                return Mono.empty();
            });
}

为了根据执行请求的用户(身份验证)切换数据库,下一步是什么?

更新:

This is almost near to the goal, but almost one year ago @mp911de 说不可能。 想知道现在是否可行。 我如何实现一个真正的反应式 ReactiveMongoDatabaseFactory returns Mono 以便我可以在返回 Mongo数据库?

几个月前我遇到了同样的问题,想分享一下我是如何解决的。

解决方案 1:自己动手

Spring 没有针对此问题提供任何 out-of-the-box 解决方案。几个月前,我创建了一个如何解决它的概念证明,并刚刚在 Github.

上发布了示例项目

spring-mongodb-multi-tenancy-example

简而言之:我创建了一个自定义 MultiTenancyReactiveMongoTemplate,它根据 subscriberContext 中的 tenantId 在内部委托给实际的 ReactiveMongoTemplate tenantId 是从自定义 WebFilter 中的 http-request header 中提取的,将其放入 subscriberContext 中。

此解决方法适用于大多数情况,并且还支持 auto-index 创建和使用 ReactiveMongoRepository

但也有一些限制,因为事务、MultiTenancyReactiveMongoTemplateReactiveGridFSTemplate 上的 IndexOps 不适用于提供的解决方案。有些事情可以通过其他委托 'templates' 来实现,但有些事情永远不会起作用,因为这些操作只是 return 标量值(没有 Publisher)并且无法访问 subscriberContext这些案例。

如果您不需要这些功能,您可以使用此解决方案。

解决方案 2:

您为每个 tenant/customer 启动并配置服务实例,并放置一个反向代理 'before' 这些服务。反向代理决定应该使用哪个服务来处理请求。 反向代理可以非常容易地实现,例如 Spring Cloud Gateway,它允许您轻松地实现 predicates 来决定(例如,基于 auth 令牌)应该使用哪个服务。

借助 CloudFoundry 或 Kubernetes 等技术,编排这些租户特定服务的部署不再那么困难,而且该解决方案还可以轻松进行租户特定监控、警报甚至计费。

我们选择了解决方案 2,因为总的来说这更容易,而且对我们来说扩展性更好。