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
。
但也有一些限制,因为事务、MultiTenancyReactiveMongoTemplate
或 ReactiveGridFSTemplate
上的 IndexOps 不适用于提供的解决方案。有些事情可以通过其他委托 'templates' 来实现,但有些事情永远不会起作用,因为这些操作只是 return 标量值(没有 Publisher
)并且无法访问 subscriberContext这些案例。
如果您不需要这些功能,您可以使用此解决方案。
解决方案 2:
您为每个 tenant/customer 启动并配置服务实例,并放置一个反向代理 'before' 这些服务。反向代理决定应该使用哪个服务来处理请求。
反向代理可以非常容易地实现,例如 Spring Cloud Gateway,它允许您轻松地实现 predicates 来决定(例如,基于 auth 令牌)应该使用哪个服务。
借助 CloudFoundry 或 Kubernetes 等技术,编排这些租户特定服务的部署不再那么困难,而且该解决方案还可以轻松进行租户特定监控、警报甚至计费。
我们选择了解决方案 2,因为总的来说这更容易,而且对我们来说扩展性更好。
我正在使用 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
。
但也有一些限制,因为事务、MultiTenancyReactiveMongoTemplate
或 ReactiveGridFSTemplate
上的 IndexOps 不适用于提供的解决方案。有些事情可以通过其他委托 'templates' 来实现,但有些事情永远不会起作用,因为这些操作只是 return 标量值(没有 Publisher
)并且无法访问 subscriberContext这些案例。
如果您不需要这些功能,您可以使用此解决方案。
解决方案 2:
您为每个 tenant/customer 启动并配置服务实例,并放置一个反向代理 'before' 这些服务。反向代理决定应该使用哪个服务来处理请求。 反向代理可以非常容易地实现,例如 Spring Cloud Gateway,它允许您轻松地实现 predicates 来决定(例如,基于 auth 令牌)应该使用哪个服务。
借助 CloudFoundry 或 Kubernetes 等技术,编排这些租户特定服务的部署不再那么困难,而且该解决方案还可以轻松进行租户特定监控、警报甚至计费。
我们选择了解决方案 2,因为总的来说这更容易,而且对我们来说扩展性更好。