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 ...
随机推荐
- initializer for conditional binding must have optional type not AVAudioPlayer
if let buttonBeep = self.setupAudioPlayerWithFile("ButtonTap", type: "wav") { ...
- 20145215《Java程序设计》第5周学习总结
20145215<Java程序设计>第五周学习总结 教材学习内容总结 异常处理 语法与继承架构 异常就是程序在运行时出现不正常情况,异常的由来是因为Java把出现的问题封装成了对象,换句话 ...
- 重写TiledServiceLayer实现Arcgis访问Mapabc地图服务 (转载)
package com.baixin.main;/** * * @ClassName: MapAbcToArcGISTLayer * @Description: ArcGIS访问MapAb ...
- ThinkPHP之视图模版的使用
用户发起一个请求后,服务器应该返回一个页面,而页面是由我们的视图层来控制的. 一.修改控制器 <?php namespace Home\Controller; use Think\Control ...
- c# 6.0新特性(一)
写在前面 接近年底了,基本上没什么活了,就学点新东西,就想着了解下c# 6.0的新特性.在code project上看到了一篇不错的文章,就准备翻译一下,顺便照着学习学习.废话不多说,直奔主题. 原文 ...
- 验证xml是否有效于.dtd文件
<html> <head> <script language="javascript"> <!-- //加载解析器对象 var xmldo ...
- Java-小数点控制
package 运算及类型转换类; import java.text.DecimalFormat; public class 控制小数点类 { public static double decimal ...
- jquery操作滚动条滚动到指定位置
<html><head><script type="text/javascript" src="/jquery/jquery.js" ...
- GPUImage学习
1.GLProgram--加载vertex和fragment的shader. 好处是完全将shader模块的加载过程独立出来. 学习:每个函数处理一件事,且函数的粒度刚好 在glLinkProgram ...
- 【SDOI2009】解题汇总
又开了波专题,感觉就和炉石开冒险一样...(说的好像我有金币开冒险似的) /---------------------------------------------/ BZOJ-1226 [SDOI ...