我们曾经在Flume-NG中的Channel与Transaction关系(原创)这篇文章中说了channel和Transaction的关系,但是在source和sink中都会使用Transaction,那么Transaction的并发性如何?

  Transaction是介于channel和source、sink直接的一层缓存,为了安全性和可靠性source、sink不能直接访问channel,只能访问在他之上的Transaction,通过Transaction间接操作channel中的数据。

  这节我们以memory channel和file channel来研究一下Flume-NG的Transaction并发性。

  首先所有channel的父类都是BasicChannelSemantics,所有Transaction的父类都是BasicTransactionSemantics,mem channel中的Transaction是MemoryTransaction是内部私有类;file channel的Transaction是FileBackedTransaction。一般来说自己定义channel需要实现自己的Transaction。

  我们在看源码时发现FileBackedTransaction不允许take和put同时操作,在其doCommit和doRollback方法中都有限制使得,究其原因是:。而mem channel的Transaction则没有诸多限制。

  我们在source和sink中见到的getTransaction()获取的Transaction是同一个吗?如果不是并发性是怎么保证的?第一个很明显不是同一个,试想如果都是同一个,那么不同组件比如一个source和一个sink都会有Transaction.close操作,将会关闭事务,那关闭晚的还如何commit?我们再来看下getTransaction()代码:  

   /**
* <p>
* Initializes the channel if it is not already, then checks to see
* if there is an open transaction for this thread, creating a new
* one via <code>createTransaction</code> if not.
* @return the current <code>Transaction</code> object for the
* calling thread
* </p>
*/
@Override
public Transaction getTransaction() { if (!initialized) {
synchronized (this) {
if (!initialized) {
initialize();
initialized = true;
}
}
} BasicTransactionSemantics transaction = currentTransaction.get();
if (transaction == null || transaction.getState().equals(
BasicTransactionSemantics.State.CLOSED)) {
transaction = createTransaction();
currentTransaction.set(transaction);
}
return transaction;
}

  上面我们可以看出来,如果transaction还未初始化或者transaction的状态是CLOSED(就是执行了close()方法改了状态),说明需要通过createTransaction()新建一个Transaction,createTransaction()这个方法在子类中实现的。我们来看看mem和file的createTransaction()方法的代码,先看mem的:

 @Override
protected BasicTransactionSemantics createTransaction() {
return new MemoryTransaction(transCapacity, channelCounter);
}

  直接就返回了自己的Transaction对象,在看file的createTransaction()方法的代码:

 @Override
