啊,AvroSink要复杂好多:《

好吧,先确定主要问题:

  1. AvroSink为啥这么多代码?有必要吗?它都有哪些逻辑需要实现?

  你看,avro-rpc-quickstart里是这么建client,然后进行RPC的

  

        NettyTransceiver client = new NettyTransceiver(new InetSocketAddress(65111));
// client code - attach to the server and send a message
Mail proxy = (Mail) SpecificRequestor.getClient(Mail.class, client);
     proxy.send(message);

  

  那么,AvroSink为啥不是这么简单?它会启动多个线程,并发的RPC? 它会使用连接池?它自己实现了一个?

  AvroSink继承自 AbstractRpcSink. AbstractRpcSink对Sink接口的process方法的实现为,由自己持有的RpcClient对象来对消息进行实际处理,即 client.appendBatch(batch);。而AvroSink实现AbstractRpcSink中的虚方法 "protected abstract RpcClient initializeRpcClient(Properties props)" 来提供一个可用的RpcClient。它的实现为:

  

  protected RpcClient initializeRpcClient(Properties props) {
logger.info("Attempting to create Avro Rpc client.");
return RpcClientFactory.getInstance(props);
}

  而RpcClientFactory的getInstance方法当“client.type"参数为空时,返回默认的RpcClient,即 NettyAvroRpcClient。


NettyAvroRpcClient

在它的"private void connect(long timeout, TimeUnit tu) throws FlumeException"方法中,实始化进行RPC所需要的代理,即此类中avroClient域。

      transceiver = new NettyTransceiver(this.address, socketChannelFactory, tu.toMillis(timeout));
avroClient = SpecificRequestor.getClient(AvroSourceProtocol.Callback.class, transceiver);

  avroClient可以代理AvroSourceProtocol.Callback.class,这个AvroSourceProtocol.Callback.class 定义了跟AvroSourceProtocol相似的接口,不过增加了一个参数用来进行回调。

  

@org.apache.avro.specific.AvroGenerated
public interface AvroSourceProtocol {
  public static final org.apache.avro.Protocol PROTOCOL = org.apache.avro.Protocol.parse("{\"protocol\":\"AvroSourceProtocol\",\"namespace\":\"org.apache.flume.source.avro\",\"doc\":\"* Licensed to the Apache Software Foundation (ASF) under one\\n * or more contributor license agreements. See the NOTICE file\\n * distributed with this work for additional information\\n * regarding copyright ownership. The ASF licenses this file\\n * to you under the Apache License, Version 2.0 (the\\n * \\\"License\\\"); you may not use this file except in compliance\\n * with the License. You may obtain a copy of the License at\\n *\\n * http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing,\\n * software distributed under the License is distributed on an\\n * \\\"AS IS\\\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\\n * KIND, either express or implied. See the License for the\\n * specific language governing permissions and limitations\\n * under the License.\",\"types\":[{\"type\":\"enum\",\"name\":\"Status\",\"symbols\":[\"OK\",\"FAILED\",\"UNKNOWN\"]},{\"type\":\"record\",\"name\":\"AvroFlumeEvent\",\"fields\":[{\"name\":\"headers\",\"type\":{\"type\":\"map\",\"values\":\"string\"}},{\"name\":\"body\",\"type\":\"bytes\"}]}],\"messages\":{\"append\":{\"request\":[{\"name\":\"event\",\"type\":\"AvroFlumeEvent\"}],\"response\":\"Status\"},\"appendBatch\":{\"request\":[{\"name\":\"events\",\"type\":{\"type\":\"array\",\"items\":\"AvroFlumeEvent\"}}],\"response\":\"Status\"}}}");
  org.apache.flume.source.avro.Status append(org.apache.flume.source.avro.AvroFlumeEvent event) throws org.apache.avro.AvroRemoteException;
  org.apache.flume.source.avro.Status appendBatch(java.util.List<org.apache.flume.source.avro.AvroFlumeEvent> events) throws org.apache.avro.AvroRemoteException;

@SuppressWarnings("all")
  public interface Callback extends AvroSourceProtocol {
    public static final org.apache.avro.Protocol PROTOCOL = org.apache.flume.source.avro.AvroSourceProtocol.PROTOCOL;
    void append(org.apache.flume.source.avro.AvroFlumeEvent event, org.apache.avro.ipc.Callback<org.apache.flume.source.avro.Status> callback) throws java.io.IOException;
    void appendBatch(java.util.List<org.apache.flume.source.avro.AvroFlumeEvent> events, org.apache.avro.ipc.Callback<org.apache.flume.source.avro.Status> callback) throws java.io.IOException;
  }
}

