org.apache.flume.sink.AvroSink是用来通过网络来数据传输的。能够将event发送到RPCserver(比方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()链接RPCserver。

  实际的链接在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的大小;获取批量大小

"batch-size"。设置batchSize。假设配置的小于1则使用默认大小100。将此client置为活动的isActive=true。能够看出这个client能够使用多个host。

  (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()方法。代码例如以下:

 1   public Status process() throws EventDeliveryException {
2 Status status = Status.READY;
3 Channel channel = getChannel(); //获得channel
4 Transaction transaction = channel.getTransaction(); //创建事务
5
6 try {
7 transaction.begin(); //事务開始
8
9 verifyConnection(); //确保存在链接且处于活动状态,假设链接处于非活动状态销毁并重建链接
10
11 List<Event> batch = Lists.newLinkedList();
12
13 for (int i = 0; i < client.getBatchSize(); i++) { //保证这批次的event数量不可能超过客户端批量处理的最大处理数量
14 Event event = channel.take();
15
16 if (event == null) { //表示channel中没有数据了
17 break;
18 }
19
20 batch.add(event); //增加event列表
21 }
22
23 int size = batch.size(); //获取这批次取得的event的数量
24 int batchSize = client.getBatchSize(); //获取客户端能够批量处理的大小
25
26 if (size == 0) {
27 sinkCounter.incrementBatchEmptyCount();
28 status = Status.BACKOFF;
29 } else {
30 if (size < batchSize) {
31 sinkCounter.incrementBatchUnderflowCount();
32 } else {
33 sinkCounter.incrementBatchCompleteCount();
34 }
35 sinkCounter.addToEventDrainAttemptCount(size);
36 client.appendBatch(batch); //批量处理event
37 }
38
39 transaction.commit(); //事务提交
40 sinkCounter.addToEventDrainSuccessCount(size);
41
42 } catch (Throwable t) {
43 transaction.rollback(); //事务回滚
44 if (t instanceof Error) {
45 throw (Error) t;
46 } else if (t instanceof ChannelException) {
47 logger.error("Rpc Sink " + getName() + ": Unable to get event from" +
48 " channel " + channel.getName() + ". Exception follows.", t);
49 status = Status.BACKOFF;
50 } else {
51 destroyConnection(); //销毁链接
52 throw new EventDeliveryException("Failed to send events", t);
53 }
54 } finally {
55 transaction.close(); //事务关闭
56 }
57
58 return status;
59 }

  即使本批次event的数量达不到client.getBatchSize()(channel中没数据了)也会马上发送到RPCserver。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)就是在此批量提交到RPCserver。然后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)方法,首先会获取能够发送到的RPCserver的迭代器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)方法也比較easy理解,不再讲述。

  四、stop()方法主要是销毁链接,关闭cxnResetExecutor。

  

  事实上flume支持avro和thrift两种(眼下)传输,上面的(2)和(3)仅仅只是是对(1)的上层业务做了一次封装而已,本质上还它们是相同的avro(基于netty)。还记得在同一时间avrosink它支持压缩。

版权声明:本文博客原创文章。博客,未经同意,不得转载。

