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 ...
随机推荐
- 如何设置nesC在vim中语法高亮
默认的vim没有支持nesC语法高亮,给阅读源码带来不便.不过可以通过装NesC Syntax Highlighting插件来解决这个问题,具体操作如下: 步骤一:下载插件 在http://www ...
- WebStorm在Mac上的快捷键(部分)
整理一下在Mac上使用WS这款IDE的快捷键 shift + Enter 软回车 ,无论在前一行代码的什么位置,都能定位到下一行. command 显示/隐藏 左侧面板 command + b / 点 ...
- 详解.NET IL代码
一.前言 IL是什么? Intermediate Language (IL)微软中间语言 C#代码编译过程? C#源代码通过LC转为IL代码,IL主要包含一些元数据和中间语言指令: JIT编译器把IL ...
- 4G时代的抢钱之道
最近最大的事件恐怕就是移动的4G上线了,50元600M起计价,上网速度最高理论到达100Mbps,相当于一秒下载10MB的数据,或者是一分钟下载一张VCD(600M)电影. 当然,对于这样的流量套餐, ...
- asp.net网站安全常见问题与防范
1:SQL 注入 2:XSS 3:CSRF 4:文件上传 1:SQL 注入 引起原因: 其实现在很多网站中都存在这种问题.就是程序中直接进行SQL语句拼接.可能有些读者不太明白. 下面通过一个登录时对 ...
- 使用git管理代码的心得
一.简易使用流程 首先下载安装git,点击Git Bash进入编辑界面,之后如下图进入目录并通过命令 git init 把这个目录变成git可以管理的仓库 接下来使用git add .命令将所有文件添 ...
- web.xml的深入学习
1.过滤器? 监听器? listener servlet filter? 2.命名空间? schema? DTD? xml文件的学习要好好的看下哦!
- Kettle_设置变量的两种方法
一个复杂的kettle作业一般包括很多子作业和转换,在主作业Start后通常会添加一个[设置变量]的流程,该流程的功能是为所有流程的公共变量设置通用值. 主作业添加的[设置变量]针对的是所 ...
- Eclipse_调试技巧
一.使用Display视图实时计算变量结果(带智能提示) windows-->show view-->display http://stackoverflow.com/questions ...
- BZOJ 2435 道路修建 NOI2011 树形DP
一看到这道题觉得很水,打了递归树形DP后RE了一组,后来发现必须非递归(BFS) 递归版本84分: #include<cstdio> #include<cstring> #in ...