原文如下地址 :

http://www.cnblogs.com/cenyuhai/p/3650943.html

看源码很久了,终于开始动手写博客了,为什么是先写负载均衡呢,因为一个室友入职新公司了,然后他们遇到这方面的问题,某些机器的硬盘使用明显比别的机器要多,每次用hadoop做完负载均衡,很快又变回来了。

首先我们先看HMaster当中怎么初始化Balancer的,把集群的状态穿进去,设置master,然后执行初始化。

//initialize load balancerthis.balancer.setClusterStatus(getClusterStatus());
this.balancer.setMasterServices(this);
this.balancer.initialize();

然后调用是在HMaster的balance()方法当中调用

Map<TableName, Map<ServerName, List<HRegionInfo>>> assignmentsByTable =
this.assignmentManager.getRegionStates().getAssignmentsByTable(); List<RegionPlan> plans = new ArrayList<RegionPlan>();
//Give the balancer the current cluster state.this.balancer.setClusterStatus(getClusterStatus());
//针对表来做平衡,返回平衡方案,针对全局,可能不是最优解for (Map<ServerName, List<HRegionInfo>> assignments : assignmentsByTable.values()) {
List<RegionPlan> partialPlans = this.balancer.balanceCluster(assignments);
if (partialPlans != null) plans.addAll(partialPlans);
}

可以看到它首先获取了当前的集群的分配情况,这个分配情况是根据表的 Map<TableName, Map<ServerName, List<HRegionInfo>>,然后遍历这个map的values,调用 balancer.balanceCluster(assignments) 来生成一个partialPlans,生成RegionPlan(Region的移动计划) 。

我们就可以切换到StochasticLoadBalancer当中了,这个是默认Balancer具体的实现了,也是最好的实现,下面就说说这玩意儿咋实现的。

看一下注释,这个玩意儿吹得神乎其神的,它说它考虑到了这么多因素:

* <ul> 
* <li>Region Load</li> Region的负载 
* <li>Table Load</li>  表的负载 
* <li>Data Locality</li> 数据本地性 
* <li>Memstore Sizes</li> 内存Memstore的大小 
* <li>Storefile Sizes</li> 硬盘存储文件的大小 
* </ul>

好,我们从balanceCluster开始看吧,一进来第一件事就是判断是否需要平衡

//不需要平衡就退出if (!needsBalance(new ClusterLoadState(clusterState))) {
return null;
}

平衡的条件是:负载最大值和最小值要在平均值(region数/server数)的+-slop值之间, 但是这个平均值是基于表的,因为我们传进去的参数clusterState就是基于表的。

// Check if we even need to do any load balancing// HBASE-3681 check sloppiness firstfloat average = cs.getLoadAverage(); // for logging//集群的负载最大值和最小值要在平均值的+-slop值之间int floor = (int) Math.floor(average * (1 - slop));
int ceiling = (int) Math.ceil(average * (1 + slop));
if (!(cs.getMinLoad() > ceiling || cs.getMaxLoad() < floor)) {
.....return false;
}
return true;

如果需要平衡的话,就开始计算开销了

// Keep track of servers to iterate through them.
Cluster cluster = new Cluster(clusterState, loads, regionFinder);
//计算出来当前的开销 double currentCost = computeCost(cluster, Double.MAX_VALUE);
double initCost = currentCost;
double newCost = currentCost;

