源码趣事-flume-队列动态扩容及容量使用

1. 前言

最近在做数据平台 整个接入系统的重构,重点看了flume的高可高机制,在看memoryChannel源码的时候发现几个有意思的实现,刚好最近得空,写出来,刚好也练练自己的文笔,哈~

2. 需要知道的组件背景

flume核心组件agent,三部分组成,SOURCE,CHANNEL,SINK。还有俩个核心部分CHANNEL_PROCESSOR,SINK_PROCESSOR。保证整个流程的高可靠,核心就在从source抽取数据到channel,从channel抽取到sink,当sink被消费后channel数据删除的这三个环节。而这些环节在flume中被统一的用事物管理起来。可以说,这是flume高可靠的关键一点。理解这一点,我们在看源码的时候就聚焦于此。于是有趣的事情来了在动态扩容上采用锁,在容量控制上上使用信号量,前者是一个比较常见东西,后面如果常规的实现应该也是使用锁。在采用信号量这种实现方式,在channel空间还是富余的情况,也能够比较保证较高的性能。虽然上面的构思不是最优,但是看到这个构思,也是颇有意思。(01世界,只有没有想到,没有做不到)

3. 源码赏析

下面是扩容的代码,在这里面可以看到严谨的代码逻辑,看了这些代码让人不禁有比较欲,如果我去实现,需要多久!我能比他写出更漂亮的代码吗,哈哈~

  private void resizeQueue(int capacity) throws InterruptedException {
    int oldCapacity;
    synchronized (queueLock) {
      //老的容量=队列现有余额+在事务被处理了但是是未被提交的容量
      oldCapacity = queue.size() + queue.remainingCapacity();
    }

    if (oldCapacity == capacity) {
      return;
    } else if (oldCapacity > capacity) {
      //缩容
     //尝试占用即将缩减的空间,以防被他人占用
      if (!queueRemaining.tryAcquire(oldCapacity - capacity, keepAlive, TimeUnit.SECONDS)) {
        LOGGER.warn("Couldn't acquire permits to downsize the queue, resizing has been aborted");
      } else {
        //直接缩容量
        synchronized (queueLock) {
          LinkedBlockingDeque<Event> newQueue = new LinkedBlockingDeque<Event>(capacity);
          newQueue.addAll(queue);
          queue = newQueue;
        }
      }
    } else {
     //扩容
      synchronized (queueLock) {
        LinkedBlockingDeque<Event> newQueue = new LinkedBlockingDeque<Event>(capacity);
        newQueue.addAll(queue);
        queue = newQueue;
      }
      queueRemaining.release(capacity - oldCapacity);
    }
  }

上面我们看到锁,配合信号实现动态增减容量。下面在来看更多信号量在容量控制的更多使用。下面这段代码是发送在commit阶段 commit阶段主要做的事情,将从source拿数据LinkedBlockingDeque putlist,和从channel拿数据的LinkedBlockingDeque takelist,进行“持久化”

    @Override
    protected void doCommit() throws InterruptedException {
      int remainingChange = takeList.size() - putList.size();
      if (remainingChange < 0) {
       //putByteCounter是需要推到channel中的数据大小,bytesRemainingchannel容量剩余
        if (!bytesRemaining.tryAcquire(putByteCounter, keepAlive, TimeUnit.SECONDS)) {
         //channel 数据大小容量不足,事物不能提交
          throw new ChannelException("Cannot commit transaction. Byte capacity " +
              "allocated to store event body " + byteCapacity * byteCapacitySlotSize +
              "reached. Please increase heap space/byte capacity allocated to " +
              "the channel as the sinks may not be keeping up with the sources");
        }
        if (!queueRemaining.tryAcquire(-remainingChange, keepAlive, TimeUnit.SECONDS)) {
         //及时释放!!!这个如果是第一次写这类的代码,很容易漏掉这类的代码
        //remainingChange如果是负数的话,说明source的生产速度,大于sink的消费速度,且这个速度大于channel所能承载的值
          bytesRemaining.release(putByteCounter);
          throw new ChannelFullException("Space for commit to queue couldn't be acquired." +
              " Sinks are likely not keeping up with sources, or the buffer size is too tight");
        }
      }
      int puts = putList.size();
      int takes = takeList.size();
     //锁住队列开始,进行数据的流转
      synchronized (queueLock) {
        if (puts > 0) {
          while (!putList.isEmpty()) {
            if (!queue.offer(putList.removeFirst())) {
              throw new RuntimeException("Queue add failed, this shouldn't be able to happen");
            }
          }
        }
        putList.clear();
        takeList.clear();
      }
      bytesRemaining.release(takeByteCounter);
      takeByteCounter = 0;
      putByteCounter = 0;

      queueStored.release(puts);
      if (remainingChange > 0) {
        queueRemaining.release(remainingChange);
      }
      if (puts > 0) {
        channelCounter.addToEventPutSuccessCount(puts);
      }
      if (takes > 0) {
        channelCounter.addToEventTakeSuccessCount(takes);
      }

      channelCounter.setChannelSize(queue.size());
    }

4. 看这篇收货了啥

信号量,队列的使用(add,put,offer,take,poll,remove)差别,严谨的使用队列逻辑(多读,多看,到实际生产中,他会成为你自然而然的思考)

好的,以下是一个示例的file-flume-kafka.conf配置文件,您可以根据自己的需求进行修改: ```properties # Name the components on this agent kafka-flume-agent.sources = kafka-source kafka-flume-agent.channels = kafka-channel kafka-flume-agent.sinks = logger-sink # Describe/configure the source kafka-flume-agent.sources.kafka-source.type = org.apache.flume.source.kafka.KafkaSource kafka-flume-agent.sources.kafka-source.zookeeperConnect = hadoop102:2181,hadoop103:2181,hadoop104:2181 kafka-flume-agent.sources.kafka-source.topic = test-topic kafka-flume-agent.sources.kafka-source.batchSize = 1000 # Describe the sink kafka-flume-agent.sinks.logger-sink.type = logger # Use a channel which buffers events in memory kafka-flume-agent.channels.kafka-channel.type = memory kafka-flume-agent.channels.kafka-channel.capacity = 10000 kafka-flume-agent.channels.kafka-channel.transactionCapacity = 1000 # Bind the source and sink to the channel kafka-flume-agent.sources.kafka-source.channels = kafka-channel kafka-flume-agent.sinks.logger-sink.channel = kafka-channel ``` 这个示例配置文件定义了一个名为"kafka-flume-agent"的Flume代理程序,它从名为"test-topic"的Kafka主题中读取数据,并将其写入到一个内存通道中。 注意,这个示例配置文件中的"zookeeperConnect"参数指定了Kafka使用的Zookeeper地址,您需要根据自己的实际情况进行修改。 启动这个配置文件的方法已经在上一条回答中给出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值