sql >> データベース >  >> NoSQL >> MongoDB

mongodb-reactiveを使用したReactiveSpringブートアプリケーションのマルチテナンシー

    mangodbを使用して、SpringReactiveアプリケーションにマルチテナンシーを実装することができました。実現を担当する主なクラスは、カスタムMongoDbFactoryクラス、テナント情報をキャプチャするための(サーブレットフィルタの代わりに)WebFilterクラス、およびテナント情報を格納するためのThreadLocalクラスでした。フローは非常に単純です:

    1. WebFilterでリクエストからテナント関連情報を取得し、ThreadLocalに設定します。ここでは、ヘッダーを使用してテナント情報を送信しています:X-Tenant
    2. カスタムMondoDbFactoryクラスを実装し、getMongoDatabase()をオーバーライドします ThreadLocalクラスで使用可能な現在のテナントに基づいてデータベースを返すメソッド。

    ソースコードは次のとおりです:

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

    更新:

    リアクティブストリームでは、リクエストが単一のスレッドに関連付けられていないため、コンテキスト情報をThreadLocalに保存できなくなります。したがって、これは正しい解決策ではありません。

    ただし、コンテキスト情報は、このようにWebFilterにreactorContextを格納できます。 chain.filter(exchange).subscriberContext(context -> context.put("tenant", tenant)); 。問題は、ReactiveMongoDatabaseFactoryでこのコンテキスト情報を取得する方法です。 実装クラス。



    1. Mongooseにオブジェクトを保存した後、objectIDを取得するにはどうすればよいですか?

    2. サブプロパティのJSON.NETカスタム名前リゾルバー

    3. ClassNotFoundExceptionを解決する方法:com.mongodb.connection.BufferProvider?

    4. Laravel 4:クラス「MongoClient」が見つかりません