sql >> Database >  >> NoSQL >> MongoDB

Multi-tenancy in Reactive Spring-opstarttoepassing met mongodb-reactive

Ik kon Multi-Tenancy implementeren in de Spring Reactive-toepassing met behulp van mangodb. De belangrijkste klassen die verantwoordelijk waren voor de realisatie waren:Custom MongoDbFactory-klasse, WebFilter-klasse (in plaats van Servlet-filter) voor het vastleggen van huurdersinformatie en een ThreadLocal-klasse voor het opslaan van huurdersinformatie. Flow is heel eenvoudig:

  1. Vang Tenant-gerelateerde informatie van het verzoek op in WebFilter en stel het in ThreadLocal in. Hier stuur ik huurdersinformatie met header:X-Tenant
  2. Implementeer de Custom MondoDbFactory-klasse en overschrijf getMongoDatabase() methode om de database te retourneren op basis van de huidige tenant die beschikbaar is in de ThreadLocal-klasse.

Broncode is:

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

UPDATE:

In reactieve stream kunnen we geen contextuele informatie meer opslaan in ThreadLocal omdat het verzoek niet aan een enkele thread is gekoppeld. Dit is dus niet de juiste oplossing.

Contextuele informatie kan echter op deze manier in de WebFilter worden opgeslagen. chain.filter(exchange).subscriberContext(context -> context.put("tenant", tenant)); . Het probleem is hoe u deze contextuele informatie in ReactiveMongoDatabaseFactory kunt krijgen implementatieklasse.



  1. verwijder een ingesloten document in mongoid

  2. MongoDB - krijg documenten met max attribuut per groep in een collectie

  3. Kan transactiestatus niet behouden omdat de verzameling van sessietransacties ontbreekt

  4. Hoe Pyramid 1.1 en Mongo DB te integreren - zo min mogelijk regels