我已经实现了一个hazelcast服务,它通过MapStoreFactory和newMapLoader将其数据存储到本地mapdb实例中。这样,如果需要重新启动集群,则可以加载密钥:
public class HCMapStore<V> implements MapStore<String, V> {
Map<String, V> map;
/** specify the mapdb e.g. via
* DBMaker.newFileDB(new File("mapdb")).closeOnJvmShutdown().make()
*/
public HCMapStore(DB db) {
this.db = db;
this.map = db.createHashMap("someMapName").<String, Object>makeOrGet();
}
// some other store methods are omitted
@Override
public void delete(String k) {
logger.info("delete, " + k);
map.remove(k);
db.commit();
}
// MapLoader methods
@Override
public V load(String key) {
logger.info("load, " + key);
return map.get(key);
}
@Override
public Set<String> loadAllKeys() {
logger.info("loadAllKeys");
return map.keySet();
}
@Override
public Map<String, V> loadAll(Collection<String> keys) {
logger.info("loadAll, " + keys);
Map<String, V> partialMap = new HashMap<>();
for (String k : keys) {
partialMap.put(k, map.get(k));
}
return partialMap;
}}
我现在面临的问题是来自hazelcast的MapLoader接口的loadAllKeys方法需要返回整个集群的所有密钥,但每个节点只存储它拥有的对象。
示例:我有两个节点并存储8个对象,例如5个对象存储在node1的mapdb中,3个存储在node2的mapdb中。我认为哪个对象由hazelcast决定由哪个节点决定。现在重新启动node1将为loadAllKeys返回5个键,node2将返回3. Hazelcast决定忽略3个项目并且数据“丢失”。
这可能是一个很好的解决方案吗?
赏金更新:Here我在hc邮件列表中提到了2个选项(我还要再添加1个),我想知道这样的事情是否已经存在可能使用hazelcast 3.2或3.3:
目前,MapStore接口仅从本地节点获取数据或更新。是否可以通知MapStore接口完整集群的每个存储操作?或者这可能已经成为一些听众的魔力?也许我可以强制使用hazelcast将所有对象放入一个分区,每个节点上都有一个副本。
如果我重新启动,例如2个节点然后使用我的本地数据库为node1正确调用MapStore接口,然后为node2正确调用。但是当两个节点都加入时,node2的数据将被删除,因为Hazelcast假设只有主节点可以正确。我可以教hazelcast接受来自两个节点的数据吗?
答案 0 :(得分:2)
根据Hazelcast 3.3文档 MapLoader初始化流程如下:
首次从任何节点调用getMap()时,将开始初始化 取决于InitialLoadMode的值。如果设置为EAGER, 初始化开始。如果设置为LAZY,则实际初始化 没有启动,但每次加载分区时都会加载数据 完成。
- Hazelcast会调用MapLoader.loadAllKeys()来获取所有密钥 每个节点
- 每个节点都会找出它拥有的密钥列表
- 每个节点都会通过调用加载所有拥有的密钥 MapLoader.loadAll(键)
- 每个节点通过调用将其拥有的条目放入映射中 IMap.putTransient(键,值)
醇>
以上暗示如果节点以不同的顺序启动,那么密钥也将以不同的方式分布。因此,每个节点将不会在其本地存储中找到所有/一些分配的密钥。您应该能够通过在HCMapStore.loadAllKeys和HCMapStore.loadAll中设置断点来验证它,并将您获取的密钥与密钥进行比较。
在我看来,你想要实现的目标与具有弹性特征的分布式缓存的概念相矛盾,因此是不可能的。即当一个节点消失(由于某种原因失败或断开连接)时,集群将通过移动部分数据来重新平衡,每次节点加入集群时都会发生相同的过程。因此,在群集更改的情况下,丢失节点的本地反向存储将变得过时。
Hazelcast集群本质上是动态的,因此它不能依赖具有静态分布式拓扑的后台存储。实质上,您需要拥有一个共享的后台存储,以使其与动态hazelcast集群一起使用。也可以分发后台存储,例如cassandra,但其拓扑必须独立于缓存集群拓扑。
更新:在我看来,您尝试实现的内容更具逻辑性,采用本地缓存的分布式数据存储(在MapDB之上)。
我希望这会有所帮助。
答案 1 :(得分:1)
也许有两个选择:
1)深入了解分区在Hazelcast中的工作原理。我认为每个分区都可以有MapLoader,强制节点只加载自己的分区,这样可以解决冲突。
2)当节点重新联机时,在添加节点之前与Hazelcast群集进行交互。你可以从MapDB中合并两个HZ第二组。
3)强制Hazelcast在每个节点上存储所有数据。将分区号设置为1或其他
答案 2 :(得分:1)
可以加载存储在所有节点上的数据,但此时您必须手动执行此操作。
在每个节点上:
HCMapStore store = createMapDbStore();
HazelcastInstance hz = createHz( store ); // use store in MapStoreConfig as implementation
IMap imap = hz.getMap("map");
Map diskMap = store.loadAll( store.loadAllKeys() ); // load all entries on disk
imap.putAll( diskMap ); // put into distributed map
但是正如邮件列表MapStore
中提到的那样,并不是真的想以这种方式使用。另请注意,备份不会以这种方式持久保存到磁盘。因此,如果重新启动群集并且一个节点上的磁盘死亡,那么这些条目将会丢失。
答案 3 :(得分:0)
Hazelcast的持久层要求它是某种形式 中央存储。像数据库或共享文件一样。