• rpc之负载均衡


    使用集群,比如zk来控制注册中心,当一个服务有多个请求地址的时候,会返回多个地址。

    那么就需要负载均衡来控制我们要请求哪台机器来得到请求。

    方案一:随机

    传入key值和key所包含的ip地址值,该地址值存入TreeSet中(有序存储)

    获得TreeSet的长度,然后随机得到其索引,挑出随机的一个。

     public String route(String serviceKey, TreeSet<String> addressSet) {
            // arr
            String[] addressArr = addressSet.toArray(new String[addressSet.size()]);
    
            // random
            String finalAddress = addressArr[random.nextInt(addressSet.size())];
            return finalAddress;
        }

    方案二:轮询

     TreeSet中的地址值存入一个数组中,并设置一个map集合来记录该函数调用了几次,每次调用,就将索引加1,然后返回该索引的地址值。这样就会按照TreeSet中的顺序依次选取请求地址。

    private ConcurrentHashMap<String, Integer> routeCountEachJob = new ConcurrentHashMap<String, Integer>();
        private long CACHE_VALID_TIME = 0;
        private int count(String serviceKey) {
            // cache clear
            if (System.currentTimeMillis() > CACHE_VALID_TIME) {
                routeCountEachJob.clear();
                CACHE_VALID_TIME = System.currentTimeMillis() + 24*60*60*1000;//一天的时间
            }
    
            // count++
            Integer count = routeCountEachJob.get(serviceKey);
            count = (count==null || count>1000000)?(new Random().nextInt(100)):++count;  // 初始化时主动Random一次,缓解首次压力
            routeCountEachJob.put(serviceKey, count);
            System.out.println("count:"+count);
            return count;
        }
    
        @Override
        public String route(String serviceKey, TreeSet<String> addressSet) {
            // arr
            String[] addressArr = addressSet.toArray(new String[addressSet.size()]);
    
            // round
            int i = count(serviceKey) % addressArr.length;
            System.out.println(i);
            String finalAddress = addressArr[i];
            return finalAddress;
        }

    方案三: LRU(最近最少使用调度算法)

    每次使用了每个节点的时候,就将该节点放置在最后面,这样就保证每次使用的节点都是最近最久没有使用过的节点,当节点数大于最大空间的时候,就直接将前面的节点删掉。

    实现:使用LinkedHashMap来实现。它内部有一个双向链表在维护

    public String doRoute(String serviceKey, TreeSet<String> addressSet) {
    
            // cache clear
            if (System.currentTimeMillis() > CACHE_VALID_TIME) {
                jobLRUMap.clear();
                CACHE_VALID_TIME = System.currentTimeMillis() + 1000*60*60*24;//一天
            }
    
            // init lru
            LinkedHashMap<String, String> lruItem = jobLRUMap.get(serviceKey);
            if (lruItem == null) {
                /**
                 * LinkedHashMap
                 *      a、accessOrder:ture=访问顺序排序(get/put时排序)/ACCESS-LAST;false=插入顺序排期/FIFO;
                 *      b、removeEldestEntry:新增元素时将会调用,返回true时会删除最老元素;可封装LinkedHashMap并重写该方法,比如定义最大容量,超出是返回true即可实现固定长度的LRU算法;
                 */
                lruItem = new LinkedHashMap<String, String>(16, 0.75f, true){
                    @Override
                    protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
                        if(super.size() > 3){
                            return true;
                        }else{
                            return false;
                        }
                    }
                };
                jobLRUMap.putIfAbsent(serviceKey, lruItem);
            }
    
            // put
            for (String address: addressSet) {
                if (!lruItem.containsKey(address)) {
                    lruItem.put(address, address);
                }
            }
    
            // load
            String eldestKey = lruItem.entrySet().iterator().next().getKey();
            String eldestValue = lruItem.get(eldestKey);//LRU算法关键体现在这里,实现了固定长度的LRU算法
            return eldestValue;
        }

    方案四:LFU(访问最频繁的使用概率也最高),因此,将使用最频繁的放在最后面使用,保证了使用不频繁的也能使用上

    hashmap的存放是无序的。

    public String doRoute(String serviceKey, TreeSet<String> addressSet) {
    
            // cache clear
            if (System.currentTimeMillis() > CACHE_VALID_TIME) {
                jobLfuMap.clear();
                CACHE_VALID_TIME = System.currentTimeMillis() + 1000*60*60*24;
            }
    
            // lfu item init
            HashMap<String, Integer> lfuItemMap = jobLfuMap.get(serviceKey);     // Key排序可以用TreeMap+构造入参Compare;Value排序暂时只能通过ArrayList;
            if (lfuItemMap == null) {
                lfuItemMap = new HashMap<String, Integer>();
                jobLfuMap.putIfAbsent(serviceKey, lfuItemMap);   // 避免重复覆盖
            }
            for (String address: addressSet) {
                if (!lfuItemMap.containsKey(address) || lfuItemMap.get(address) >1000000 ) {
                    lfuItemMap.put(address, 0);
                }
            }
    //        System.out.println(lfuItemMap);
    
            // load least userd count address
            List<Map.Entry<String, Integer>> lfuItemList = new ArrayList<Map.Entry<String, Integer>>(lfuItemMap.entrySet());
            Collections.sort(lfuItemList, new Comparator<Map.Entry<String, Integer>>() {
                @Override
                public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                    return o1.getValue().compareTo(o2.getValue());
                }
            });
            System.out.println(lfuItemList);
    
            Map.Entry<String, Integer> addressItem = lfuItemList.get(0);
            String minAddress = addressItem.getKey();
            addressItem.setValue(addressItem.getValue() + 1);
    
            return minAddress;
    //        return null;
        }

    方案五:一致性哈希

    consistent hashing 是一种 hash 算法,简单的说,在移除 / 添加一个 cache 时,它能够尽可能小的改变已存在 key 映射关系,尽可能的满足单调性的要求。

    1. 每个节点设置5个虚拟节点

    2. 计算serviceKey的hash值

    3. 使用treeMap的tailMap方法返回其键大于或等于fromKey的部分视图

    4. 取视图的第一个作为服务调用的address

    private int VIRTUAL_NODE_NUM = 5;
    
        /**
         * get hash code on 2^32 ring (md5散列的方式计算hash值)
         * @param key
         * @return
         */
        private long hash(String key) {
    
            // md5 byte
            MessageDigest md5;
            try {
                md5 = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException("MD5 not supported", e);
            }
            md5.reset();
            byte[] keyBytes = null;
            try {
                keyBytes = key.getBytes("UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException("Unknown string :" + key, e);
            }
    
            md5.update(keyBytes);
            byte[] digest = md5.digest();
    
            // hash code, Truncate to 32-bits
            long hashCode = ((long) (digest[3] & 0xFF) << 24)
                    | ((long) (digest[2] & 0xFF) << 16)
                    | ((long) (digest[1] & 0xFF) << 8)
                    | (digest[0] & 0xFF);
    
            long truncateHashCode = hashCode & 0xffffffffL;
            return truncateHashCode;
        }
    
        public String doRoute(String serviceKey, TreeSet<String> addressSet) {
    
            // ------A1------A2-------A3------
            // -----------J1------------------
            TreeMap<Long, String> addressRing = new TreeMap<Long, String>();
            for (String address: addressSet) {
                for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {
                    long addressHash = hash("SHARD-" + address + "-NODE-" + i);
                    addressRing.put(addressHash, address);
                }
            }
            //TreeMap的存放是根据addressHash值排序
    
    
            long jobHash = hash(serviceKey);
            SortedMap<Long, String> lastRing = addressRing.tailMap(jobHash);
            //将addressHash值大于jobHash值的adress都取出来
    //        System.out.println(lastRing);
            if (!lastRing.isEmpty()) {
                //如果这个地址不为空,就返回这个的第一个
                return lastRing.get(lastRing.firstKey());
            }
    //        System.out.println(lastRing.firstKey());
            //返回没有减少的地址的第一个
            return addressRing.firstEntry().getValue();
        }
  • 相关阅读:
    vue(30)vuex使用子模块
    Python写一个简单的控制台输入输出并打包为exe文件
    使用npm安装的方式使用Arcgisapi
    跨域的理解
    vue中使provide中的数据变为响应式
    ArcObjects或者ArcEngine的C#版本在读取shp文件的时候报错:HRESULT:0x80040228
    C++ 新标准生成随机数
    Arcgisapi for js 4.x使用query/goto/PopupTemplate
    Arcgisapi for js 4.x加载Arcgis serve发布的矢量切片
    abp 指定 AppService中某个方法不是用驼峰命名(CamelCase)返回
  • 原文地址:https://www.cnblogs.com/lovejune/p/12464752.html
Copyright © 2020-2023  润新知