protected BasicTransactionSemantics createTransaction() {
if(!open) {
String msg = "Channel closed " + channelNameDescriptor;
if(startupError != null) {
msg += ". Due to " + startupError.getClass().getName() + ": " +
startupError.getMessage();
throw new IllegalStateException(msg, startupError);
}
throw new IllegalStateException(msg);
}
FileBackedTransaction trans = transactions.get();
if(trans != null && !trans.isClosed()) { //在这保证put和take只能一个时刻有一个
Preconditions.checkState(false,
"Thread has transaction which is still open: " +
trans.getStateAsString() + channelNameDescriptor);
}
trans = new FileBackedTransaction(log, TransactionIDOracle.next(),
transactionCapacity, keepAlive, queueRemaining, getName(),
channelCounter);
transactions.set(trans);
return trans;
}

  这个就比mem的复杂了点,毕竟代码多了不少。

  在看上面的getTransaction()方法,如果已经创建了一个Transaction则会放入currentTransaction中,然后以后再调用getTransaction()就会通过currentTransaction返回currentTransaction.get(),这莫不是同一个Transaction吗?那就好像有点不对了,对吧,那到底是怎么回事呢?

  关键在于currentTransaction这个东西,我们看声明:private ThreadLocal<BasicTransactionSemantics> currentTransaction = new ThreadLocal<BasicTransactionSemantics>()是ThreadLocal的实例,可能有很多人不了解这个东西,其实我也不了解!!简单来说:ThreadLocal使得各线程能够保持各自独立的一个对象,ThreadLocal并不是一个Thread,而是Thread的局部变量,为解决多线程程序的并发问题提供了一种新的思路,详细请谷歌、百度之。ThreadLocal有一个ThreadLocalMap静态内部类,你可以简单理解为一个MAP,这个‘Map’为每个线程复制一个变量的‘拷贝’存储其中,这个“Map”的key是当前线程的ID,value就是set的变量,而get方法会依据当前线程ID从ThreadLocalMap中获取对应的变量,咱们这里就是Transaction。这下明白了吧,每个source和sink都会有单独的线程来驱动的,所以都有各自的Transaction,是不同的,因此也就可以并发了(针对memory channel)。

  但是上面file的createTransaction()方法为什么是那样的?因为我们说了file的Transaction不能同时put和take(同一个Transaction一般只会做一个事就是put或者take),也就是不存在并发性的,所以在file channel中的transactions也设置为了private final ThreadLocal<FileBackedTransaction> transactions =new ThreadLocal<FileBackedTransaction>(),由于channel也是单独的线程驱使,所以这个transactions中始终只存在一对值就是file channel的线程ID和创建的Transaction,如果不同sink或者source调用getTransaction()时会试图通过createTransaction()方法来创建新的Transaction但是file的createTransaction()方法由于已经有了一个Transaction,在其关闭之前是不会同意 再次创建的,所以就只能等待这个Transaction关闭了,因此也就保证了put和take不会同时存在了。也就没有并发性了,性能自然大受影响。

  那file channel为什么不让put和take同时操作呢?这个问题很值得研究,一:put、take、commit、rollback都会获取log的共享锁,一旦获取其他就只能读,获取锁的目的就是这四个操作都要写入log文件;二,put操作并不会将写入log的event的指针放到queue中,而是在commit中才会放到queue中;三、take的操作会直接从queue中取数据,这时如果put已经commit就可以获取数据,如果没有则会返回null;四、由于四个操作都会获取log锁,导致实际上写达不到并发,而且这个log锁使得即使是写不同的数据文件也不可能,因为只有这一个锁,不是每个数据文件一个锁(数据文件的个数是动态的这个不好做);五、若take和put同时操作会使得可能交替执行获取锁,此时可能put没commit而queue中无数据,take获取锁之后也没什么意义而且也是轮流不是并行,只会降低put和take的性能,比如put和take各自单独只需1s即可,但是这样可能需要2s甚至更长时间(take一直在等待put的commit)才能完成。综上不让put和take同时操作比较合理。

  但是有没有更好的方案可以提高file的性能呢?因为file是基于文件的性能不可能很高,更为合理的办法是合理提高并发性,可以优化的一个方案是put、take、commit、rollback单独以文件存放,并设置相应的多个锁,但是文件的动态变化以及put和put、take和take、commit和commit、rollback和rollback之间的并发性又难以实现了,似乎只适合take和put的并发,这样貌似会使得file channel更复杂了,但是性能应该会提高一些,会不会得不偿失啊?

  还有一个问题就是:file channel中的createTransaction()方法如果再次创建Transaction,而先前创建的并未关闭,会执行Preconditions.checkState(false,"Thread has transaction which is still open: " +trans.getStateAsString()+ channelNameDescriptor)会直接抛出异常,但是似乎日志中没有类似的异常啊,而且进程也并未中断,但是显然使用了file channel的flume,sink和source可以正常运行,这是怎么搞得?

