MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Mandantenfähigkeit in Reactive Spring Boot-Anwendung mit mongodb-reactive

Ich konnte Multi-Tenancy in der Spring Reactive-Anwendung mit mangodb implementieren. Hauptklassen, die für die Realisierung verantwortlich waren, waren:Benutzerdefinierte MongoDbFactory-Klasse, WebFilter-Klasse (anstelle von Servlet-Filter) zum Erfassen von Mandanteninformationen und eine ThreadLocal-Klasse zum Speichern von Mandanteninformationen. Der Ablauf ist sehr einfach:

  1. Mieterbezogene Informationen aus der Anfrage in WebFilter erfassen und in ThreadLocal festlegen. Hier sende ich Mandanteninformationen mit dem Header:X-Tenant
  2. Implementieren Sie die benutzerdefinierte MondoDbFactory-Klasse und überschreiben Sie getMongoDatabase() Methode zum Zurückgeben der Datenbank basierend auf dem aktuellen Mandanten, der in der ThreadLocal-Klasse verfügbar ist.

Quellcode ist:

CurrentTenantHolder.java

package com.jazasoft.demo;

public class CurrentTenantHolder {
    private static final ThreadLocal<String> currentTenant = new InheritableThreadLocal<>();

    public static String get() {
        return currentTenant.get();
    }

    public static void set(String tenant) {
        currentTenant.set(tenant);
    }

    public static String remove() {
        synchronized (currentTenant) {
            String tenant = currentTenant.get();
            currentTenant.remove();
            return tenant;
        }
    }
}

TenantContextWebFilter.java

package com.example.demo;

import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

@Component
public class TenantContextWebFilter implements WebFilter {

    public static final String TENANT_HTTP_HEADER = "X-Tenant";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        if (request.getHeaders().containsKey(TENANT_HTTP_HEADER)) {
            String tenant = request.getHeaders().getFirst(TENANT_HTTP_HEADER);
            CurrentTenantHolder.set(tenant);
        }
        return chain.filter(exchange).doOnSuccessOrError((Void v, Throwable throwable) -> CurrentTenantHolder.remove());
    }
}

MultiTenantMongoDbFactory.java

package com.example.demo;

import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoDatabase;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory;


public class MultiTenantMongoDbFactory extends SimpleReactiveMongoDatabaseFactory {
    private final String defaultDatabase;

    public MultiTenantMongoDbFactory(MongoClient mongoClient, String databaseName) {
        super(mongoClient, databaseName);
        this.defaultDatabase = databaseName;
    }


    @Override
    public MongoDatabase getMongoDatabase() throws DataAccessException {
        final String tlName = CurrentTenantHolder.get();
        final String dbToUse = (tlName != null ? tlName : this.defaultDatabase);
        return super.getMongoDatabase(dbToUse);
    }
}

MongoDbConfig.java

package com.example.demo;

import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.ReactiveMongoClientFactoryBean;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;

@Configuration
public class MongoDbConfig {

    @Bean
    public ReactiveMongoTemplate reactiveMongoTemplate(MultiTenantMongoDbFactory multiTenantMongoDbFactory) {
        return new ReactiveMongoTemplate(multiTenantMongoDbFactory);
    }

    @Bean
    public MultiTenantMongoDbFactory multiTenantMangoDbFactory(MongoClient mongoClient) {
        return new MultiTenantMongoDbFactory(mongoClient, "test1");
    }

    @Bean
    public ReactiveMongoClientFactoryBean mongoClient() {
        ReactiveMongoClientFactoryBean clientFactory = new ReactiveMongoClientFactoryBean();
        clientFactory.setHost("localhost");
        return clientFactory;
    }
}

AKTUALISIERUNG:

Im reaktiven Stream können wir keine Kontextinformationen mehr in ThreadLocal speichern, da die Anfrage nicht an einen einzelnen Thread gebunden ist. Dies ist also nicht die richtige Lösung.

Kontextinformationen können jedoch wie folgt im Reaktorkontext im WebFilter gespeichert werden. chain.filter(exchange).subscriberContext(context -> context.put("tenant", tenant)); . Das Problem ist, wie Sie diese Kontextinformationen in ReactiveMongoDatabaseFactory erhalten Implementierungsklasse.