for (step = 0; step < computedMaxSteps; step++) { 
//随机挑选一个"选号器" 
int pickerIdx = RANDOM.nextInt(pickers.length); 
RegionPicker p = pickers[pickerIdx]; 
//用选号器从集群当中随机跳出一对来,待处理的<server,region>对 
Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> picks = p.pick(cluster);

int leftServer = picks.getFirst().getFirst(); 
int leftRegion = picks.getFirst().getSecond(); 
int rightServer = picks.getSecond().getFirst(); 
int rightRegion = picks.getSecond().getSecond();

cluster.moveOrSwapRegion(leftServer, 
rightServer, 
leftRegion, 
rightRegion); 
//移动或者交换完之后,看看新的开销是否要继续 
newCost = computeCost(cluster, currentCost); 
// Should this be kept? 挺好,保存新状态 
if (newCost < currentCost) { 
currentCost = newCost; 
} else { 
// 操作不划算,就回退 
cluster.moveOrSwapRegion(leftServer, 
rightServer, 
rightRegion, 
leftRegion); 
}

if (initCost > currentCost) { 
//找到了满意的平衡方案 
List<RegionPlan> plans = createRegionPlans(cluster); 
return plans; 
}

上面的被我清除了细枝末节之后的代码主体,okay,上面逻辑过程如下:

1. 生成一个虚拟的集群cluster,方便计算计算当前状态的开销,其中clusterState是表的状态,loads是整个集群的状态。

// Keep track of servers to iterate through them.
Cluster cluster = new Cluster(clusterState, loads, regionFinder);
//计算出来当前的开销 double currentCost = computeCost(cluster, Double.MAX_VALUE);
double initCost = currentCost;
double newCost = currentCost;

2. 然后循环 computedMaxSteps次,随机从选出一个picker来计算平衡方案

int pickerIdx = RANDOM.nextInt(pickers.length);
RegionPicker p = pickers[pickerIdx];
//用选号器从集群当中随机跳出一对来,待处理的<server,region>对
Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> picks = p.pick(cluster);

picker是啥?这里面有三个,第一个是RandomRegionPicker是随机挑选region,这里就不详细介绍了,主要讨论后面两个;第二个LoadPicker是计算负载的,第三个主要是考虑本地性的。

给我感觉就很像ZF的摇号器一样,用哪种算法还要摇个号

pickers = new RegionPicker[] {
new RandomRegionPicker(),
new LoadPicker(),
localityPicker
};

下面我们先看localityPicker的pick方法,这个方法是随机抽选出来一个server、region,找出region的其他本地机器,然后他们返回。

  @Override
Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> pick(Cluster cluster) {
if (this.masterServices == null) {
return new Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>(
new Pair<Integer, Integer>(-1,-1),
new Pair<Integer, Integer>(-1,-1)
);
}
// Pick a random region server 随机选出一个server来 int thisServer = pickRandomServer(cluster); // Pick a random region on this server 随机选出region int thisRegion = pickRandomRegion(cluster, thisServer, 0.0f); if (thisRegion == -1) {
return new Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>(
new Pair<Integer, Integer>(-1,-1),
new Pair<Integer, Integer>(-1,-1)
);
} // Pick the server with the highest locality 找出本地性最高的目标server int otherServer = pickHighestLocalityServer(cluster, thisServer, thisRegion); // pick an region on the other server to potentially swap int otherRegion = this.pickRandomRegion(cluster, otherServer, 0.5f); return new Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>(
new Pair<Integer, Integer>(thisServer,thisRegion),
new Pair<Integer, Integer>(otherServer,otherRegion)
);
}

okay,这个结束了,下面我们看看LoadPicker吧。

  @Override
Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> pick(Cluster cluster) {
cluster.sortServersByRegionCount();
//先挑选出负载最高的serverint thisServer = pickMostLoadedServer(cluster, -1);
//再选出除了负载最高的server之外负载最低的serverint otherServer = pickLeastLoadedServer(cluster, thisServer); Pair<Integer, Integer> regions = pickRandomRegions(cluster, thisServer, otherServer);
return new Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>(
new Pair<Integer, Integer>(thisServer, regions.getFirst()),
new Pair<Integer, Integer>(otherServer, regions.getSecond()) );
}

这里的负载高和负载低是按照Server上面的region数来算的,而不是存储文件啥的,选出负载最高和负载最低的时候,又随机抽出region来返回了。

pick挑选的过程介绍完了,那么很明显,计算才是重头戏了,什么样的region会导致计算出来的分数高低呢?

3. 重点在计算函数上 computeCost(cluster, Double.MAX_VALUE) 结果这个函数也超级简单,哈哈

protected double computeCost(Cluster cluster, double previousCost) {
double total = 0; for (CostFunction c:costFunctions) {
if (c.getMultiplier() <= 0) {
continue;
} total += c.getMultiplier() * c.cost(cluster); if (total > previousCost) {
return total;
}
}
return total;
}

遍历CostFunction,拿cost的加权平均和计算出来。

那costFunction里面都有啥呢?localityCost又出现了,看来本地性是一个很大的考虑的情况。