Flume-NG中Transaction并发性探究的更多相关文章

  1. 如何在Django模型中管理并发性 orm select_for_update

    如何在Django模型中管理并发性 为单用户服务的桌面系统的日子已经过去了 - 网络应用程序现在正在为数百万用户提供服务,许多用户出现了广泛的新问题 - 并发问题. 在本文中,我将介绍在Django模 ...

  2. Flume NG中的Kafka Channel

    kafka(官网地址:http://kafka.apache.org)是一款分布式消息发布和订阅的系统 在Flume中的KafkaChannel支持Flume与Kafka整合,可以将Kafka当做ch ...

  3. Flume NG中的Netcat Source

    NetCat是一个非常简单的Unix工具,可以读.写TCP或UDP网络连接(network connection)中数据 在Flume中的netcat支持Flume与NetCat整合,flume可以使 ...

  4. Flume NG中的ElasticSearch Sink

    ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为Apach ...

  5. Flume NG Getting Started(Flume NG 新手入门指南)

    Flume NG Getting Started(Flume NG 新手入门指南)翻译 新手入门 Flume NG是什么? 有什么改变? 获得Flume NG 从源码构建 配置 flume-ng全局选 ...

  6. 错误:违反并发性: DeleteCommand 影响了预期 1 条记录中的 0 条

    在access的mdb数据库动态更新的过程中,遇到了DeleteCommand出现DBConcurrencyException异常,错误:违反并发性: DeleteCommand 影响了预期 1 条记 ...

  7. 违反并发性: UpdateCommand影响了预期 1 条记录中的 0 条 解决办法

    本文转载:http://www.cnblogs.com/litianfei/archive/2007/08/16/858866.html UpdateCommand和DeleteCommand出现DB ...

  8. 【转】Flume(NG)架构设计要点及配置实践

    Flume(NG)架构设计要点及配置实践   Flume NG是一个分布式.可靠.可用的系统,它能够将不同数据源的海量日志数据进行高效收集.聚合.移动,最后存储到一个中心化数据存储系统中.由原来的Fl ...

  9. Flume NG 简介及配置实战

    Flume 作为 cloudera 开发的实时日志收集系统,受到了业界的认可与广泛应用.Flume 初始的发行版本目前被统称为 Flume OG(original generation),属于 clo ...

随机推荐

  1. Silverlight button 图片切换样式

    之前一直做WPF现在开始接触Slilverlight感触很多. 今天做一个Button要求 有两个图片,button默认有一个图片,鼠标over时用另一个图片, 用wpf做的时候写一个template ...

  2. 常用的user32API说明

    整理了一下常用的user32API说明 还有软件Microsoft Spy++供大家下载  Spyv10.00.30319.rar using System; using System.Collect ...

  3. swift项目实战FoodPin目录

    好吧,据说写博客能够找到好工作,那我也来分享一个项目吧! 自己自学iOS开发也有半年多了,现在就来分享一个swift的小项目吧!这个项目的来源是<Beginning iOS8 programmi ...

  4. 『转载』C# winform 中dataGridView的重绘(进度条,虚线,单元格合并等)

    原文转载自:http://hi.baidu.com/suming/item/81e45b1ab9b4585f2a3e2243 最近比较浅的研究了一下dataGridView的重绘,发现里面还是有很多东 ...

  5. PM2实用入门指南

    简介 PM2是node进程管理工具,可以利用它来简化很多node应用管理的繁琐任务,如性能监控.自动重启.负载均衡等,而且使用非常简单. 下面就对PM2进行入门性的介绍,基本涵盖了PM2的常用的功能和 ...

  6. python&MongoDB爬取图书馆借阅记录(没有验证码)

    题外话:这个爬虫本来是想用java完成然后发布在博客园里的,但是一直用java都失败了,最后看到别人用了python,然后自己就找别人问了问关键的知识点,发现连接那部分,python只用了19行!!! ...

  7. Object C学习笔记18-SEL,@ selector,Class,@class

    本章是对上一章<<Object C学习笔记17-动态判断和选择器>>的一点补充,所以比较简单点. 一. SEL 类型 在上一篇介绍了几个方法,都只是介绍了其使用方式但是没有具体 ...

  8. 原生js dom记忆的内容

    1.DOM基础getElementByIdgetElementByTagNamegetElementByName getElementsByClass querySelector querySelec ...

  9. javascript继承(三)—继承的实现原理

    打算针对js的继承写一系列文章,详细的分析js里继承原理,实现方式,各种继承方式的优缺点,以及最优继承方案,还有多继承的问题等…. 面向对象的编程的核心是封装.继承和多态,js可以看作是一种面向对象的 ...

  10. UIScrollView和UIPageController

    实现代码: // // ViewController.m // UIpageControl // // Created by dllo on 16/3/10. // Copyright © 2016年 ...