本文转载自http://shift-alt-ctrl.iteye.com/blog/1961598
有一个项目用来负责调度集群中的"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
经过提取,"相对平均"的设计代码如下,仅作备忘:
- 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();
- }
- }
- }
相关推荐
在企业性质方面,外商独资企业的总经理层薪酬平均值最高,达到了826,357元,而民营企业该职位的薪酬平均值则相对较低,为483,006元。 报告还展示了行业的薪酬结构分布,包括总经理层、总监层、经理层、主管层、专业...
在热门租赁小区中,一房户型的租售比相对较高,平均在1.4%以上,个别小区租售比甚至超过2%。一房热门租赁小区的房龄普遍较老,超过26年,且有超六成热门小区位于轨道交通便利的区域。 以上这些数据和分析报告提供的...
与此相对,办公用房的供应量环比显著减少,即所谓的“腰斩”,但即便如此,其供应量还是保持在了年内月度平均水平之上。 在区域分布上,9月份各个行政区的商办物业供应差异显著。以浦东新区为例,该区的商业用房...
- 商业用房的成交量在各区分布相对平均,但浦东新区和青浦区分别占据第一和第二位,青浦区的成交量超过了6700平方米。 4. 成交均价变化: - 商办物业的成交均价呈现下降趋势,但大部分热销商业项目的价格表现良好...
3. 土地供应分布:9月份的经营性用地供应分布相对平均,各类用地均有供应。其中,保障性用地位列供应首位,当月有7幅地块集中入市;商品住宅用地供应排名第二,供应地块的平均体量接近十万方;含住宅的综合用地供应...
报告提供了25分位值、中位值、75分位值、90分位值和平均值等数据,帮助企业和个人了解薪酬水平在不同百分比的位置。这些数据对于薪资谈判和市场定位具有参考意义。 7. 行业分布和岗位分类: 报告涉及了多个部门的...
例如,人力资源总监一职在外商独资企业、外商合资企业、民营企业和国有企业中的薪酬分布情况,包括25分位值、中位值、75分位值、90分位值以及平均值。这反映了不同性质公司对相同职位的薪酬定位差异,有利于对比不同...
此外,还引入了招聘难度的概念,通过平均招聘花费时间的不同等级来衡量各岗位的招聘难度,从“容易”到“极难”,分为五个等级,为招聘策略的制定提供了参考。 报告覆盖了广泛的行业范围,从信息技术与互联网、电子...
同时,报告也展示了不同公司类型(外商独资企业、外商合资企业、民营企业、国有企业)对于同一职位的薪酬水平分布,包括25分位值、中位值、75分位值、90分位值和平均值等指标。 5. 招聘难度与薪酬关系:报告中对...
报告中使用了分位值(P25、P50、P75、P90)和平均值等统计学指标来描述薪酬数据的分布情况。分位值可以反映在不同薪酬水平下,员工的相对位置,有助于企业了解特定职位薪酬的市场定位。 7. 薪酬调查的版权与使用...
首先,本月出让的地块较多地分布在地价水平相对较低的外围区域;其次,缺少高价地块支撑整体均价,导致各类用地的价格水平均有不同程度的下降。就工业用地而言,尽管土地供应量增加且市场活跃度提高,但并未带动价格...
从给定文件的【标题】、...通过这份薪酬报告系列的内容,我们可以深刻理解薪酬结构的组成要素,以及不同行业和岗位中薪酬水平的分布情况,这对于人力资源管理、职业规划和个人薪酬谈判等方面都具有重要的指导作用。
招聘难度的评估也是薪酬调研中的一部分,人力资源总监职位的招聘难度被评为四星半(★★★★☆),表示招聘这一职位相对较为困难。 二、薪酬数据的解读与应用 1. 薪酬水平分析 通过对不同岗位的薪酬水平分析,企业...