costFunctions = new CostFunction[]{
new RegionCountSkewCostFunction(conf),
new MoveCostFunction(conf),
localityCost,
new TableSkewCostFunction(conf),
regionLoadFunctions[0],
regionLoadFunctions[1],
regionLoadFunctions[2],
regionLoadFunctions[3],
};

regionLoadFunctions = new CostFromRegionLoadFunction[] { 
new ReadRequestCostFunction(conf), 
new WriteRequestCostFunction(conf), 
new MemstoreSizeCostFunction(conf), 
new StoreFileCostFunction(conf) 
};

可以看出来,里面真正看中硬盘内容大小的,只有一个StoreFileCostFunction,cost的计算方式有些区别,但都是一个0-1之间的数字,下面给出里面5个函数都用过的cost的函数。

//cost函数double max = ((count - 1) * mean) + (total - mean);
for (double n : stats) {
double diff = Math.abs(mean - n);
totalCost += diff;
} double scaled = scale(0, max, totalCost);
return scaled; //scale函数protected double scale(double min, double max, double value) {
if (max == 0 || value == 0) {
return 0;
} return Math.max(0d, Math.min(1d, (value - min) / max));
}

经过分析吧,我觉得影响里面最后cost最大的是它的权重,下面给一下,这些function的默认权重。

RegionCountSkewCostFunction hbase.master.balancer.stochastic.regionCountCost ,默认值500

MoveCostFunction hbase.master.balancer.stochastic.moveCost,默认值是100

localityCost hbase.master.balancer.stochastic.localityCost,默认值是25
TableSkewCostFunction hbase.master.balancer.stochastic.tableSkewCost,默认值是35

ReadRequestCostFunction hbase.master.balancer.stochastic.readRequestCost,默认值是5

WriteRequestCostFunction hbase.master.balancer.stochastic.writeRequestCost,默认值是5

MemstoreSizeCostFunction hbase.master.balancer.stochastic.memstoreSizeCost,默认值是5

StoreFileCostFunction hbase.master.balancer.stochastic.storefileSizeCost,默认值是5
Storefile的默认值是5,那么低。。。可以试着提高一下这个参数,使它在计算cost消耗的时候,产生更加正向的意义,效果不好说。

4. 根据虚拟的集群状态生成RegionPlan,这里就不说了

List<RegionPlan> plans = createRegionPlans(cluster);

源码的分析完毕,要想减少存储内容分布不均匀,可以试着考虑增加一个picker,这样又不会缺少对其他条件的考虑,具体可以参考 LoadPicker,复制它的实现再写一个,在pickMostLoadedServer和pickLeastLoadedServer这两个方法里面把考虑的条件改一下,以前的条件是Integer[] servers = cluster.serverIndicesSortedByRegionCount; 通过这个来查找一下负载最高和最低的server,那么现在我们要在Cluster里面增加一个Server ---> StoreFile大小的关系映射集合,但是这里面没有,只有regionLoads,RegionLoad这个类有一个方法 getStorefileSizeMB可以获得StoreFile的大小,我们通过里面的region和server的映射 regionIndexToServerIndex来最后计算出来这个映射关系即可,这个计算映射关系个过程放在Cluster的构造函数里面。

