MongoDB
 sql >> Teknologi Basis Data >  >> NoSQL >> MongoDB

Multi-Tenancy dalam aplikasi boot Reactive Spring menggunakan mongodb-reactive

Saya dapat Menerapkan Multi-Tenancy di aplikasi Spring Reactive menggunakan mangodb. Kelas utama yang bertanggung jawab untuk mewujudkan adalah:Kelas MongoDbFactory Kustom, kelas WebFilter (bukan Servlet Filter) untuk menangkap info penyewa dan kelas ThreadLocal untuk menyimpan info penyewa. Alurnya sangat sederhana:

  1. Ambil info terkait Penyewa dari permintaan di WebFilter dan atur di ThreadLocal. Berikut saya kirimkan info Tenant menggunakan header:X-Tenant
  2. Terapkan kelas MondoDbFactory Kustom dan ganti getMongoDatabase() metode untuk mengembalikan database berdasarkan penyewa saat ini yang tersedia di kelas ThreadLocal.

Kode sumbernya adalah:

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

PERBARUI:

Dalam aliran reaktif, kami tidak dapat menyimpan informasi kontekstual di ThreadLocal lagi karena permintaan tidak terikat pada satu utas, Jadi, Ini bukan solusi yang tepat.

Namun, informasi Kontekstual dapat disimpan Context reaktor di WebFilter seperti ini. chain.filter(exchange).subscriberContext(context -> context.put("tenant", tenant)); . Masalahnya adalah bagaimana cara mendapatkan info kontekstual ini di ReactiveMongoDatabaseFactory kelas implementasi.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Perintah gagal dalam skrip, berfungsi di baris perintah

  2. Spring Data Mongodb:Memperbarui dokumen

  3. Bagaimana cara menggunakan Elasticsearch dengan MongoDB?

  4. pencarian agregat mongoDB pada array objek bersarang

  5. Meteor.Collection.ObjectID() vs MongoDB ObjectId()