相对平均分布

有一个项目用来负责调度集群中的"cron任务",比如一个application中可以配置N个定时任务,这些任务信息最终注册到zookeeper上,并开发了一系列代码用于维护这些任务的"活性";当applicaton中一个server故障,那么这个server上接管的任务,需要迁移到其他server上,如果多个server存活的话,还需要这些任务能够"均衡"的分布.
其中"负载均衡",很好理解,比如有6个任务,3个server,那么就需要每个server上尽可能的运行2个任务;其实这个事情想起来很简单,但是做起来似乎有些不得不考虑的问题:
1) "相对平均"怎么设计
2) 迁移任务时,是否会丢失任务的触发时机;比如一个任务凌晨3点执行,刚好此时运行了一次"均衡",任务在原来的server上没有触发,在新的server上又过了时间..
3) 迁移任务时,还需要考虑"最少移动"次数,不能大面积迁移任务;只能从"负载高"的server上迁移到"负载低"的.

例如:
sid1: w1 w2 w3 w4
sid2: w5
sid3:w6
期望迁移之后:
sid1:w1 w2
sid2:w5 w3
sid3:w4 w6

而不是(这种结果迁移的面积太大,只需要把"多余"的任务迁移出去即可,而不是重新洗牌再均衡)
sid1:w6 w5
sid2:w2 w3
sid3:w1 w4

经过提取,"相对平均"的设计代码如下,仅作备忘:
Java代码

package com.test.demo.zookeeper;  

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;  

public class WorkersBalanceMain {
    private List<String> servers = new ArrayList<String>();
    private Map<String, List<String>> current = new HashMap<String, List<String>>();
    private Set<String> workers = new HashSet<String>();  

