i2p源码笔记-KBucket.java KBucketImpl.java

文章目录

  • KBucket.java
    • sourcecode
    • 详细笔记
      • 概念介绍
      • 代码分析
  • KBucketImpl.java
    • sourcecode
    • 笔记
      • 注释
      • 代码
        • 方法
    • 下我们需要看I2PContext以及ConcurrentSet两个文件

KBucket.java

sourcecode

package net.i2p.kademlia;
/*
 * free (adj.): unencumbered; not under the control of others
 * Written by jrandom in 2003 and released into the public domain 
 * with no warranty of any kind, either expressed or implied.  
 * It probably won't make your computer catch on fire, or eat 
 * your children, but it might.  Use at your own risk.
 *
 */

import java.util.Set;

import net.i2p.data.SimpleDataStructure;

/**
 * Group, without inherent ordering, a set of keys a certain distance away from
 * a local key, using XOR as the distance metric
 *
 * Refactored from net.i2p.router.networkdb.kademlia
 * @since 0.9.2 in i2psnark, moved to core in 0.9.10
 */
public interface KBucket<T extends SimpleDataStructure> {

    /** 
     * Lowest order high bit for difference keys.
     * The lower-bounds distance of this bucket is 2**begin.
     * If begin == 0, this is 43the closest bucket.
     */
    public int getRangeBegin();

    /**
     * Highest high bit for the difference keys.
     * The upper-bounds distance of this bucket is (2**(end+1)) - 1.
     * If begin == end, the bucket cannot be split further.
     * If end == (numbits - 1), this is the furthest bucket.
     */
    public int getRangeEnd();

    /**
     * Number of keys already contained in this kbucket
     */
    public int getKeyCount();

    /**
     * Add the peer to the bucket
     *
     * @return true if added
     */
    public boolean add(T key);

    /**
     * Remove the key from the bucket
     * @return true if the key existed in the bucket before removing it, else false
     */
    public boolean remove(T key);
    
    /**
     *  Update the last-changed timestamp to now.
     */
    public void setLastChanged();

    /**
     *  The last-changed timestamp
     */
    public long getLastChanged();

    /**
     * Retrieve all routing table entries stored in the bucket
     * @return set of Hash structures
     */
    public Set<T> getEntries();

    public void getEntries(SelectionCollector<T> collector);

    public void clear();
}

详细笔记

概念介绍

首先,想要了解kbucket是什么意思需要了解kademilia算法,它是p2p数据库相关的算法,用来分布式存储一定的信息。想哟具体了解更多可以通过一下链接来了解kademilia算法、分布式存储

简单来讲就是用k-bucket来存储某些信息,这些信息可以理解成地址信息,每个k-bucket装的都是与本节点距离相等的节点的信息

这里的节点的距离的意思是,我们用一组01序列来表示节点信息,routerid,节点之间的距离是抑或距离 xor,这个是一个单独的异或程序来计算,下边是比较详细的解释

上文提到的距离,是学号(Node ID)之间的异或距离(XOR distance)。异或是针对yes/no或者二进制的运算.
异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1)
举2个例子:
01010000与01010010距离(即是2个ID的异或值)为00000010(换算为十进制即为2);
01000000与00000001距离为01000001(换算为十进制即为26+1,即65);
如此类推。
那通讯录是如何按距离分层呢?下面的示例会告诉你,按异或距离分层,基本上可以理解为按位数分层。设想以下情景:
以0000110为基础节点,如果一个节点的ID,前面所有位数都与它相同,只有最后1位不同,这样的节点只有1个——0000111,与基础节点的异或值为0000001,即距离为1;对于0000110而言,这样的节点归为“k-bucket 1”;
如果一个节点的ID,前面所有位数相同,从倒数第2位开始不同,这样的节点只有2个:0000101、0000100,与基础节点的异或值为0000011和0000010,即距离范围为3和2;对于0000110而言,这样的节点归为“k-bucket 2”;
……
如果一个节点的ID,前面所有位数相同,从倒数第n位开始不同,这样的节点只有2(i-1)个,与基础节点的距离范围为[2(i-1), 2i);对于0000110而言,这样的节点归为“k-bucket i”;
来源:作者:Li_Heng_lius
链接:https://www.jianshu.com/p/f2c31e632f1d