下边看下NettyAvroRpcClient是怎么实现其RpcClient接口的append和appendBatch方法的。

它所override的append(Event event)方法中,把消息的处理交给自己的append(Event event, long timeout, TimeUnit tu)来处理。

append方法首先将Event对象转化为用于RPC的AvroFlumeEvent对象。然后把RPC的动作提交给一个线程池。

try {
  // due to AVRO-1122, avroClient.append() may block
  handshake = callTimeoutPool.submit(new Callable<Void>() {

    @Override
    public Void call() throws Exception {
      avroClient.append(avroEvent, callFuture);
    return null;
    }
  });
} catch (RejectedExecutionException ex) {
  throw new EventDeliveryException(this + ": Executor error", ex);
}

avroClient.append中有一个callFuture参数,future代表了一个异步执行的结果,所以它所被期望的行为是append方法会立即返回,然后另一个线程通过callFuture对象来获取执行的结果。但是实际上,由于avro RPC 之前有个handshake的过程用于确认双方持有的shema是否合适,这个handshake的过程会阻塞client端RPC调用的方法,即会阻塞client端的append方法,使得在提交任务后,直接使用callFuture的get(timeout),若append方法执行完返回了结果,那这个timeout实际上可能包括了handshake的时长加上server端实际执行append方法的时长。因此AvroSink把这两个时长都设为可配置的,即用户可以设定handshake的花的时长,以及等待server端处理请求的时长。但是这个handshake只在client和server第一次通信时进行。所以后续的client端的append RPC调用会立即返回,不再需要等待handshake。

参见AVRO-1122

  看一下AvroSink的配置选项。

connect-timeout 20000 Amount of time (ms) to allow for the first (handshake) request.
request-timeout 20000 Amount of time (ms) to allow for requests after the first.

connect-timeout的设置是通过下面的代码实现的。等待这个Callable执行完,如果超时,就取消这个Callable.去掉异常处理后的代码是这样子:

