当前位置: 首页 > 知识库问答 >
问题:

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

徐景明
2023-03-14

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

<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";
    }
}

整个应用程序是一个OAuth2.0资源服务器,我可以从ReactiveSecurityContextHolder中的身份验证中检索TenantID。

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();
            });
}

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

共有1个答案

林蕴藉
2023-03-14

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

Spring没有为这个问题提供任何开箱即用的解决方案。几个月前,我创建了一个概念证明,如何解决它,并刚刚在GitHub上发布了示例项目。

Spring-MongoDB-多租户-示例

简而言之:我创建了一个自定义的MultiTenancyReactiveMongoTemplate,它根据来自SubscriberContext的tenantId在内部委托给实际的ReactiveMongoTemplate。tenantId是从定制WebFilter中的HTTP请求头中提取的,它将其放在SubscriberContext中。

此解决方案适用于大多数情况,还支持自动创建索引和使用reactivemongorepository

但也有一些限制,因为MultiTenancyReactiveMongoTemplateReactiveGridFStemplate上的事务、IndexOp不能与提供的解决方案一起工作。有些事情可以用其他委托“模板”来实现,但有些事情永远不会起作用,因为这些操作只是返回标量值(没有publisher),并且在这些情况下无法访问subscriberContext。

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

您可以为每个租户/客户升级和配置服务实例,并在这些服务的‘前面’放置一个反向代理。反向代理决定应该使用哪个服务来处理请求。反向代理可以很容易地实现,例如Spring Cloud Gateway,它允许您很容易地实现决定(例如,基于身份验证令牌)应该使用哪种服务的谓词。

有了CloudFoundry或Kubernetes这样的技术,协调这些特定于承租人的服务的部署就不再那么困难了,这个解决方案也使得进行特定于承租人的监视、警报甚至计费变得很容易。

我们选择解决方案2是因为总体而言,这对我们来说更容易,规模更大。

 类似资料:
  • 我正在Postman中编写测试,我有多个请求分组如下: > 某些测试标题: > 创建用户(一组“预请求”): 发送一些创建用户所需的请求,这些请求是运行测试所必需的 对已创建用户的一些操作(这里我正在测试测试标题中的内容) 一个或多个请求 < li >创建用户(一组“预先请求”,与测试1中相同。): < ul > < li >发送创建用户、运行测试所需的一些请求 一个或多个请求 总的来说,当我想测

  • 如何每秒触发10个请求,而不是等待前面的线程在jmeter中完成。 需要打1000次, 当前线程组中的以下配置,用户数:10循环:100 我猜线程正在等待响应,即使在第二秒之后。 但是我需要每秒发出10个请求,不管响应如何。 恒定吞吐量计时器将有助于做到这一点?如果是这种情况,我应该提供什么配置。 非常感谢您的帮助。。

  • 我不知道如何用图表来做这件事。js。 请帮帮我。 我的Javascript代码: php代码:

  • 我的方法正确吗?在正文中发送凭据>接收响应(成功)>登录成功>将令牌存储在会话存储中并将其设置在头中>会话过期时注销 存储jwt

  • 问题内容: 我有以下数据框: 我尝试过数据透视表 但出现以下错误: 数据透视表的任何替代选择吗? 问题答案: 您可以用来转置数据框。这将数据框切换为圆形,以便行变为列。 您也可以使用。

  • 我正在创建一个不使用数据库的服务。 我将所有的请求数据存储在文件系统中。 我正在使用spring Boot开发服务。