【Java】【Flume】Flume-NG阅读源代码AvroSink的更多相关文章

  1. Emacs和它的朋友们——阅读源代码篇(转)

    正如那本<Code Reading>一书中指出的那样,源代码阅读一直没有被很好的重 视:你上大学的时候有“代码阅读”这门课吗?相信没有. 1 Source Insight 谈到阅读源代码, ...

  2. Java虚拟机系列——检视阅读

    Java虚拟机系列--检视阅读 参考 java虚拟机系列 入门掌握JVM所有知识点 2020重新出发,JAVA高级,JVM JVM基础系列 从 0 开始带你成为JVM实战高手 Java虚拟机-垃圾收集 ...

  3. Java 网络爬虫获取网页源代码原理及实现

    Java 网络爬虫获取网页源代码原理及实现 1.网络爬虫是一个自动提取网页的程序,它为搜索引擎从万维网上下载网页,是搜索引擎的重要组成.传统爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL ...

  4. 利用vim阅读源代码一个好用的工具

    阅读源代码时常常遇到找变量,函数定义的问题.vim为我们提供了一个好用的工具,ctags. 安装 ctags. 在 libvirt的源代码根目录运行 ctags -R . vim -t virConn ...

  5. java基础一(阅读Head First Java记录)

    写在前面的话 在实际应用java中,因为没有系统去看过书或者学习过,所以基础薄弱,刚好这本书是比较入门级的一些书,记录一下下面的一些基本概念,以方便自己来学习.当然如果对大家有帮助也是很好的. 因为书 ...

  6. Java中arraylist和linkedlist源代码分析与性能比較

    Java中arraylist和linkedlist源代码分析与性能比較 1,简单介绍 在java开发中比較经常使用的数据结构是arraylist和linkedlist,本文主要从源代码角度分析arra ...

  7. 《Java编程思想》阅读笔记二

    Java编程思想 这是一个通过对<Java编程思想>(Think in java)进行阅读同时对java内容查漏补缺的系列.一些基础的知识不会被罗列出来,这里只会列出一些程序员经常会忽略或 ...

  8. [转]Linux下阅读源代码:(g)vim+Taglist+ctags

      Linux下阅读源代码的方法很多,聪明人从标题应该就可以知道,需要(g)vim+Taglist+ctags.3者配合,真是珠联璧合,功力无限啊! vim/gvim什么是vim/gvim,如果看官连 ...

  9. Java Web教程——检视阅读

    Java Web教程--检视阅读 参考 java web入门--概念理解.名词解释 Java Web 教程--w3school 蓝本 JavaWeb学习总结(一)--JavaWeb开发入门 小猴子mo ...

随机推荐

  1. HQL和SQL的区别

    1.hql与sql的区别 sql 面向数据库表查询 hql 面向对象查询 hql : from 后面跟的 类名+类对象 where 后 用 对象的属性做条件 sql: from 后面跟的是表名    ...

  2. windows phone 2048 源代码奉上

    小梦今天把2048的游戏源代码给大家分享一下,其实感觉这个重在算法,UI很简单,而且几乎没有用到什么windows phone的API.小梦遇到最主要的问题就是对触摸事件的处理,我刚开始想到的就是 M ...

  3. Cisco IOS images

    Cisco IOS images for Dynamips - GNS3http://docs.gns3.com/1-kBrTplBltp9P3P-AigoMzlDO-ISyL1h3bYpOl5Q8m ...

  4. Java中a=a+b 与 a+=b差别

    一般觉得a=a+b的运行效率是低于a+=b的,由于它多进行了一步中间变量的操作,并且会多占用一个变量的空间.而Java编译器默认对其进行了优化,优化之后两条语句都当做 a+=b来运行了,所以实际上是没 ...

  5. mui常用功能链接地址

    1.下拉刷新mui.pullToRefresh插件http://ask.dcloud.net.cn/article/12152.打包app权限列表http://ask.dcloud.net.cn/ar ...

  6. 动态获取server时间

    近期在做的这个项目要获取到后台的数据.然后使用html5绘制曲线.曾经都是在自己电脑上使用没有发现有什么不正常的地方,昨天连接同事的server发现有段数据怎么都对不上了,開始我还以为程序有问题呢,找 ...

  7. java开发中的那些事(5)--------一点经历,败给2分钟的2个小时

    特意记下这个经历,这个让我感慨万千又斗志昂扬的一次经历,这是经验,也是生活. 故事的始末是这种,先给大家上几句代码,如今身在家中,仅仅能凭记忆敲打几行,大致意思倒不会错: {field:'code', ...

  8. html5中的dom中的各种节点的层次关系是怎样的

    html5中的dom中的各种节点的层次关系是怎样的 一.总结 一句话总结:Node节点是所有节点的基类,所以都继承它的方法 1.dom提供在js中动态修改html标签的作用 比如增加修改标签等,并且是 ...

  9. 轮询方法FileSystemWatcher

    具体实现 FileSystemWatcher watcher = new FileSystemWatcher(WriteBackDir, "*result.txt"); watch ...

  10. NOIP模拟 path - 按二进制位分组

    题目原文: 企鹅豆豆即将要去考长跑了,但是作为一只企鹅,长跑自然比不过鸵鸟和鸡.为了公平起见,教练告诉豆豆,他可以从 K 个指定地点中选择两个不同的地点分别作为起点和终点来考试.考试地图是一个由 N ...