当前位置 博文首页 > fareast_mzh的博客:一致性Hash, consitency hashing, 分布式

    fareast_mzh的博客:一致性Hash, consitency hashing, 分布式

    作者:[db:作者] 时间:2021-08-13 15:55

    常见的分布式协议与算法?试一下这里的代码

    *?ConsistentHashingWithoutVirtualNode.php

    <?php
    /**
     * Class ConsistentHashingWithoutVirtualNode
     * @ref: https://www.cnblogs.com/luozhiyun/p/13251892.html
     */
    class ConsistentHashingWithoutVirtualNode
    {
        /** @var array assoc */
        private $sortedMap;
    
        /** @var array index */
        private $servers;
    
        /**
         * ConsistentHashingWithoutVirtualNode constructor.
         * @param $srvList array{string}
         */
        public function __construct($srvList) {
            $this->sortedMap = array();
            $this->servers = array();
            foreach ($srvList as $srv) {
                array_push($this->servers, $srv);
                $hash = self::getHash($srv);
                $this->sortedMap[$hash] = $srv;
                printf("[%s]加入集合中, 其Hash值为%d\n", $srv, $hash);
            }
            ksort($this->sortedMap);
            printf("\n");
        }
    
        public function getServer($node) {
            $hash = self::getHash($node);
            // 得到>=该Hash值之后的所有Map
            $subMap = array();
            foreach ($this->sortedMap as $h => $s) {
                if ($h >= $hash) {
                    $subMap[$h] = $s;
                }
            }
            // 第一个Key就是顺时针过去离node最近的那个结点
            $key = key($subMap);
            // 返回对应的服务器名称
            return $subMap[$key];
        }
    
        public static function getHash(/* string */$str, $M = 249997) {
            $hash = 0;
            for($i = 0; isset($str[$i]); $i++) {
                $hash = ($hash << 4) + ord($str[$i]);
                $x = $hash & 0xf0000000;
                if ($x) {
                    $hash ^= ($x >> 24);
                    $hash &= ~$x;
                }
            }
            return $hash % $M;
        }
    }

    PHP 使用数组指针遍历数组
    ① next:将数组指针,后移一位。并返回后一位的值;没有返回false
    ② prev:将数组指针,前移一位。并返回前一位的值;没有返回false
    ③ end: 将数组指针,移至最后一位,返回最后一位的值;空数组返回false
    ④ reset:将数组指针,恢复到第一位。并返回第一位的值;空数组返回false
    ⑤ key: 返回当前指针所在位的键;超出返回null。
    ⑥ current:返回当前指针所在位的值;超出返回false。

    * index.php

    <?php
    /**
     * Created by PhpStorm.
     * User: EDZ
     * Date: 2021/5/6
     * Time: 14:37
     */
    include "ConsistentHashingWithoutVirtualNode.php";
    
    use \ConsistentHashingWithoutVirtualNode as Hashing;
    
    $servers = ["192.168.0.0:111", "192.168.0.1:111", "192.168.0.2:111",
                "192.168.0.3:111", "192.168.0.4:111"];
    $hashing = new Hashing($servers);
    
    $nodes = [
        "127.0.0.1:1111", "221.226.0.1:2222", "10.211.0.1:3333"
    ];
    foreach ($nodes as $node) {
        printf("[%s]的hash值为%s, 被路由到的节点[%s]\n",
            $node, Hashing::getHash($node), $hashing->getServer($node));
    }

    ?

        private static int getHash(String str) {
            int hash = 0, x = 0;
            for (int i = 0; i < str.length(); i++) {
                hash = (hash << 4) + str.charAt(i);
                x = hash & 0xf0000000;
                if (0 != x) {
                    hash ^= (x >> 24);
                    hash &= ~x;
                }
            }
            return hash % 249997;
        }

    这里的hash code算出来不一样,不知道哪里错了。php大数转换为float类型,还是符号、越界的问题?

    ConsistentHashingWithoutVirtualNode.java
    package com.merrytech;
    
    import java.util.SortedMap;
    import java.util.TreeMap;
    
    /**
     * 不带虚拟节点的一致性Hash算法
     * https://www.cnblogs.com/luozhiyun/p/13251892.html
     */
    public class ConsistentHashingWithoutVirtualNode
    {
        /**
         * 待添加入Hash环的服务器列表
         */
        private static final String[] servers = {"192.168.0.0:111", "192.168.0.1:111", "192.168.0.2:111",
                "192.168.0.3:111", "192.168.0.4:111"};
    
        /**
         * key表示服务器的hash值,value表示服务器的名称
         */
        private static final SortedMap<Integer, String> sortedMap = new TreeMap<>();
    
        static
        {
            for (String server : servers) {
                int hash = getHash(server);
                System.out.println("[" + server + "]加入集合中, 其Hash值为" + hash);
                sortedMap.put(hash, server);
            }
            System.out.println();
        }
    
        /**
         * 得到应当路由到的结点
         */
        private static String getServer(String node)
        {
            // 得到带路由的结点的Hash值
            int hash = getHash(node);
            // 得到大于该Hash值的所有Map
            SortedMap<Integer, String> subMap = sortedMap.tailMap(hash);
            // 第一个Key就是顺时针过去离node最近的那个结点
            Integer i = subMap.firstKey();
            // 返回对应的服务器名称
            return subMap.get(i);
        }
    
        /**
         * 常见的hash 函数, RSHash
         * https://blog.csdn.net/fareast_mzh/article/details/98481381
         * @param str string
         * @return int
         */
    //    private static int getHash(String str) {
    //        int b    = 378551;
    //        int a    = 63689;
    //        int hash = 0;
    //
    //        for(int i = 0; i < str.length(); i++) {
    //            hash = hash * a + str.charAt(i);
    //            a = a * b;
    //        }
    //
    //        return (hash & 0x7FFFFFFF);
    //    }
        private static int getHash(String str) {
            int hash = 0, x = 0;
            for (int i = 0; i < str.length(); i++) {
                hash = (hash << 4) + str.charAt(i);
                x = hash & 0xf0000000;
                if (0 != x) {
                    hash ^= (x >> 24);
                    hash &= ~x;
                }
            }
            return hash % 249997;
        }
    
        public static void main(String[] args)
        {
            String[] nodes = {"127.0.0.1:1111", "221.226.0.1:2222", "10.211.0.1:3333"};
            for (String node : nodes) {
                System.out.println("[" + node + "]的hash值为" +
                        getHash(node) + ", 被路由到结点[" + getServer(node) + "]");
            }
        }
    }
    

    再试一下带虚拟节点的

    package com.merrytech;
    
    import java.util.*;
    
    /**
     * 带虚拟节点的一致性Hash算法
     */
    public class ConsistentHashingWithVirtualNode
    {
        /**
         * 待添加入Hash环的服务器列表
         */
        private static final String[] servers = {"192.168.0.0:111", "192.168.0.1:111", "192.168.0.2:111",
                "192.168.0.3:111", "192.168.0.4:111"};
    
        /**
         * 真实结点列表,考虑到服务器上线、下线的场景,即添加、删除的场景会比较频繁,这里使用LinkedList会更好
         */
        private static final List<String> realNodes = new LinkedList<String>();
    
        /**
         * 虚拟节点,key表示虚拟节点的hash值,value表示虚拟节点的名称
         */
        private static final SortedMap<Integer, String> virtualNodes =
                new TreeMap<Integer, String>();
    
        /**
         * 虚拟节点的数目,这里写死,为了演示需要,一个真实结点对应5个虚拟节点
         */
        private static final int VIRTUAL_NODES = 5;
    
        static
        {
            // 先把原始的服务器添加到真实结点列表中
            Collections.addAll(realNodes, servers);
    
            // 再添加虚拟节点,遍历LinkedList使用foreach循环效率会比较高
            for (String str : realNodes) {
                for (int i = 0; i < VIRTUAL_NODES; i++) {
                    String virtualNodeName = str + "&&VN" + String.valueOf(i);
                    int hash = getHash(virtualNodeName);
                    System.out.println("虚拟节点[" + virtualNodeName + "]被添加, hash值为" + hash);
                    virtualNodes.put(hash, virtualNodeName);
                }
            }
            System.out.println();
        }
    
        /**
         * 得到应当路由到的结点
         */
        private static String getServer(String node)
        {
            // 得到带路由的结点的Hash值
            int hash = getHash(node);
            // 得到大于该Hash值的所有Map
            SortedMap<Integer, String> subMap = virtualNodes.tailMap(hash);
            // 第一个Key就是顺时针过去离node最近的那个结点
            Integer i = subMap.firstKey();
            // 返回对应的虚拟节点名称,这里字符串稍微截取一下
            String virtualNode = subMap.get(i);
            return virtualNode.substring(0, virtualNode.indexOf("&&"));
        }
    
        /**
         * 常见的hash 函数, RSHash
         * https://blog.csdn.net/fareast_mzh/article/details/98481381
         * @param str string
         * @return int
         */
        private static int getHash(String str) {
            int a = 63689, b = 378551, hash = 0;
            for(int i = 0; i < str.length(); i++) {
                hash = hash * a + str.charAt(i);
                a *= b;
            }
            return (hash & 0x7FFFFFFF);
        }
    
        public static void main(String[] args)
        {
            String[] nodes = {"127.0.0.1:1111", "221.226.0.1:2222", "10.211.0.1:3333"};
            for (String node : nodes) {
                System.out.println("[" + node + "]的hash值为" + getHash(node) + ", 被路由到结点["
                        + getServer(node) + "]");
            }
    
        }
    }

    ?

    1. fpm跟fcgi关系

    FastCGI接口方式采用C/S结构,可以将HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。

    当HTTP服务器遇到动态PHP程序时,交付给FastCGI进程,PHP返回的结果传给浏览器。

    FastCGI像是一个常驻(long-live)型的CGI,避免频繁fork。

    PHP-FPM 是对于 FastCGI协议的具体实现,他负责管理一个进程池,来处理来自Web服务器的请求。


    2. nginx为什么比apache快?

    Nginx采用epoll模型,异步非阻塞。一个http请求划分为多个步骤,accept(), receive(),磁盘I/O,send(), 分别注册event handler

    apache 对于请求,fork进程,CPU上下文切换


    3. 讲一下https

    传输加密、身份认证、SSL

    传输数据之前,会通过基于X.509证书对双方进行身份认证?

    客户端发起 SSL 握手消息给服务端要求连接。

    服务端将证书发送给客户端。

    客户端检查服务端证书,确认是否由自己信任的证书签发机构签发。

    服务端要求客户端发送证书,并检查是否通过验证。


    4. MySQL有哪几种索引,使用索引优缺点

    FULLTEXT,HASH,BTREE,RTREE

    优点: B+ tree的索引结构, 平衡多路查找树方式去查询,速度快; 减少访问数据库IO次数
    缺点: 索引块占磁盘空间,update操作更新索引、delete删除索引中的值 产生额外开销


    5. redis数据类型,哪些用途?

    string: incr, decr, incrby, decrby
    list: lpush, lpop, rpush, rpop, llen
    set: sadd, scard, sismember, srem
    zset: zadd, zcard, zrange
    hash: hset, hget, hmget

    redis的5种数据类型


    6. MySQL分库分表

    水平: 按照一定策略(hash、range, 时间等),将一个中的数据拆分到多个中。

    垂直: 拆字段

    Mysql数据库分库分表


    7. PHP安全问题,有哪些措施?

    防xss: strip_tags去掉传入的js代码, htmlspecialchars
    防CSRF(跨站请求伪造cross site request forgery): 校验Referrer, 源IP
    防盗链: apache .htaccess HTTP_REFERER, 使用云存储添加Expires, access key, 例如
    ? ?http://ks3-cn-beijing.ksyun.com/resource-test/crm/black_swan_20210510200138.png?Expires=1620649902&KSSAccessKeyId=AKLTzoWE5LxLRgSZ4oFYniPPug&Signature=XGEANBpAC3EGE8EJdcvACjyTFmQ%3D
    防CC: redis计数, 限制同一域名单位时间的访问次数
    防SQL注入: addslashes, stripslashes, mysql_ real _escape_string
    ?? ?去掉引号,单引号, 空格 and, or, # - **, % _,?
    ?? ?去掉 select,insert,update,delete, union, join, into, load_file,outfile


    8. jwt讲一下

    Json web token, 结构: header.payload.signature
    header = base64({"type":"JWT","alg":"HS256"})
    payload = base64(自定义要传输的json)
    secret = HMACSHA256(header + payload + secret)
    secret是保存在服务器端的私钥
    使用方式:?
    fetch('api/user/1', {
    ?? ?headers:{
    ?? ??? ?'Authorization': 'Bearer ' + token
    ?? ?}
    });


    9. websocket

    ? 单个 TCP 连接上进行全双工通讯的协议。允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。比传统的ajax轮询节省服务器资源,能够更实时地进行通讯。?HTTP请求包含较长的头部, 所以websocket节省带宽。

    websocket示例代码


    10. 常见分布式算法有哪些?

    一致性hash, gossip协议, Quorum NWR算法, PBFT算法, Pow算法

    ?

    一致哈希算法是对节点ip地址 + 端口号字符串 调用hash函数,&0x7fffffff,?2^32 进行取模运算。将节点映射到哈希环上,从而每个节点就能确定其在哈希环上的位置了。

    然后当要读取指定key(ip地址 + 端口号字符串)的值的时候,通过对key做一个hash,并确定此 key 在环上的位置,从这个位置沿着哈希环顺时针“行走”,

    遇到的第一节点就是 key 对应的节点。(有序map key大于当前这个hash值, 首个key对应的value)

    ?

    gossip协议

    当一个节点有了新数据后,这个节点变成活跃状态,并周期性地联系其他节点向其发送新数据,直到所有的节点都存储了该新数据。(这里没有代码,看不懂)

    PBFT

    拜占庭容错

    ?

    ?

    ?

    ?

    cs
    下一篇:没有了