一 Balancer regionCountCost 权重。的更多相关文章

  1. 2.【Spring Cloud Alibaba】实现负载均衡-Ribbon

    负载均衡的两种方式 如何实现负载均衡 目前已经实现让A总能找到B,如何实现负载均衡 负载均衡的两种方式 服务器端负载均衡 客户端负载均衡 使用Ribbo实现负载均衡 Ribbon是什么 ==Netfl ...

  2. Redis分布式锁 (图解-秒懂-史上最全)

    文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...

  3. hbase源码系列(一)Balancer 负载均衡

    看源码很久了,终于开始动手写博客了,为什么是先写负载均衡呢,因为一个室友入职新公司了,然后他们遇到这方面的问题,某些机器的硬盘使用明显比别的机器要多,每次用hadoop做完负载均衡,很快又变回来了. ...

  4. go微服务框架kratos学习笔记七(kratos warden 负载均衡 balancer)

    目录 go微服务框架kratos学习笔记七(kratos warden 负载均衡 balancer) demo demo server demo client 池 dao service p2c ro ...

  5. Azure Load Balancer(一) 为我们的Web项目提供负载均衡

    一,引言 上节,我们讲到使用 Azure Traffic Manager 为我们的 Demo 项目提供负载均衡,以及流量转发的功能.但是Azure 提供类似的功能的服务远远不止这一个,今天我们就来讲一 ...

  6. Azure Load Balancer(二) 基于内部的负载均衡来转发为访问请求

    一,引言 上一节,我们使用 Azure Load Balancer 类型为外部的,来转发我们的 Web 服务.今天我们看看另一种类型为 “Internal” 的 Azure Load Balancer ...

  7. 前端极易被误导的css选择器权重计算及css内联样式的妙用技巧

    记得大学时候,专业课的网页设计书籍里面讲过css选择器权重的计算:id是100,class是10,html标签是5等等,然后全部加起来的和进行比较... 我只想说:真是误人子弟,害人不浅! 最近,在前 ...

  8. SEO:权重如何做到从0到1

    SEO:权重如何做到从0到1 [写于2016年9月]我真的好久好久没到我的博客上去看过了,今天突然登上 seo.chinaz.com,搜索 dkplus.iteye.com,发现自己的博客在百度收录中 ...

  9. CSS选择器的权重与优先规则?

    我们做项目的时候,经常遇到样式层叠问题,被其他的样式覆盖,或者写的权重不高没效果,对权重没有具体的分析,做了一个总结. css继承是从一个元素向其后代元素传递属性值所采用的机制.确定应当向一个元素应用 ...

随机推荐

  1. ActiveMQ学习笔记(10)----ActiveMQ容错的连接

    1. Failover Protocol 前面讲述的都是Client配置连接到指定的broker上,但是,如果Broker的连接失败怎么办呢?此时,Client有两个选项:要么立刻死掉,要么连接到其他 ...

  2. 优动漫PAINT-超简单灌木教程

    超简单灌木教程~零基础神马的都能神还原哦! 优动漫PAINT下载:http://wm.makeding.com/iclk/?zoneid=18597 想要Get到更多有关优动漫的信息包括软件下载,可关 ...

  3. Matlab从入门到精通 Chapter5 数据可视化--

    5-1 图形绘制示例 >> x2=-17:0.02:3; >> y2=1./((x2+3).^2+1)+1./((x2+9).^2+4)+5; >> subplot ...

  4. Dapper Dapper-Extensions

    之前公司在开发项目的时候有用到过Dapper 仓储 以及IOC Castel .最近新项目上想使用上这些技术,决定新学习一下. 看了很多资料,发现几个比较容易菜鸟的我理解的,整理下来,找时间好好整理一 ...

  5. vue 所有的路由跳转加一个统一参数

    需求是什么 所有的路由跳转加一个统一的参数 实现方式 先深入理解一下router的全局前置守卫 router.beforeEach((to, from, next) => { const que ...

  6. confluence6.0.3安装文档

    一.Atlassian Confluence 6.0.3安装文档包含内容 1.wiki的安装步骤: 2.旧系统迁移中碰到的无法编辑和问题和解决方案: 3.wiki源码安装包.连接mysql用的jar包 ...

  7. mysql中如何查看某个数据库或表占用的磁盘空间

    查整个库的状态:select concat(truncate(sum(data_length)/1024/1024,2),'MB') as data_size,          concat(tru ...

  8. centos7 jumpserver 部署和使用手册(二)

    前面已经介绍了jumpserver的部署,基于这篇部署文档,下面介绍下部署完成后的的功能使用: 一.系统设置 1.1根据提供的帐号密码(admin/admin)登录jumpserver 修改 url ...

  9. [Java] 使用 Apache的 Commons-net库 实现FTP操作

    因为最近工作中需要用到FTP操作,而手上又没有现成的FTP代码.就去网上找了一下,发现大家都使用Apache的 Commons-net库中的FTPClient. 但是,感觉用起来不太方便.又在网上找到 ...

  10. hdu 4496 并查集 逆向 并查集删边

    貌似某大犇说过 正难则反,,, 题目说要对这张图进行删边,然后判断联通块的个数,那么就可以先把所有边都删掉,之后从后往前加边,若加的边两端点不在同一个联通块中, 那么此时联通快个数少一,否则不变 #i ...