Flume-NG中Transaction并发性探究
我们曾经在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并发性探究的更多相关文章
- 如何在Django模型中管理并发性 orm select_for_update
如何在Django模型中管理并发性 为单用户服务的桌面系统的日子已经过去了 - 网络应用程序现在正在为数百万用户提供服务,许多用户出现了广泛的新问题 - 并发问题. 在本文中,我将介绍在Django模 ...
- Flume NG中的Kafka Channel
kafka(官网地址:http://kafka.apache.org)是一款分布式消息发布和订阅的系统 在Flume中的KafkaChannel支持Flume与Kafka整合,可以将Kafka当做ch ...
- Flume NG中的Netcat Source
NetCat是一个非常简单的Unix工具,可以读.写TCP或UDP网络连接(network connection)中数据 在Flume中的netcat支持Flume与NetCat整合,flume可以使 ...
- Flume NG中的ElasticSearch Sink
ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为Apach ...
- Flume NG Getting Started(Flume NG 新手入门指南)
Flume NG Getting Started(Flume NG 新手入门指南)翻译 新手入门 Flume NG是什么? 有什么改变? 获得Flume NG 从源码构建 配置 flume-ng全局选 ...
- 错误:违反并发性: DeleteCommand 影响了预期 1 条记录中的 0 条
在access的mdb数据库动态更新的过程中,遇到了DeleteCommand出现DBConcurrencyException异常,错误:违反并发性: DeleteCommand 影响了预期 1 条记 ...
- 违反并发性: UpdateCommand影响了预期 1 条记录中的 0 条 解决办法
本文转载:http://www.cnblogs.com/litianfei/archive/2007/08/16/858866.html UpdateCommand和DeleteCommand出现DB ...
- 【转】Flume(NG)架构设计要点及配置实践
Flume(NG)架构设计要点及配置实践 Flume NG是一个分布式.可靠.可用的系统,它能够将不同数据源的海量日志数据进行高效收集.聚合.移动,最后存储到一个中心化数据存储系统中.由原来的Fl ...
- Flume NG 简介及配置实战
Flume 作为 cloudera 开发的实时日志收集系统,受到了业界的认可与广泛应用.Flume 初始的发行版本目前被统称为 Flume OG(original generation),属于 clo ...
随机推荐
- IOS开发之——Masonry 只支持OC,暂不支持swift
前言 1 MagicNumber -> autoresizingMask -> autolayout 以上是纯手写代码所经历的关于页面布局的三个时期 在iphone1-iphone3gs时 ...
- 使用网易云音乐,丢掉QQ音乐吧
我是一个听音乐的重度用户,基本上每天大约有三分之一的时间里我在使用网易云音乐去听音乐.包括工作写代码的时候,跑步的时候,去上班的途中我都去听.首先需要声明的是,在这里我不是故意的去抹黑其他的音乐产品, ...
- 20145208 《Java程序设计》第4周学习总结
20145208 <Java程序设计>第4周学习总结 教材学习内容总结 继承 在学习指导中我了解到继承是符合DRY原则的,DRY(Don't repeat yourself),字面意思来看 ...
- 看牙与IT项目
周末为了一颗牙第五次去牙科诊所,得到的消息是:还需要观察至少2周才能做牙冠,同时发现了较深的牙结石需要做刮治,刮治疗需要2次.因此至少要再去医院3次.从去年的六月体检发现这颗牙的问题,目前最乐观估计也 ...
- 『片段』OracleHelper (支持 多条SQL语句)
C# 调用 Oracle 是如此尴尬 >System.Data.OracleClient.dll —— .Net 自带的 已经 过时作废. >要链接 Oracle 服务器,必须在 本机安装 ...
- servlet设置缓存时间以及文件的下载
缓存时间的设置: public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletE ...
- 取当前的地址栏的Url和url中的参数
看到这样一段代码: exports.showLogin = function (req, res) { req.session._loginReferer = req.headers.referer; ...
- [USACO2004][poj2373]Dividing the Path(DP+单调队列)
http://poj.org/problem?id=2373 题意:一条直线分割成N(<=25000)块田,有一群奶牛会在其固定区域吃草,每1把雨伞可以遮住向左右延伸各A到B的区域,一只奶牛吃草 ...
- android 通过帧动画方式播放Gif动画
注意:经过本人测试,这个方法很耗内存, 图片一多就崩了.慎用 <1>用工具(photoshop或者FireWorks)将GIF动画图片分解成多个GIF静态图片,然后保存在res\drawa ...
- 搭建andiord sdk和安装eclipse adt插件的个人小体会
由于以前就已经搭建好了java jdk的运行环境这里就不多说了,不过这几天看了一篇博客才明白jdk变量环境设置的某些要义,不在是人云亦云而不知其所以然. 其博客的地址:http://www.cnblo ...