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. centos7 安装php环境和安装swoole

    这仅是我在网上找了多个解决方法,搞定了我遇到的问题,做的一个记录,买这个服务器就是为了测试swoole,结果快到期了,swoole还没装好 感谢https://www.cnblogs.com/phpw ...

  2. 从源码角度实现一个自己的Promise

    原文链接:https://geniuspeng.github.io/2017/12/14/my-promise/ 关于Promise的概念以及意义就不在这里介绍了,最近看到了一些实现Promise的核 ...

  3. Android应用开发按下返回键退向后台执行

    转载请注明来源:http://blog.csdn.net/kjunchen/article/details/50429694 Android应用开发按下返回键退向后台执行 我们日常使用的非常多Andr ...

  4. php计算两个坐标直线距离

    function rad($d) { return $d * 3.1415926535898 / 180.0; } function GetDistance($lat1, $lng1, $lat2, ...

  5. [TypeScript] Find the repeated item in an array using TypeScript

    Say you have an array that has at least one item repeated. How would you find the repeated item. Thi ...

  6. Ext.Ajax.request用法

    向一个远程服务器发送HTTP请求. Ajax服务器请求是异步的, 所以对响应数据的处理需要使用回调函数来实现. var params = {}; var ret; Ext.Ajax.request({ ...

  7. 使用Nexus搭建Maven仓库

    1.目的 通过建立自己的私服,能够减少中央仓库负荷.节省外网宽带.加速maven构建.自己部署构件等,从而高效的使用maven,nexus是当前流行的Maven仓库管理软件. 2.下载nexus 2. ...

  8. @RequiresPermissions 解释

    @RequiresAuthentication 验证用户是否登录,等同于方法subject.isAuthenticated() 结果为true时. @RequiresUser 验证用户是否被记忆,us ...

  9. Ambari——大数据平台的搭建利器(一)

    Ambari 跟 Hadoop 等开源软件一样,也是 Apache Software Foundation 中的一个项目,并且是**项目.目前最新的发布版本是 2.0.1,未来不久将发布 2.1 版本 ...

  10. NYOJ 36 最长公共子序列 (还是dp)

    这个好多算法书上都有,不仅限于<算法导论> 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描写叙述 咱们就不拐弯抹角了,如题.须要你做的就是写一个程序,得出最长公 ...