Flume-NG源码阅读之AvroSink
org.apache.flume.sink.AvroSink是用来通过网络来传输数据的,可以将event发送到RPC服务器(比如AvroSource),使用AvroSink和AvroSource可以组成分层结构。它继承自AbstractRpcSink extends AbstractSink implements Configurable这跟其他的sink一样都得extends AbstractSink implements Configurable,所以重点也在confgure、start、process、stop这四个方法,实现了initializeRpcClient(Properties props)方法。
一、configure(Context context)方法,先获取配置文件中的主机hostname和端口port;设置clientProps的属性hosts=h1,hosts.h1=hostname:port;然后将配置信息中的所有信息放入clientProps中;获取cxnResetInterval表示重复建立连接的时间间隔,默认是0就是不重复建立连接。
二、start()方法是调用createConnection()建立连接,如果出现异常就调用destroyConnection()掐断连接,避免资源泄漏。createConnection()方法主要是初始化client = initializeRpcClient(clientProps)以及创建一个线程,并执行在给定延迟cxnResetInterval后执行一次销毁链接destroyConnection(),由于默认cxnResetInterval=0,所以是不会执行这个线程的。这点不是很明白,为什么要销毁???initializeRpcClient(clientProps)方法会根据配置文件中的信息进行构造相应的RpcClient:首先会获取"client.type"参数指定的类型可用的有四种(NettyAvroRpcClient(如果没有"client.type"则使用这个作为默认Client)、FailoverRpcClient、LoadBalancingRpcClient、ThriftRpcClient),实例化之后需要对其在进行必要的配置执行client.configure(properties)进行配置:
(1)NettyAvroRpcClient.configure(Properties properties)方法首先会获取锁,检查connState连接状态要保证是没有配置过的;其次获取"batch-size"设置batchSize,如果配置的小于1则使用默认值100;获取“hosts”,如果配置了多个hosts则只使用第一个;获取"hosts."前缀,如果有多个则使用第一个,再解析出hostname和port,构建一个InetSocketAddress的对象address;获取连接超时时间"connect-timeout",设置connectTimeout,如果配置的小于1000则使用默认值20000,单位是ms;获取相应时间"request-timeout",设置requestTimeout,如果配置的小于1000,则使用默认值20000,单位ms;获取压缩类型"compression-type",如果有配置压缩还需要获取压缩的等级compressionLevel;最后调用connect()链接RPC服务器。
实际的链接在connect(long timeout, TimeUnit tu)方法中,先构造一个线程池callTimeoutPool;然后根据是否有压缩构造相应的工厂类CompressionChannelFactory(有压缩配置)或者NioClientSocketChannelFactory(无压缩配置);构造一个
NettyTransceiver(this.address,socketChannelFactory,tu.toMillis(timeout))收发器对象transceiver;根据transceiver返回一个avroClient;最后设置链接状态为READY。
(2)FailoverRpcClient.configure(Properties properties)方法会调用configureHosts(Properties properties)方法,这个方法会获取配置文件中的host列表hosts;获取最大尝试次数"max-attempts",设置maxTries,默认是hosts的大小;获取批量大小
(3)LoadBalancingRpcClient.configure(Properties properties)会获取配置文件中的host列表hosts,且不允许少于两个,否则爆异常;获取主机选择器"host-selector",有两种内置的选择器:LoadBalancingRpcClient.RoundRobinHostSelector和LoadBalancingRpcClient.RandomOrderHostSelector,默认是ROUND_ROBIN(即RoundRobinHostSelector)轮询的方式(也可以自定义,要实现LoadBalancingRpcClient.HostSelector接口);获取"backoff",设置backoff(是否使用推迟算法,就是sink.process出问题后对这个sink设置惩罚时间,在此期间不再认为其可活动)的boolean值(默认false就是不启用);获取最大推迟时间"maxBackoff",设置maxBackoff;然后根据选择器是ROUND_ROBIN还是RANDOM选择对应的类并实例化selector,最后设置主机selector.setHosts(hosts)。
这两个内置选择器:RoundRobinHostSelector实际使用的是RoundRobinOrderSelector;RandomOrderHostSelector实际使用的是RandomOrderSelector,这两个都在Flume-NG源码阅读之SinkGroups和SinkRunner 这篇文章中有介绍,这里不再说明。
(4)ThriftRpcClient.configure(Properties properties)会获取状态锁stateLock.lock();获取配置文件中的host列表中的第一个,只需要一个;获取批量大小"batch-size",设置batchSize,如果配置的小于1则使用默认大小100;获取主机名hostname和端口port;获取响应时间requestTimeout,如果小于1000设置为默认的20000ms;获取连接池大小"maxConnections",设置connectionPoolSize,如果大小小于1则设置为默认的值5;创建连接池管理对象connectionManager= new ConnectionPoolManager(connectionPoolSize);设置连接状态为READY,connState = State.READY;最后状态锁解锁stateLock.unlock()。
这四个Client都是extends AbstractRpcClient implements RpcClient。
三、process()方法,代码如下:
public Status process() throws EventDeliveryException {
Status status = Status.READY;
Channel channel = getChannel(); //获得channel
Transaction transaction = channel.getTransaction(); //创建事务 try {
transaction.begin(); //事务开始 verifyConnection(); //确保存在链接且处于活动状态,如果链接处于非活动状态销毁并重建链接 List<Event> batch = Lists.newLinkedList(); for (int i = 0; i < client.getBatchSize(); i++) { //保证这批次的event数量不可能超过客户端批量处理的最大处理数量
Event event = channel.take(); if (event == null) { //表示channel中没有数据了
break;
} batch.add(event); //加入event列表
} int size = batch.size(); //获取这批次取得的event的数量
int batchSize = client.getBatchSize(); //获取客户端可以批量处理的大小 if (size == 0) {
sinkCounter.incrementBatchEmptyCount();
status = Status.BACKOFF;
} else {
if (size < batchSize) {
sinkCounter.incrementBatchUnderflowCount();
} else {
sinkCounter.incrementBatchCompleteCount();
}
sinkCounter.addToEventDrainAttemptCount(size);
client.appendBatch(batch); //批量处理event
} transaction.commit(); //事务提交
sinkCounter.addToEventDrainSuccessCount(size); } catch (Throwable t) {
transaction.rollback(); //事务回滚
if (t instanceof Error) {
throw (Error) t;
} else if (t instanceof ChannelException) {
logger.error("Rpc Sink " + getName() + ": Unable to get event from" +
" channel " + channel.getName() + ". Exception follows.", t);
status = Status.BACKOFF;
} else {
destroyConnection(); //销毁链接
throw new EventDeliveryException("Failed to send events", t);
}
} finally {
transaction.close(); //事务关闭
} return status;
}
即使本批次event的数量达不到client.getBatchSize()(channel中没数据了)也会立即发送到RPC服务器。verifyConnection()方法是确保存在链接且处于活动状态,如果链接处于非活动状态销毁并重建链接。如果本批次没有event,则不会想RPC发送任何数据。client.appendBatch(batch)方法是批量发送event。
(1)NettyAvroRpcClient.appendBatch(batch)方法会调用appendBatch(events, requestTimeout, TimeUnit.MILLISECONDS)方法,该方法会首先确认链接处于READY状态,否则报错;然后将每个event重新封装成AvroFlumeEvent,放入avroEvents列表中;然后构造一个CallFuture和avroEvents一同封装成一个Callable放入线程池 handshake = callTimeoutPool.submit(callable)中去执行,其call方法内容是avroClient.appendBatch(avroEvents, callFuture)就是在此批量提交到RPC服务器;然后handshake.get(connectTimeout, TimeUnit.MILLISECONDS)在规定时间等待执行的返回结果以及等待append的完成waitForStatusOK(callFuture, timeout, tu),详细的可看这里Flume的Avro Sink和Avro Source研究之二 : Avro Sink ,有对于这两个future更深入的分析。一个批次传输的event的数量是min(batchSize,events.size())
(2)FailoverRpcClient.appendBatch(batch)方法会做最多maxTries次尝试直到获取到可以正确发送events的Client,通过localClient=getClient()--》getNextClient()来获取client,这个方法每次会获取hosts中的下一个HostInfo,并使用NettyAvroRpcClient来作为RPC Client,这就又回到了(1)中,这个方法还有一个要注意的就是会先从当前的lastCheckedhost+1位置向后找可以使用的Client,如果不行会再从开始到到lastCheckedhost再找,再找不到就报错。使用localClient.appendBatch(events)来处理events,可参考(1)。
(3)LoadBalancingRpcClient.appendBatch(batch)方法,首先会获取可以发送到的RPC服务器的迭代器Iterator<HostInfo> it = selector.createHostIterator();然后取一个HostInfo,RpcClient client = getClient(host)这个Client和(2)一样都是NettyAvroRpcClient,但是getClient方法会设置一个保存名字和client映射的clientMap;client.appendBatch(events)执行之后就会跳出循环,下一次appendBatch会选择下一个client执行。
(4)ThriftRpcClient.appendBatch(batch)方法,从connectionManager.checkout()获取一个client,ConnectionPoolManager类主要维护俩对象availableClients用来存放可用的client(是一个ClientWrapper,维护一个ThriftSourceProtocol.Client client 是用来批量处理event的)、checkedOutClients用来存储从availableClients中拿出的Client表示正在使用的Client;ConnectionPoolManager.checkout()用于从availableClients中remove出client并放入checkedOutClients中,返回这个client;ConnectionPoolManager.checkIn(ClientWrapper client)方法用于将指定的Client从checkedOutClient中remove出并放入availableClients中;ConnectionPoolManager.destroy(ClientWrapper client)用于将checkedOutClients中的指定Client remove并close。appendBatch方法中获得client后,会每次封装min(batchSize,events.size())个event,把他们封装成ThriftFlumeEvent加入thriftFlumeEvents列表,然后如果thriftFlumeEvents>0则执行doAppendBatch(client, thriftFlumeEvents).get(requestTimeout,TimeUnit.MILLISECONDS)阻塞等待传输完毕。doAppendBatch方法会构建一个Callable其call方法执行client.client.appendBatch(e),将这个Callable放入线程池callTimeoutPool中执行并返回执行结果Future。
以上四种RpcClient的append(Event event)方法也比较容易理解,不再讲述。
四、stop()方法主要是销毁链接,关闭cxnResetExecutor。
其实flume支持avro和thrift两种(目前)传输,上面的(2)和(3)只不过是对(1)的上层业务做了一次封装而已,本质上还是一样的都是avro(基于netty)。同时记住avrosink是支持压缩的。
在此,由于博主对avro、netty、thrift并未深入研究过,所以只能从flume层面讲解avrosink,对于某些人来说,可能讲的并不深入,相关内容请自行学习!!
Flume-NG源码阅读之AvroSink的更多相关文章
- ng2048源码阅读
ng2048源码阅读 Tutorial: http://www.ng-newsletter.com/posts/building-2048-in-angularjs.html Github: http ...
- Pytorch版本yolov3源码阅读
目录 Pytorch版本yolov3源码阅读 1. 阅读test.py 1.1 参数解读 1.2 data文件解析 1.3 cfg文件解析 1.4 根据cfg文件创建模块 1.5 YOLOLayer ...
- 编译spark源码及塔建源码阅读环境
编译spark源码及塔建源码阅读环境 (一),编译spark源码 1,更换maven的下载镜像: <mirrors> <!-- 阿里云仓库 --> <mirror> ...
- spark源码阅读
根据spark2.2的编译顺序来确定源码阅读顺序,只阅读核心的基本部分. 1.common目录 ①Tags②Sketch③Networking④Shuffle Streaming Service⑤Un ...
- Sping学习笔记(一)----Spring源码阅读环境的搭建
idea搭建spring源码阅读环境 安装gradle Github下载Spring源码 新建学习spring源码的项目 idea搭建spring源码阅读环境 安装gradle 在官网中下载gradl ...
- JDK源码阅读-------自学笔记(一)(java.lang.Object重写toString源码)
一.前景提要 Object类中定义有public String toString()方法,其返回值是 String 类型. 二.默认返回组成 类名+@+16进制的hashcode,当使用打印方法打印的 ...
- 【原】FMDB源码阅读(三)
[原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...
- 【原】FMDB源码阅读(二)
[原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...
- 【原】FMDB源码阅读(一)
[原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于 ...
随机推荐
- Storm-源码分析-Topology Submit-Task
mk-task, 比较简单, 因为task只是概念上的结构, 不象其他worker, executor都需要创建进程或线程 所以其核心其实就是mk-task-data, 1. 创建TopologyCo ...
- sql中in和exists的区别
in 和exists in是把外表和内表作hash 连接,而exists 是对外表作loop 循环,每次loop 循环再对内表进行查询. 一直以来认为exists 比in 效率高的说法是不准确的.如果 ...
- caffe自定义layer
caffe自带layers: http://caffe.berkeleyvision.org/tutorial/layers.html Layers: Image Data - read raw im ...
- Delphi中MD5实现方法(转)
原来写过一个计算MD5的程序,是用了一个叫MD5.pas的单元,使用起来还算简单,但还有更简单的办法,安装了indy就会有IdHashMessageDigest单元(delphi 7默认安装indy) ...
- kotlin写的几个小例子
Kotlin-AdapterDemo kotlin语言下BaseAdapter,ArrayAdapter,SimpleAdapter,SimpleCursorAdapter四种适配器的示例 工具and ...
- node.js---sails项目开发(5)---用户表的建立
1. ctrl+c 暂停sails项目 ,输入如下命令,创建一个user表 sails generate api user 2.在api目录分别建立了两个文件 api/controllers/U ...
- XPath学习
一.基本语法 1.以 / 斜线开始,该路径表示到一个元素下的绝对路径 2.如果路径以双斜线 // 开头, 则表示选择文档中所有满足双斜线//之后规则的元素(无论层级关系) 3.星号 * 表示选择所有由 ...
- 利用开源的TaskScheduler组件实现监控和管理windows计划任务
对于计划任务的执行有很多种解决方案,如利用开源Quartz作业调度框架,在SQL Server的作业等等,同时Windows的任务计划程序功能也很强大,利用此可以很方便的实现很多计划任务,除了人工进行 ...
- CNN学习笔记:激活函数
CNN学习笔记:激活函数 激活函数 激活函数又称非线性映射,顾名思义,激活函数的引入是为了增加整个网络的表达能力(即非线性).若干线性操作层的堆叠仍然只能起到线性映射的作用,无法形成复杂的函数.常用的 ...
- HDU - 6333 Problem B. Harvest of Apples (莫队+组合数学)
题意:计算C(n,0)到C(n,m)的和,T(T<=1e5)组数据. 分析:预处理出阶乘和其逆元.但如果每次O(m)累加,那么会超时. 定义 S(n, m) = sigma(C(n,m)).有公 ...