前言
文章标题一开始提及到了一个令人感到有些抽象又显得有些很"大"的词,限流.其实这个词语在很多行业都可以用到,比如最近春运,各大主要城市,火车站,地铁站都要做到限流吧,避免人流量过大造成事故或间接事故,这叫人流量限流,同理也可以用在车流量上.如果基于这个背景,把这里的人群和车辆抽象为数据,对数据进行限流,就是本篇文章的主题了.可能就有人疑惑了,数据为什么要做限流,怎么做限流,有什么好处呢,带着这个疑问,仔细的阅读下文的分析吧.
数据的限流
数据的限流更让人理解的称呼应该是"数据流的限流".数据流指的就是传输中的源源不断的数据.这些数据传输会耗尽大量的网络带宽,bandwith.一台机器的网络带宽必定是有限的,如果带宽被这台机器上的某些任务用满的话,就会造成正常任务网络传输数据受到影响.如果带宽长时间的被打满之后,还会造成机器IO报警.所以限流的目的正在与此.可能造成网络带宽迅速被占满的不一定都是恶意的程序或服务,程序中一个疏忽的处理或小错误都可能会造成大规模数据的传输.所以与其去劝导用户规范写程序,还不如从系统层面进行强加管理限制,把主动权掌握在自己手中.
Hadoop的数据限流
数据限流是一涉及面很大的词,数据类型,使用场景就有很多,所以本文只分析我们所想要分析的数据限流,就是hadoop内部的限流机制.在写本文之前,我稍微搜素了一下网上关于此方面的文章,发现确实相关文章少之又少.但是作为一个大型的分布式存储系统,数据的读写操作一定是非常频繁的,所以数据的传输量一定很大.在数据量传输很大的情况下,如何保证避免出现个别服务把带宽占满的情况就显得格外重要。有一点是至少明确的,在Hadoop中跑的job的读写数据操作都需要是正常的。为了方便下文的描述,我们可以称此类型的数据传输为"普通任务数据流",既然这里已经先定义一个了,那就必然还存在另外的数据流传输,而且类型比想象中的多了许多:
1.Balancer数据平衡数据流
2.Fsimage镜像文件的上传下载数据流传输
3.VolumeScanner磁盘扫描的数据读操作的数据数据传输
看完这3个结果,第一个Balance的数据流还是能想得到的,后面2个如果你没有从源码中进行分析,很容易会忽略掉.因为以上列举的3种属于非正常业务的数据流传输,是在系统自身内部进行的,所以hadoop对这3种操作做了限流操作.限流相关的类名叫做DataTransferThrottler,限流关系图结构如下:
DataTransferThrottler限流原理
data-transfer数据传输的限流原理在DataTransferThrottler中有着非常巧妙的设计.先看一下这个类的源码注释:
/**
* a class to throttle the data transfers.
* This class is thread safe. It can be shared by multiple threads.
* The parameter bandwidthPerSec specifies the total bandwidth shared by
* threads.
*/
public class DataTransferThrottler {
通过传入指代的bandwidthPerSec带宽速率来作为一个最大的限制值,在限制类的作用下,带宽的平均速度将会控制在这个速率之下.在这个类中,定义了下面几个变量:
private final long period; // period over which bw is imposed
private final long periodExtension; // Max period over which bw accumulates.
private long bytesPerPeriod; // total number of bytes can be sent in each period
private long curPeriodStart; // current period starting time
private long curReserve; // remaining bytes can be sent in the period
private long bytesAlreadyUsed;
在DataTransferThrottler类中的主要限流思想是通过单位时间段内限制指定字节数的方式来控制平均传输速度,如果发现IO传输速度过快,超过规定时间内的带宽限定字节数,则会进行等待操作,等待下一个允许带宽传输周期的到来,这个用结构图表示如下:
所以每个period周期内的可允许传输字节数就是很关键的变量,他是根据传入的带宽上限值进行转换.
/**
* Constructor
* @param period in milliseconds. Bandwidth is enforced over this
* period.
* @param bandwidthPerSec bandwidth allowed in bytes per second.
*/
public DataTransferThrottler(long period, long bandwidthPerSec) {
this.curPeriodStart = monotonicNow();
this.period = period;
//将带宽按照周期做比例转化
this.curReserve = this.bytesPerPeriod = bandwidthPerSec