Update 3
Beispiel und unter Erklärung ist die gleiche wie in GitHub
Update 2
Das Beispiel in GitHub arbeitet nun. Die zukunftssicherste Lösung schien die Verwendung von Repository-Erweiterungen zu sein. Wird das folgende Beispiel bald aktualisieren.
aktualisieren
Beachten Sie, dass die Lösung, die ich ursprünglich hatte einige Mängel geschrieben, die ich während JMeter Tests entdeckt. Die Datastax-Java-Treiberreferenz rät, das Festlegen des Schlüsselbereichs durch Session
Objekt zu vermeiden. Sie müssen den Schlüsselbereich in jeder Abfrage explizit festlegen.
Ich habe das GitHub-Repository aktualisiert und auch die Beschreibung der Lösung geändert.
sehr vorsichtig sein, aber: wenn die Sitzung von mehreren Threads gemeinsam genutzt wird, den Schlüsselraum zur Laufzeit Schalt leicht unerwartete Abfrage Fehler verursachen könnten.
Im Allgemeinen wird empfohlen, eine einzelne Sitzung ohne Schlüsselbereich zu verwenden und alle Ihre Abfragen mit einem Präfix zu versehen.
Lösung Beschreibung
Ich würde einen separaten Schlüsselraum für diesen speziellen Kunden Set-up und Unterstützung Schlüsselraum bei der Anwendung zu ändern. Wir haben diesen Ansatz zuvor mit RDBMS und JPA in der Produktion verwendet. Also würde ich sagen, dass es auch mit Cassandra funktionieren kann. Lösung war ähnlich wie unten.
Ich werde kurz beschreiben, wie Sie Spring Data Cassandra vorbereiten und einrichten, um den Ziel-Keypace bei jeder Anfrage zu konfigurieren.
Schritt 1: Vorbereiten der Dienste
definiere ich würde zunächst, wie die Mieter ID auf jede Anfrage zu setzen. Ein gutes Beispiel wäre in-case-of seinen REST API ist einen bestimmten HTTP-Header zu verwenden, die es definiert:
Tenant-Id: ACME
Ebenso auf jedem Remote-Protokoll Sie Mieter ID bei jeder Nachricht weiterleiten können. Angenommen, Sie verwenden AMQP oder JMS, können Sie diese interne Nachrichtenkopfzeile oder Eigenschaften weiterleiten.
Schritt 2: Erste Mieter ID in Anwendung
Als nächstes sollten Sie den eingehenden Header gespeichert auf jede Anforderung in Ihrem Controller. Sie können ThreadLocal
verwenden oder Sie können versuchen, eine beanspruchte Bean zu verwenden.
@Component
@Scope(scopeName = "request", proxyMode= ScopedProxyMode.TARGET_CLASS)
public class TenantId {
private String tenantId;
public void set(String id) {
this.tenantId = id;
}
public String get() {
return tenantId;
}
}
@RestController
public class UserController {
@Autowired
private UserRepository userRepo;
@Autowired
private TenantId tenantId;
@RequestMapping(value = "/userByName")
public ResponseEntity<String> getUserByUsername(
@RequestHeader("Tenant-ID") String tenantId,
@RequestParam String username) {
// Setting the tenant ID
this.tenantId.set(tenantId);
// Finding user
User user = userRepo.findOne(username);
return new ResponseEntity<>(user.getUsername(), HttpStatus.OK);
}
}
Schritt 3: Einstellen Mieter ID in Datenzugriffsschicht
Schließlich sollten Sie Repository
Implementierungen erweitern und Set-up Schlüsselraum entsprechend der Mieter ID
public class KeyspaceAwareCassandraRepository<T, ID extends Serializable>
extends SimpleCassandraRepository<T, ID> {
private final CassandraEntityInformation<T, ID> metadata;
private final CassandraOperations operations;
@Autowired
private TenantId tenantId;
public KeyspaceAwareCassandraRepository(
CassandraEntityInformation<T, ID> metadata,
CassandraOperations operations) {
super(metadata, operations);
this.metadata = metadata;
this.operations = operations;
}
private void injectDependencies() {
SpringBeanAutowiringSupport
.processInjectionBasedOnServletContext(this,
getServletContext());
}
private ServletContext getServletContext() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest().getServletContext();
}
@Override
public T findOne(ID id) {
injectDependencies();
CqlIdentifier primaryKey = operations.getConverter()
.getMappingContext()
.getPersistentEntity(metadata.getJavaType())
.getIdProperty().getColumnName();
Select select = QueryBuilder.select().all()
.from(tenantId.get(),
metadata.getTableName().toCql())
.where(QueryBuilder.eq(primaryKey.toString(), id))
.limit(1);
return operations.selectOne(select, metadata.getJavaType());
}
// All other overrides should be similar
}
@SpringBootApplication
@EnableCassandraRepositories(repositoryBaseClass = KeyspaceAwareCassandraRepository.class)
public class DemoApplication {
...
}
Lassen Sie mich wissen, wenn es Probleme mit dem obigen Code gibt.
Beispielcode in GitHub
https://github.com/gitaroktato/spring-boot-cassandra-multitenant-example
Referenzen
Ich sehe nicht, wie Sie es erreichen können, ohne einen neuen Schlüsselraum zu erstellen, wenn alle Kunden denselben Schlüsselraum teilen und seine Daten in Rechenzentren in mehreren Ländern verteilt sind. Im Schlüsselbereich geben Sie an, dass die Daten nur in bestimmten Datencentern (im Kundenland) abgelegt werden sollen. – Edu
@Edu, ja, wir denken auf die gleiche Weise (potentielle Lösung # 1), aber mit Spring Data Cassandra ist es nicht möglich, dynamisches Umschalten von Schlüsselraum zu verwenden (zumindest meine paar Stunden Forschung hat nicht geholfen). – walv
@walv: warum sagst du das Hinzufügen von "keyspace vor der Tabelle select * von blabla.mytable" klingt wie ein hack? Es ist eine normale Art, eine Tabelle zu referenzieren und wird ziemlich verwendet. Es ist wie ein vollständig qualifizierter Name der Tabelle. – Horia