gpt4 book ai didi

Cassandra:每个键空间的客户数据

转载 作者:行者123 更新时间:2023-12-04 03:03:18 25 4
gpt4 key购买 nike

问题 :我们的一位新客户希望将数据存储在他自己的国家(法律规定)。但是,我们使用分布在不同国家/地区的少数数据中心的现有客户数据。

问题 :我们如何在不改变现有 Cassandra 架构的情况下将新客户的数据分离到自己的国家/地区?

潜在解决方案#1 :为此客户使用单独的 key 空间。键空间之间的模式将相同,这增加了数据迁移等的复杂性。 DataStax 支持确认可以为每个区域配置 key 空间。
但是我们使用的 Spring Data Cassandra 不允许动态选择键空间。
唯一的方法是使用 CqlTemplate 并运行 use keyspace blabla每次调用前或在表前添加键空间select * from blabla.mytable但这对我来说听起来像是一个黑客。

潜在解决方案#2 为新客户使用单独的环境,但管理层拒绝这样做。

还有其他方法可以实现这个目标吗?

最佳答案

更新 3

下面的示例和解释与 GitHub 中的相同

更新 2

GitHub 中的示例现在正在运行。最面向 future 的解决方案似乎是使用存储库扩展。将很快更新下面的示例。

更新

请注意,我最初发布的解决方案存在一些我在 JMeter 测试期间发现的缺陷。 Datastax Java 驱动引用建议避免通过Session 设置键空间。目的。您必须在每个查询中明确设置键空间。

我更新了 GitHub 存储库并更改了解决方案的描述。

Be very careful though: if the session is shared by multiple threads, switching the keyspace at runtime could easily cause unexpected query failures.

Generally, the recommended approach is to use a single session with no keyspace, and prefix all your queries.



解决方案说明

我会为这个特定的客户设置一个单独的键空间,并为在应用程序中更改键空间提供支持。我们之前在生产环境中将这种方法与 RDBMS 和 JPA 一起使用。所以,我想说它也可以与 Cassandra 一起使用。解决方案类似于以下。

我将简要描述如何准备和设置 Spring Data Cassandra 以在每个请求上配置目标键空间。

第 1 步:准备服务

我将首先定义如何在每个请求上设置租户 ID。一个很好的例子是 REST API 是使用定义它的特定 HTTP header :
Tenant-Id: ACME

同样,在每个远程协议(protocol)上,您都可以在每条消息上转发租户 ID。假设您使用 AMQP 或 JMS,您可以在消息头或属性中转发此消息。

步骤 2:在应用程序中获取租户 ID

接下来,您应该将传入的 header 存储在 Controller 内的每个请求中。您可以使用 ThreadLocal或者您可以尝试使用请求范围的 bean。
@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);
}
}

步骤 3:在数据访问层设置租户 ID

最后你应该扩展 Repository根据租户 ID 实现和设置 key 空间
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 {
...
}

如果上面的代码有任何问题,请告诉我。

GitHub 中的示例代码

https://github.com/gitaroktato/spring-boot-cassandra-multitenant-example

引用
  • Example: Using JPA interceptors
  • Spring @RequestHeader example
  • Spring request-scoped beans
  • Datastax Java Driver Reference
  • 关于Cassandra:每个键空间的客户数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46964654/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com