    public static void main(String[] args) {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line;
        Set<String> servers = new HashSet<String>();
        WorkersBalanceMain balancer = new WorkersBalanceMain();
        try {
            while ((line = br.readLine()) != null) {
                if (line.startsWith("addWorker")) {
                    balancer.addWorkers(line);
                } else if (line.startsWith("addServer")) {
                    balancer.addServers(line);
                } else {
                    System.out.println("???");
                    continue;
                }
                balancer.rebalance();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("--END---");
    }  

    public void addServers(String source) {
        int index = source.indexOf(" ");
        if (index == -1) {
            return;
        }
        String[] values = source.substring(index + 1).split(" ");
        if (values == null || values.length == 0) {
            return;
        }
        for (String server : values) {
            servers.add(server);
            if(current.get(server) == null){
                current.put(server,new ArrayList<String>());
            }
        }
    }  

    public void addWorkers(String source) {
        int index = source.indexOf(" ");
        if (index == -1) {
            return;
        }
        String[] values = source.substring(index + 1).split(" ");
        if (values == null || values.length == 0) {
            return;
        }
        //当有新的worker提交时,将咱有一台机器接管
        String sid = servers.get(0);
        List<String> sw = current.get(sid);
        if(sw == null){
            current.put(sid,new ArrayList<String>());
        }
        for (String worker : values) {
            workers.add(worker);
            sw.add(worker);
        }  

    }  

    public void rebalance() {
        try {
            if (workers.isEmpty()) {
                return;
            }
            for (String sid : servers) {
                if (current.get(sid) == null) {
                    current.put(sid, new ArrayList<String>());
                }
            }
            //根据每个sid上的worker个数,整理成一个排序的map
            TreeMap<Integer, List<String>> counterMap = new TreeMap<Integer, List<String>>();
            for (Map.Entry<String, List<String>> entry : current.entrySet()) {
                int total = entry.getValue().size();
                List<String> sl = counterMap.get(total);
                if (sl == null) {
                    sl = new ArrayList<String>();
                    counterMap.put(total, sl);
                }
                sl.add(entry.getKey());//sid
            }
            int totalWorkers = workers.size();
            int totalServers = current.keySet().size();
            int avg = totalWorkers / totalServers;//每个server实例可以接管任务的平均数
            while (true) {
                Map.Entry<Integer, List<String>> gt = counterMap.higherEntry(avg);  //大于平均数的列表, >= avg + 1
                Map.Entry<Integer, List<String>> lt = counterMap.lowerEntry(avg); //与平均数差值为2的 <= arg  - 1
                //允许任务个数与avg上线浮动1各个,不是绝对的平均  

                if (gt == null || lt == null) {
                    break;
                }
                Integer gtKey = gt.getKey();
                Integer ltKey = lt.getKey();
                if (gtKey - ltKey < 2) {
                    break;
                }
                if (gt.getValue().size() == 0) {
                    counterMap.remove(gt.getKey());
                }
                if (lt.getValue().size() == 0) {
                    counterMap.remove(lt.getKey());
                }
                Iterator<String> it = gt.getValue().iterator(); //sid列表
                while (it.hasNext()) {
                    String _fromSid = it.next();
                    List<String> _currentWorkers = current.get(_fromSid);
                    if (_currentWorkers == null || _currentWorkers.isEmpty()) {
                        it.remove();
                        current.remove(_fromSid);
                        continue;
                    }
                    List<String> _ltServers = lt.getValue();
                    if (_ltServers.isEmpty()) {
                        counterMap.remove(ltKey);
                        break;
                    }
                    //取出需要交换出去的任务id
                    int _currentWorkersSize = _currentWorkers.size();
                    String _wid = _currentWorkers.get(_currentWorkersSize - 1);
                    String _toSid = _ltServers.get(0);
                    //从_fromSid的worker列表中移除低workerId
                    //注意:移除最后一个,和_ltWorkers.add(_wid)对应,_ltWorkers将新任务添加到list的尾部
                    //即从尾部移除,从尾部添加,基本保证"原任务,最少迁移次数"
                    _currentWorkers.remove(_currentWorkersSize - 1);
                    it.remove();
                    _ltServers.remove(0);
                    //将此workerId添加到_toSid的worker列表中
                    List<String> _ltWorkers = current.get(_toSid);
                    if (_ltWorkers == null) {
                        _ltWorkers = new ArrayList<String>();
                        current.put(_toSid, _ltWorkers);
                    }
                    _ltWorkers.add(_wid);
                    //将gt的key降低一个数字
                    List<String> _next = counterMap.get(gtKey - 1);
                    if (_next == null) {
                        _next = new ArrayList<String>();
                        counterMap.put(gtKey - 1, _next);
                    }
                    _next.add(_fromSid);
                    //将lt的key提升一个数字
                    List<String> _prev = counterMap.get(ltKey + 1);
                    //从lt的countMap中移除,因为它将被放置在key + 1的新位置
                    Iterator<String> _ltIt = _ltServers.iterator();
                    while (_ltIt.hasNext()) {
                        if (_ltIt.next().equalsIgnoreCase(_toSid)) {
                            _ltIt.remove();
                            break;
                        }
                    }
                    if (_prev == null) {
                        _prev = new ArrayList<String>();
                        counterMap.put(ltKey + 1, _prev);
                    }
                    _prev.add(_toSid);
                }
            }
            //dump info
            for (Map.Entry<String, List<String>> entry : current.entrySet()) {
                System.out.println("Sid:" + entry.getKey());
                System.out.println(entry.getValue().toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }  

    }  

}

原文链接:[http://wely.iteye.com/blog/2228792]

时间: 2024-11-10 05:22:43

相对平均分布的相关文章

Word2007表格:平均分布行列的技巧

  在前面学习Word2007表格的过程中,使用最多的功能应该算"表格工具"功能区中的"布局"选项卡了,不仅可以对行列的插入.删除还可以设置行列的尺寸.其中用户可以根据实际需要在表格总尺寸不改变的情况下,平均分布行与列. 第1步,打开Word2007文档窗口,在表格任意单元格中单击鼠标. 单击"分布列"按钮 第2步,在打开的"表格工具"功能区中切换到"布局"选项卡,单击"单元格大小"分组

win7简单、快速让你窗口平均分布在桌面

很多时候难免我们要用电脑对两份文件的字是不是完全一样的,或者网页上面的字和word里面的字是不是完全一样的.有的也是也有可能是攻略要怎么样操作,这时候我们都需要窗口并列,如果一直切换到这个看,切换到那个看下,非常麻烦.下面我们说下在win7中有哪些方法能够快速实现吧! 1 鼠标拖动 将打开的一个Word文档窗口,将鼠标放到窗口上部空白处,将其拖放到桌面屏幕的左边,它会自动占据半个桌面屏幕,同样将鼠标放到打开的网页窗口上部空白处,将其拖放到桌面屏幕的右边,它会自动占据半个屏幕,两个窗口就这样轻松地

网站外链平均分布利于节省资源提升整站权重

内容为王外链为皇的时代虽然已经过了,虽然这几年搜索引擎也不断在进步,把外链的影响因素不断降低,但目前来看外链对网站排名还是起着决定性的影响.特别是在网站的前期对排名更是起着决定性的作用,当然外链的重要性已经不用我再来多讲了,相信大家都清楚,今天我要讲的是怎么把外链用好,怎么样把最小的资源价值最大人,比如一个关键词只需要十条外链就能进入前三名,而你却给它做一百条外链,那多余的90条就被浪费了,如何把这浪费的资源盘活价值最大化是我们每个人都需要做的. 其实现在很多人在做外链的时候陷入一个极大的误区,

【转载】相对平均分布

本文转载自http://shift-alt-ctrl.iteye.com/blog/1961598   有一个项目用来负责调度集群中的"cron任务",比如一个application中可以配置N个定时任务,这些任务信息最终注册到zookeeper上,并开发了一系列代码用于维护这些任务的"活性";当applicaton中一个server故障,那么这个server上接管的任务,需要迁移到其他server上,如果多个server存活的话,还需要这些任务能够"均衡

让win7窗口快速平均分布在桌面

  1.鼠标拖动 将打开的一个Word文档窗口,将鼠标放到窗口上部空白处,将其拖放到桌面屏幕的左边,它会自动占据半个桌面屏幕,同样将鼠标放到打开的网页窗口上部空白处,将其拖放到桌面屏幕的右边,它会自动占据半个屏幕,两个窗口就这样轻松地占据着屏幕的一左一右,非常方便. 2.键盘操作 其实也可以用键盘来实现,先打开一个Word文档窗口,然后同时按下"WIN键+左方向键",然后再打开一个网页窗口,然后同时按下"WIN+右方向键",两个窗口也"一分为二"

在DB2优化器中使用分布统计信息

本文配套源码 简介 为了执行查询或 DML 语句(INSERT.UPDATE.DELETE),DB2 必须创建一个访问计划(access plan).访问计划定义按什么顺序访问表,使用哪些索引,以及用何种连接(join)方法来关联数据.好的访问计划对于 SQL 语句的快速执行至关重要.DB2 优化器可以创建访问计划.这是一种基于成本的优化器,这意味着它是根据表和索引的相关统计信息来作出决策的.DB2 在生成统计信息时,不但能提供基本统计信息,还允许创建所谓的分布统计信息.不但数据库管理员要理解分

任意分布的随机数的产生方法—VC程序实现方法

摘要: 随机数在实际运用中非常之多,如游戏设计,信号处理,通常我们很容易得到平均分布的随机数.但如何根据平均分布的随机数进而产生其它分布的随机数呢?本文提出了一种基于几何直观面积的方法,以正态分布随机数的产生为例讨论了任意分布的随机数的产生方法. 正文: 一.平均分布随机数的产生 大家都知道,随机数在各个方面都有很大的作用,在vc的环境下,为我们提供了库函数rand()来产生一个随机的整数.该随机数是平均在0~RAND_MAX之间平均分布的,RAND_MAX是一个常量,在VC6.0环境下是这样定

PS为照片添加逼真的雪花飘飞的效果教程

原图 效果 1.打开原图,复制一层,选择图像-调整-去色. 2.图像-调整-变化,加深青色和深蓝色. 3.滤镜-杂色-添加杂色,数量:8%,分布选择:高斯分布. 4.把图层混合模式改为"柔光",不透明度为:80%,合并. 5.新建一个图层,填充黑色. 6.选择合造大小的画笔并如右图进行调整,用来制作雪花. 7.用画笔涂出雪花的形状. 8.滤镜-杂色-添加杂色,数量:4%,分布选择:平均分布,再把本图层混合模式改为"滤色". 9.滤镜-模糊-动感模糊,角度:-48度,

Photoshop制作真实黑板上的粉笔字

仅仅使用PS滤镜.蒙版和渐变来制作真实的黑板粉笔文字效果,本文作者将细节处理得十分到位,甚至考虑到 了黑板上的文字印记,可以说十分真实. 最终效果如下: 综述 在Photoshop里可以仅仅使用滤镜.蒙版和渐变轻松地创造令人惊叹的真实材质效果,本教程就是这真实的一例. 第一步:新建文档,我使用的大小是1000×609.使用暗绿到中绿的径向渐变,填充背景图层. 效果如下: 第二步:给黑板添加噪点:"滤镜->杂色->添加杂色".设置数量为1.6%. 分布方式为平均分布,并勾选单