代码分析

我们看一下这个类的定义,是 interface … 我们可以知道,这个KBucket是一个容器相关的interface,因为用到了泛型(泛型介绍),而泛型一般是为了容器而设计的,这里不赘述太多,而其容器的作用与我们介绍的KBucket的作用也相互印证
由于这个是泛型接口,那么对于一个接口,其所有变量和成员方法都应该是abstract的。这样我们就看一下他们的命名,对其功能有一个大致的了解,之后我们在KBucketLmpl,java中再分析

而对于这个T,它是一个泛型,泛型在这里可以解释为就是没有指定具体类型但是给出了范围的特定的类型,他就应该是KBucket其中的元素,包含了一个节点的相关的信息

KBucketImpl.java

sourcecode

package net.i2p.kademlia;
/*
 * free (adj.): unencumbered; not under the control of others
 * Written by jrandom in 2003 and released into the public domain
 * with no warranty of any kind, either expressed or implied.
 * It probably won't make your computer catch on fire, or eat
 * your children, but it might.  Use at your own risk.
 *
 */

import java.util.Collections;
import java.util.Set;

import net.i2p.I2PAppContext;
import net.i2p.data.SimpleDataStructure;
import net.i2p.util.ConcurrentHashSet;

/**
 *  A concurrent implementation using ConcurrentHashSet.
 *  The max size (K) may be temporarily exceeded due to concurrency,
 *  a pending split, or the behavior of the supplied trimmer,
 *  as explained below.
 *  The creator is responsible for splits.
 *
 *  This class has no knowledge of the DHT base used for XORing,
 *  and thus there are no validity checks in add/remove.
 *
 *  The begin and end values are immutable.
 *  All entries in this bucket will have at least one bit different
 *  from us in the range [begin, end] inclusive.
 *  Splits must be implemented by creating two new buckets
 *  and discarding this one.
 *
 *  The keys are kept in a Set and are NOT sorted by last-seen.
 *  Per-key last-seen-time, failures, etc. must be tracked elsewhere.
 *
 *  If this bucket is full (i.e. begin == end && size == max)
 *  then add() will call KBucketTrimmer.trim() do
 *  (possibly) remove older entries, and indicate whether
 *  to add the new entry. If the trimmer returns true without
 *  removing entries, this KBucket will exceed the max size.
 *
 *  Refactored from net.i2p.router.networkdb.kademlia
 *  @since 0.9.2 in i2psnark, moved to core in 0.9.10
 */
class KBucketImpl<T extends SimpleDataStructure> implements KBucket<T> {
    /**
     *  set of Hash objects for the peers in the kbucket
     */
    private final Set<T> _entries;
    /** include if any bits equal or higher to this bit (in big endian order) */
    private final int _begin;
    /** include if no bits higher than this bit (inclusive) are set */
    private final int _end;
    private final int _max;
    private final KBucketTrimmer<T> _trimmer;
    /** when did we last shake things up */
    private long _lastChanged;
    private final I2PAppContext _context;
    
    /**
     *  All entries in this bucket will have at least one bit different
     *  from us in the range [begin, end] inclusive.
     */
    public KBucketImpl(I2PAppContext context, int begin, int end, int max, KBucketTrimmer<T> trimmer) {
        if (begin > end)
            throw new IllegalArgumentException(begin + " > " + end);
        _context = context;
        _entries = new ConcurrentHashSet<T>(max + 4);
        _begin = begin;
        _end = end;
        _max = max;
        _trimmer = trimmer;
    }
    
    public int getRangeBegin() { return _begin; }

    public int getRangeEnd() { return _end; }

    public int getKeyCount() {
        return _entries.size();
    }
    
    /**
     *  @return an unmodifiable view; not a copy
     */
    public Set<T> getEntries() {
        return Collections.unmodifiableSet(_entries);
    }

    public void getEntries(SelectionCollector<T> collector) {
        for (T h : _entries) {
             collector.add(h);
        }
    }
    
    public void clear() {
        _entries.clear();
    }
    
