一致性Hash原理和时钟同步

523 阅读3分钟

这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战

一致性Hash算法

Hash冲突解决

开放寻址法

向前或向后找空闲位置存放

拉链法

在数组存储位置放一个链表

多哈希法

通过多个哈希算法进行哈希

建立公共溢出区

将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表

应用场景

  • 请求负载均衡

    nignx的ipHash策略

  • 分布式存储

    mysql, mongodb 或redis分布式存储

存在的问题

负载均衡场景时,如果服务器数目发生变化,session存储就会出现问题。

一致性Hash原理

顺时针将0-2的32次方-1的数编造成一个环, 将节点哈希的定位到哈希环, 对请求方进行哈希定位到哈希环,将请求落在按顺时针最近的节点。

节点缩容后, 原来路由到缩容节点的请求会路由在下一个节点上,只是这小部分收到影响, 避免了大量请求迁移。

节点扩容后, 原来路由到下个节点的请求会有一部分路由到新节点上, 只是这小部分收到影响, 避免了大量请求迁移。

虚拟节点

一致性Hash对于节点的影响,只有一小部分数据收到影响, 有较好的容错和扩展性。但是当节点数目太少后, 可能因为节点分布不均匀造成数目倾斜问题。为了解决这个问题,引入了虚拟节点。

虚拟节点方案是: 对每一个服务节点计算多个哈希, 每个计算结果都放置一个服务节点,也就是虚拟节点。请求落在虚拟节点时也就路由到真实节点上。

代码示例

一致性Hash

public void hash(){
    String[] servers = new String[]{"192.168.0.1","192.168.0.2","192.168.0.3"};
    SortedMap<Integer, String> hashServerMap = new TreeMap<>();
    
    for(String server: servers){
        int serverHash = Math.abs(server.hashCode());
        hashServerMap.put(serverHash, server);
    }
     
    String[] clients = new String[]{"192.168.0.4","192.168.0.5","192.168.0.6"};
    for(String client: clients){
        int clientHash = Math.abs(client.hashCode());
        SortedMap<Integer, String> map = hashServerMap.tailMap(clientHash);
        if(map.isEmpty()){
            Integer key =   hashServerMap.firstKey();
        }else {
            Integer key = map.firstKey();
        }
    }
   
}

虚拟节点Hash

public void hash(){
    String[] servers = new String[]{"192.168.0.1","192.168.0.2","192.168.0.3"};
    SortedMap<Integer, String> hashServerMap = new TreeMap<>();
    
    int virtualCount =3;
    for(String server: servers){
        int serverHash = Math.abs(server.hashCode());
        hashServerMap.put(serverHash, server);
    	for(int i=0; i< virtualCount; i++){
             int virServerHash = Math.abs((server+"#"+i).hashCode());
        	 hashServerMap.put(virServerHash, server);
        }
    }
     
    String[] clients = new String[]{"192.168.0.4","192.168.0.5","192.168.0.6"};
    for(String client: clients){
        int clientHash = Math.abs(client.hashCode());
        SortedMap<Integer, String> map = hashServerMap.tailMap(clientHash);
        if(map.isEmpty()){
            Integer key =   hashServerMap.firstKey();
        }else {
            Integer key = map.firstKey();
        }
    }
   
}

时钟同步

分布式集群中各个服务器节点都可以连接互联网

和国家授时中心/时间服务器同步, 通过定时任务执行

ntpdate -u ntp.api.bz #从一个时间服务器同步时间

分布式集群中只有部分节点可以访问互联网

可以访问的节点通过ntpdate访问互联网,其他机器从可以访问的节点上访问

分布式节点都不能访问互联网

选取其中一个节点作为时间服务器, 其他服务器从这个节点访问

# 配置时间服务器
vim /etc/ntp.conf
# 如果有restrict default ignore, 注释掉
# 放开局域网同步功能, 172.17.0.0 是局域网网段
restrict 172.17.0.0 mask 255.255.255.0 nomodify notrap 
server 127.127.1.0
fudge 127.127.1.0
service ntpd restart
chkconfig ntpd on