我在这里编写了一个改进的Kademlia P2P系统,但我在这里描述的问题与原始版本的实现非常相似。
那么,实施k-Buckets最有效的方法是什么?对我来说重要的是访问时间,并行性(读取和写入)和内存消耗。
考虑使用ConcurrentLinkedQueue和ConcurrentHashMap来做这件事,但是它非常冗余和讨厌,不是吗?
目前我只是同步一个LinkedList。
这是我的代码:
import java.util.LinkedList;
class Bucket {
private final LinkedList<Neighbour> neighbours;
private final Object lock;
Bucket() {
neighbours = new LinkedList<>();
lock = new Object();
}
void sync(Neighbour n) {
synchronized(lock) {
int index = neighbours.indexOf(n);
if(index == -1) {
neighbours.add(n);
n.updateLastSeen();
} else {
Neighbour old = neighbours.remove(index);
neighbours.add(old);
old.updateLastSeen();
}
}
}
void remove(Neighbour n) {
synchronized(lock) {
neighbours.remove(n);
}
}
Neighbour resolve(Node n) throws ResolveException {
Neighbour nextHop;
synchronized(lock) {
int index = neighbours.indexOf(n);
if(index == -1) {
nextHop = neighbours.poll();
neighbours.add(nextHop);
return nextHop;
} else {
return neighbours.get(index);
}
}
}
}
请不要怀疑,我已经实施了另一个邻居驱逐程序。
答案 0 :(得分:1)
那么,实施k-Buckets的最有效方法是什么?
这取决于。如果您想使用铃声和口哨(例如铲斗拆分,多宿主)进行实施,那么您需要灵活的列表或树。 根据我的经验,写入数组+二进制搜索的副本适用于路由表,因为您很少修改存储桶的总数,只需要修改存储桶的内容。
使用CoW语义,您需要更少的锁定,因为您只需获取阵列的当前副本,检索感兴趣的存储桶,然后锁定存储桶。或者在每个桶中使用原子阵列。 但是当然,只有当你期望高吞吐量时才需要这样的优化,大多数DHT节点看到非常少的流量,最多每秒几个数据包,即除非你实现一个具有如此多吞吐量的专用节点,否则不需要涉及多个线程。需要多个线程来处理数据。
CoW对于类似路由表的查找缓存或在查找期间构建的短暂访问节点/目标节点集的效果较差,因为它们会得到快速修改。如果您期望高负载,ConcurrentSkipListMaps可能是更好的选择。
如果你想要一个简化的近似实现,那么只需使用160个元素的固定大小的数组,其中数组索引是共享前缀位数相对于您的节点ID。这表现相当不错,但不允许修订的kademlia论文提出的一些优化。