handshake.get(connectTimeout, TimeUnit.MILLISECONDS);
finally {
if (!handshake.isDone()) {
handshake.cancel(true);
}
} waitForStatusOK(callFuture, timeout, tu);

  而waitForStatusOK是这样子:

    try {
Status status = callFuture.get(timeout, tu);
if (status != Status.OK) {
throw new EventDeliveryException(this + ": Avro RPC call returned " +
"Status: " + status);
}

  即, append方法会根据在flume配置文件里设置的超时参数进行等待。调用append方法的线程还是会阻塞到这个消息处理完毕。

 

  由于handshake会阻塞RPC调用,而handshake花的时间是不确定的,所以才不得不使用一个线程池,即callTimeoutPool来将append这个RPC调用放在单独的Callable里执行,用Future对RPC的执行情况进行监控,如果append进行过长时间的等待,就通过future取消这个任务。真是用心良苦……

Flume的Avro Sink和Avro Source研究之二 : Avro Sink的更多相关文章

  1. Flume的Avro Sink和Avro Source研究之一: Avro Source

    问题 : Avro Source提供了怎么样RPC服务,是怎么提供的? 问题 1.1 Flume Source是如何启动一个Netty Server来提供RPC服务. 由GitHub上avro-rpc ...

  2. [ETL] Flume 理论与demo(Taildir Source & Hdfs Sink)

    一.Flume简介 1. Flume概述 Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集.聚合和传输的系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据: ...

  3. hadoop深入研究:(十六)——Avro序列化与反序列化

    转载请写明来源地址:http://blog.csdn.net/lastsweetop/article/details/9773233 所有源码在github上,https://github.com/l ...

  4. WARN conf.FlumeConfiguration: Could not configure sink sink1 due to: No channel configured for sink: sink1 org.apache.flume.conf.ConfigurationException: No channel configured for sink: sink1

    1.错误如下所示,启动flume采集文件到hdfs案例的时候,出现如下所示的错误: 大概是说No channel configured for sink,所以应该是sink哪里配置出现了错误,百度了一 ...

  5. 二、Sink例程

    1. Sink例程 CSR粗略的将audio蓝牙设备分为了两大类:sink和source设备,并分别提供了两类设备的例程,配置工具,说明文档.如对于sink设备,提供了sink app例程,SinkU ...

  6. 关于ADMM的研究(二)

    关于ADMM的研究(二) 4. Consensus and Sharing 本节讲述的两个优化问题,是非常常见的优化问题,也非常重要,我认为是ADMM算法通往并行和分布式计算的一个途径:consens ...

  7. hadoop深入研究:(十八)——Avro schema兼容

    转载请写明来源地址:http://blog.csdn.net/lastsweetop/article/details/9900129 所有源码在github上,https://github.com/l ...

  8. Hadoop实战-Flume之Source regex_extractor(十二)

    a1.sources = r1 a1.sinks = k1 a1.channels = c1 # Describe/configure the source a1.sources.r1.type = ...

  9. 【原创】JPEG图像密写研究(二) 哈夫曼树的建立

    [原创]记录自己研究的过程,仅供参考,欢迎讨论... 在根据JPEG图像文件结构读取完文件后,提取出其中DHT段,利用其中内容建立哈夫曼树,便于之后译码工作.这里需要注意的是文件中的哈夫曼表数量不固定 ...

随机推荐

  1. getParameterMap的使用

    就是前端提交到Servlet或者Action里面的参数Map哈,如果你是以表单提交,那么request.getParameterMap()中将包含你表单里面所有input标签的数据,以其name为ke ...

  2. 关于jquery的取消阻止默认事件

    最近帮朋友做个东西,整个屏幕有一个遮罩层,但是这个遮罩层能滑动,于是百度了一下,返现,用 $('body').bind("touchmove",function(e){ e.pre ...

  3. of 循环 改变 对象值 对const的理解 对象的字面量 计算属性

    const arr = [{a:23,b:34},{a:123,b:134}]console.log(arr)for (let v of arr){console.log(v)const old = ...

  4. Python菜鸟之路:Django 表单验证

    前言 Django中完成表单验证,常用的有两种方法: 一种是通过HTML + JS + Ajax实现. 另一种是通过Django自身的forms模块来生成相应个HTML标签来完成表单验证.这是本节着重 ...

  5. Python操作Redis(二)

    List操作 redis中的List在在内存中按照一个name对应一个List来存储.如图: lpush(name,values) # 在name对应的list中添加元素,每个新的元素都添加到列表的最 ...

  6. STL之空间配置器

    在前面很多随笔里都有提到new对象是先分配内存然后初始化对象,主要是对operator new和placement new的使用 在SGI STL中内存的分配和初始化是分开的,分配内存是使用类模板,模 ...

  7. brew和brew cask安装

    brew 是从下载源码解压然后 ./configure && make install ,同时会包含相关依存库.并自动配置好各种环境变量,而且易于卸载. 这个对程序员来说简直是福音,简 ...

  8. C#加快Bitmap的访问速度

    在对Bitmap图片操作的时候,有时需要用到获取或设置像素颜色方法:GetPixel 和 SetPixel, 如果直接对这两个方法进行操作的话速度很慢,这里我们可以通过把数据提取出来操作,然后操作完在 ...

  9. 3.6中的range()

    在python3中range()是这样的: >>> range(4) range(0, 4) #额,列表跑哪去了 在之前的python2中是这样的: >>> ran ...

  10. python并发编程之多线程2---(死锁与递归锁,信号量等)

    一.死锁现象与递归锁 进程也是有死锁的 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用, 它们都将无法推进下去.此时称系统处于死锁状态或系统 ...