    /**
     *  Sets last-changed if rv is true OR if the peer is already present.
     *  Calls the trimmer if begin == end and we are full.
     *  If begin != end then add it and caller must do bucket splitting.
     *  @return true if added
     */
    public boolean add(T peer) {
        if (_begin != _end || _entries.size() < _max ||
            _entries.contains(peer) || _trimmer.trim(this, peer)) {
            // do this even if already contains, to call setLastChanged()
            boolean rv = _entries.add(peer);
            setLastChanged();
            return rv;
        }
        return false;
    }
    
    /**
     *  @return if removed. Does NOT set lastChanged.
     */
    public boolean remove(T peer) {
        boolean rv = _entries.remove(peer);
        //if (rv)
        //    setLastChanged();
        return rv;
    }
    
    /**
     *  Update the last-changed timestamp to now.
     */
    public void setLastChanged() {
        _lastChanged = _context.clock().now();
    }

    /**
     *  The last-changed timestamp, which actually indicates last-added or last-seen.
     */
    public long getLastChanged() {
        return _lastChanged;
    }
    
    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder(1024);
        buf.append(_entries.size());
        buf.append(" entries in (").append(_begin).append(',').append(_end);
        buf.append(") : ").append(_entries.toString());
        return buf.toString();
    }
}

笔记

注释

涉及的信息

  • 使用concurrenthashset来实现并发,concurrenthashmap是java的自封装类,concurrenthashset是该工程自己写的类,具体是怎么实现的现在不用管,知道是并发的集合,效率高且安全就可以了
  • 由于并发的原因,最大值k可能会超出限制。这里解释我们算法中k的含义,是每个bucket中能够存放的最大的值
  • kBucket的添加信息和删除信息都没有经过合法性验证
  • beigin和end的值都不可以改变,并且在bucket中的所有实体元素至少有一位的不同(说的是hash值那一串01序列,了解kademila算法的知道我说的是啥)
  • 在bucket的key的存放顺序并不是根据last-seen来排序
  • 如果bucket满了(满了的情况例如有: beigin == end或者 size ==max),如果满了的话,在调用add函数来添加元素,那么就会使用trimmer来删除元素,来remove老得实体

代码

记这一个文件的笔记的时候很纠结,一是本身就对这个KBucketImpl的代码看过一遍,另外是对kademila算法有一定的了解,而且这个文件写的代码并不复杂,但是因为其中有几个点我看不明白,但是想了半天没看懂,还是先留着这些问题,之后再补上。

上边我们记录来KBucket.java文件,这个文件是一个泛型类,是一个容器,一个接口,而这里我们记录的KBucketImpl。从命名上我们就能看明白这个是KBucket 类的实现.

我们通过对Kademila算法的了解知道,Kbucket是一个用来装节点信息的bucket,本来只需要一个节点信息就好了,但是我们为了增加安全性和稳定性,我们用了多个节点,所以KBucket本身是个容器,这个是通过set来完成的,是一个无序不重复的集合。

class KBucketImpl<T extends SimpleDataStructure> implements KBucket<T> {
    /**
     *  set of Hash objects for the peers in the kbucket
     */
    private final Set<T> _entries;
    /** include if any bits equal or higher to this bit (in big endian order) */
    private final int _begin;
    /** include if no bits higher than this bit (inclusive) are set */
    private final int _end;
    private final int _max;
    private final KBucketTrimmer<T> _trimmer;
    /** when did we last shake things up */
    private long _lastChanged;
    private final I2PAppContext _context;
  • T 是泛型,是simpledatastructure的子类,注释中表示是hash类
  • _entries是集合,存放一个个hash类做元素,每个元素存储相关信息
  • 至于这个_begin和_end,是常量,作用存疑 应该是不同的高位低位
  • _max 是常量,是每个bucket能存放的最大数量
  • trimmer是用来修改的
  • _lastchanged记录上一次修改的时间
  • _context作用存疑

方法

  • 构造函数 注意_entitie是一个set集合,是使用concurrentHashset来构造的,并且构造的size比最大值还大4,因为这是一个并发以及其他操作等原因,会比最大值还要打一点,所以我们需要超出max一点
  • 三个get函数,注意getEntries函数的返回值是一个set集合,为了安全性,并不是返回原集合,而是调用了Collection.unmodifiableSet方法,返回一个不可以修改的集合
  • add() 和 remove():增删的功能函数,具体的操作看concurrentset来了解

下我们需要看I2PContext以及ConcurrentSet两个文件

你可能感兴趣的:(i2p)