原文如下地址 :

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. IPv6第二层寻址,IPv6接口要求

    1. IPv6第二层寻址 IPV6地址以两种方式与第2层地址相关.第一种方式是IPV6独有的,提供了从第2层地址构建接口ID的机制.第二种方式对IPv4和IPV6都是一样的,提供了将一个IP组播地址映 ...

  2. vuecli的使用之项目中的文件

    cli创建的项目截图 node_moudule :下载的依赖包的存储位置. public :html的地方??? src :写代码的地方 man.js :入口文件 .browserslistrc :浏 ...

  3. pyftpdlib 搭建ftp环境

    环境搭建: pythonwindows/linuxpip install pyftpdlib (安装失败请到这里下载:https://pypi.python.org/pypi/pyftpdlib/)一 ...

  4. java的selenium环境搭建

    1.下载jdk1.8   环境变量我的博客有我就不说                   selenium下载地址:http://npm.taobao.org/mirrors/selenium 2.下 ...

  5. C语言静态库与动态库(Windows下测试)

    转载于:https://zhidao.baidu.com/question/1946953913764139388.html,原文为Linux上测试,本文为在Windows上编译测试 我们通常把一些公 ...

  6. Linux系统下安装配置 OpenLDAP + phpLDAPadmin

    实验环境: 操作系统:Centos 7.4 服务器ip:192.168.3.41 运行用户:root 网络环境:Internet LDAP(轻量级目录访问协议)是一个能实现提供被称为目录服务的信息服务 ...

  7. webpack2.X、Vue学习以及将两者相结合

    在家的闲暇时间来完善自己的前端知识. 经过两三天的学习,按照webpack文档学习,vue文档学习,最后实现了两者结合的目标. webpack 按照网站上guide的流程依次学习 1.使用npm安装w ...

  8. Qt之布局管理器

    简述 Qt的布局系统提供了一个简单的和强有力的方式,来自动排列窗口子控件布局. 所有QWidget子类可以使用布局来管理他们的子控件.QWidget::setLayout()函数可以为一个控件布局.当 ...

  9. 50个经典Sql语句

    50个经典Sql语句 --1.学生表 Student(S,Sname,Sage,Ssex) --S 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别 --2.课程表  Cours ...

  10. BZOJ 2683 简单题 cdq分治+树状数组

    题意:链接 **方法:**cdq分治+树状数组 解析: 首先对于这道题,看了范围之后.二维的数据结构是显然不能过的.于是我们可能会考虑把一维排序之后还有一位上数据结构什么的,然而cdq分治却可以非常好 ...