


import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: test
 * Date: 12-5-24
 * Time: 下午5:37
 * To change this template use File | Settings | File Templates.
 */
public class ConsistencyHash {
	//TreeMap默认用key值来进行排序的，而且是升序排序
    private TreeMap<Long,Object> nodes = null;
    //真实服务器节点信息
    private List<Object> shards = new ArrayList();
    //设置虚拟节点数目，一台服务器虚拟多少台
    public int VIRTUAL_NUM = 1;

    
 
    /**
     * 初始化一致环
     */
    public void init(SeverList severlist) {
    	
         shards.add("192.168.0.0-服务器0");
         shards.add("192.168.0.1-服务器1");
         shards.add("192.168.0.2-服务器2");
         shards.add("192.168.0.3-服务器3");
         shards.add("192.168.0.4-服务器4");

        nodes = new TreeMap<Long,Object>();
        for(int i=0; i<shards.size(); i++) {
            Object shardInfo = shards.get(i);
            
            System.out.println(shardInfo);
            
            for(int j=0; j<VIRTUAL_NUM; j++) {
            	//node产生的节点数为服务器*虚拟节点数，key=shard-i-node-j和j共同产生的hash值，value=实质那台服务器
                nodes.put(hash(computeMd5("SHARD-" + i + "-NODE-" + j),j), shardInfo);
                System.out.println("SHARD-" + i + "-NODE-" + j);
                System.out.println(computeMd5("SHARD-" + i + "-NODE-" + j)+":"+hash(computeMd5("SHARD-" + i + "-NODE-" + j),j));
                
            }
        }
    }

    /**
     * 根据key的hash值取得服务器节点信息
     * @param hash
     * @return
     */
    public Object getShardInfo(long hash) {
        Long key = hash;
        //该方法调用返回此映射，其键大于或等于key的部分视图。就是返回大于或等于hash的所有key，value
        SortedMap<Long, Object> tailMap=nodes.tailMap(key);
		if(tailMap.isEmpty()) {
			//返回第一个（最小那个hash）
			key = nodes.firstKey();
		} else {
			key = tailMap.firstKey();
		}
        return nodes.get(key);
    }

    /**
     * 打印圆环节点数据
     */
     public void printMap() {
         System.out.println(nodes);
     }

    /**
     * 根据2^32把节点分布到圆环上面。
     * @param digest
     * @param nTime
     * @return
     */
     //   // 哈希算法
      public long hash(byte[] digest, int nTime) {
		long rv = ((long) (digest[3+nTime*4] & 0xFF) << 24)
				| ((long) (digest[2+nTime*4] & 0xFF) << 16)
				| ((long) (digest[1+nTime*4] & 0xFF) << 8)
				| (digest[0+nTime*4] & 0xFF);

		return rv & 0xffffffffL; /* Truncate to 32-bits */
	  }

	/**
	 * Get the md5 of the given key.
     * 计算MD5值
	 */
	 public byte[] computeMd5(String k) {
		MessageDigest md5;
		try {
			md5 = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException("MD5 not supported", e);
		}
		md5.reset();
		byte[] keyBytes = null;
		try {
			keyBytes = k.getBytes("UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("Unknown string :" + k, e);
		}

		md5.update(keyBytes);
		return md5.digest();
	 }

    
}


