मैं मैंगोडब का उपयोग करके स्प्रिंग रिएक्टिव एप्लिकेशन में मल्टी-टेनेंसी को लागू करने में सक्षम था। साकार करने के लिए जिम्मेदार मुख्य वर्ग थे:कस्टम MongoDbFactory वर्ग, वेबफ़िल्टर वर्ग (सर्वलेट फ़िल्टर के बजाय) टेनेंट जानकारी कैप्चर करने के लिए और एक थ्रेडलोकल क्लास टेनेंट जानकारी संग्रहीत करने के लिए। प्रवाह बहुत सरल है:
- वेबफ़िल्टर में अनुरोध से किरायेदार से संबंधित जानकारी कैप्चर करें और इसे थ्रेडलोकल में सेट करें। यहां मैं हेडर का उपयोग करके किरायेदार की जानकारी भेज रहा हूं:एक्स-टेनेंट
- कस्टम MondoDbFactory क्लास लागू करें और
getMongoDatabase()
को ओवरराइड करें थ्रेडलोकल क्लास में उपलब्ध मौजूदा टैनेंट के आधार पर डेटाबेस वापस करने की विधि।
स्रोत कोड है:
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;
}
}
अपडेट करें:
रिएक्टिव-स्ट्रीम में हम प्रासंगिक जानकारी को थ्रेडलोकल में स्टोर नहीं कर सकते क्योंकि अनुरोध एक थ्रेड से बंधा नहीं है, इसलिए, यह सही समाधान नहीं है।
हालाँकि, प्रासंगिक जानकारी को इस तरह WebFilter में रिएक्टर संदर्भ संग्रहीत किया जा सकता है। chain.filter(exchange).subscriberContext(context -> context.put("tenant", tenant));
. समस्या यह है कि ReactiveMongoDatabaseFactory
. में इस प्रासंगिक जानकारी को कैसे पकड़ें कार्यान्वयन वर्ग।