getting start with storm 翻译 第八章 part-1
转载请注明出处:http://blog.csdn.net/lonelytrooper/article/details/12434915
第八章 事务性Topologies
在Storm中,正如本书前边提到的,你可以通过使用ack和fail策略来确保消息处理。但是如果元组被重放了会发生什么?你怎样确保你不会计数过多?
事务性Topologies是包含在Storm0.7.0版本中的新特性,它激活消息语义来确保你以一种安全的方式重放元组并且它们只会被处理一次。没有事务性topologies的支持,你不可能以一种完全精确、可扩展和容错的方式计数。
事务性Topologies是建立标准Storm spout和bolts之上的一个抽象。
设计
在事务性topology中,Storm使用并行和顺序元组处理的混合模式。Spout产生的批量的元组被bolts并行的处理。这些bolts中的一部分被认为是提交者,它们以某种严格排序的方式提交处理过的批量元组。这意味着如果你有两个批量,每个批量包含五个元组,两边的元组会被bolts以并行的方式处理,但是提交者bolts直到第一个元组被提交成功后才会提交第二个元组。
当处理事务性Topology时,可以从源重放批量的元组,甚至有时候是多次重放是非常重要的。所以确保你的数据源--你的spout将要连接的那个--具备这个能力。
这可以被描述成两个不同的步骤,或者阶段:
处理阶段
完全并行的阶段,许多批量被同时执行。
提交阶段
严格排序的阶段,第二个批量直到第一个批量被提交成功后才提交。
把这两个阶段称为Storm事务。
storm使用zookeeper来保存事务元数据。缺省的情况下就使用为topology服务的那个zookeeper来保存元数据。你可以通过覆盖配置键transactional.zookeeper.
servers 和 transactional.zookeeper.port.来更改。
事务实战
为看清事务怎样工作,你将建立一个Twitter分析工具。你会读取存储在Redis中的tweets,通过一系列bolts处理他们,然后存储--在另一个Redis数据库中--所有标签和它们在tweets中的频率的列表,所有用户和他们出现在tweets中的总计的列表和一个用户及他们标签和频率的列表。
你即将创建的这个工具的topology由图8-1描述.
图8-1 Topology视图
正如你看到的,TweetsTransactionalSpout会连接到你的tweets数据库并且在topology中发射批量的元组。两个不同的bolts,UserSplitterBolt 和 HashtagSplitterBolt,将从spout接收元组。UserSplitterBolt会分析tweet并且查找用户--@后边的单词--并发送这些词一个叫做users的自定义流。HashatagSplitterBolt也分析tweet,查找#以前的单词,并且发送这些单词到一个叫做hashtags的自定义流。第三个bolt,UserHashtagJoinBolt,会接收两个流并且计算在一个命名用户的tweet中一个hashtag出现了多少次。为了计数和发送结果,该bolt会是BaseBatchBolt(后边介绍更多)。
最终,最后一个叫做RedisCommitterBolt 的bolt,接收这三个--由UserSplitterBolt, HashtagSplitterBolt和 UserHashtagJoinBolt产生的流。在同一个事物中,它会完成所有计数,并且一旦完成了元组批次的处理就会发送到Redis。该bolt被认为是一种特殊的bolt,叫做提交者bolt,后续的章节中会解释它。
为了构建这个topology,使用TransactionalTopologyBuilder,类似下边的代码块:
TransactionalTopologyBuilder builder =
newTransactionalTopologyBuilder("test","spout",newTweetsTransactionalSpout());
).shuffleGrouping("spout");
builder.setBolt("hashtag-splitter",
).shuffleGrouping("spout");
)
.fieldsGrouping("users-splitter","users", newFields("tweet_id"))
.fieldsGrouping("hashtag-splitter", "hashtags", newFields("tweet_id"));
builder.setBolt("redis-committer", newRedisCommiterCommiterBolt())
.globalGrouping("users-splitter","users")
.globalGrouping("hashtag-splitter", "hashtags")
.globalGrouping("user-hashtag-merger");
我们看一下怎么在事务topology中实现spout.
The Spout
事务topology中的spout与标准的spout完全不同。
public class TweetsTransactionalSpoutextends
BaseTransactionalSpout<TransactionMetadata> {
正如你在类定义中看到的,TweetsTransactionalSpout继承自BaseTransactionalSpout并带一个泛型类型。你设置在这里的类型被认为是事务元数据。它将在后边从源发送批量的元组时被使用。
在这个例子中,TransactionMetadata被定义为:
public class TransactionMetadataimplementsSerializable{
private static final longserialVersionUID=1L;
long from;
int quantity;
public TransactionMetadata(longfrom,intquantity) {
this.from=from;
this.quantity=quantity;
}
}
这里你存储了from和quantity,它们会告诉你具体怎样产生批量的元组。
为完成spout的实现,你需要实现如下方法:
@Override
publicITransactionalSpout.Coordinator<TransactionMetadata>getCoordinator(
Map conf,TopologyContextcontext) {
returnnew TweetsTransactionalSpoutCoordinator();
}
@Override
publicbacktype.storm.transactional.ITransactionalSpout.Emitter<TransactionMetadata>
getEmitter(
Map conf,TopologyContextcontext) {
returnnew TweetsTransactionalSpoutEmitter();
}
@Override
publicvoiddeclareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(newFields("txid","tweet_id","tweet"));
}
在getCoordinator方法中,你告诉Storm哪个类将协调批量元组的产生。在getEmitter中,你告诉Storm哪个类负责从源读取批量的元组并且发射它们到topology的一个流中。最后,如你之前所做的,你需要声明发射了哪些域。
The RQ class
为了使例子更简单些,我们决定封装所有的Redis相关的操作在一个类中。
public class RQ{
public static final String NEXT_READ="NEXT_READ";
public static final String NEXT_WRITE="NEXT_WRITE";
Jedis jedis;
public RQ() {
jedis =newJedis("localhost");
}
public longgetAvailableToRead(longcurrent) {
return getNextWrite() -current;
}
publiclonggetNextRead() {
String sNextRead =jedis.get(NEXT_READ);
if(sNextRead==null)
;
returnLong.valueOf(sNextRead);
}
publiclonggetNextWrite() {
returnLong.valueOf(jedis.get(NEXT_WRITE));
}
publicvoidclose() {
jedis.disconnect();
}
publicvoidsetNextRead(longnextRead) {
jedis.set(NEXT_READ,""+nextRead);
}
publicList<String>getMessages(longfrom,intquantity) {
String[]keys=newString[quantity];
;i<quantity;i++)
keys[i] =""+(i+from);
returnjedis.mget(keys);
}
}
仔细阅读每个方法的实现,确保你明白了他们在做什么。
协调器
我们看一下这个例子中协调器的实现。
public static class TweetsTransactionalSpoutCoordinatorimplements
ITransactionalSpout.Coordinator<TransactionMetadata> {
TransactionMetadata lastTransactionMetadata;
RQ rq =newRQ();
;
public TweetsTransactionalSpoutCoordinator() {
nextRead =rq.getNextRead();
}
@Override
public TransactionMetadatainitializeTransaction(BigInteger txid,
TransactionMetadata prevMetadata) {
long quantity=rq.getAvailableToRead(nextRead);
quantity =quantity>MAX_TRANSACTION_SIZE?MAX_TRANSACTION_SIZE:quantity;
TransactionMetadata ret =newTransactionMetadata(nextRead, (int)quantity);
nextRead +=quantity;
returnret;
}
@Override
publicbooleanisReady() {
;
}
@Override
publicvoidclose() {
rq.close();
}
}
很重要的需要提醒的一点是在整个topology中只有一个协调器实例。当协调器被初始化后,它从Redis检索一个时序,该时序告诉协调器下一条要读取的tweet是哪条。第一次时,该值为1,意味着接下来要读取的tweet是第一条。
第一个要被调用的方法是isReady。它在initializeTransaction之前总是会被调用来确保源已经准备好被读取数据。你要相应的返回true或者false。在这个例子中,检索tweets的数量并把他们和你读到的tweets数量做比较。它们之间的差异是可以读取的tweets数量。如果它大于0,说明你还有tweets可读。
最后,initializeTransaction被执行。正如你看到的,你用txid和prevMetadata作为参数。第一个参数是一个由Storm产生的唯一的事务ID,它代表产生的元组的批次。prevMetadata是前一个事务的协调器产生的元数据。
在这个例子中,首先确保有多少tweets可读取。一旦你整理好了,创建一个新的TransactionMetadata,指明哪个是第一个要读的tweet,要读取的量是多少。
你一返回元数据,Storm就把它和txid存到zookeeper中。这确保了一旦有错误,Storm有能力来使发射器重新发送元组。
发射器
创建事务spout的最后一步是实现发射器。
我们从下边的实现开始:
public static class TweetsTransactionalSpoutEmitterimplements
ITransactionalSpout.Emitter<TransactionMetadata> {
RQ rq =newRQ();
public TweetsTransactionalSpoutEmitter() {
}
@Override
publicvoidemitBatch(TransactionAttempt tx,
TransactionMetadata coordinatorMeta,BatchOutputCollector collector) {
rq.setNextRead(coordinatorMeta.from+coordinatorMeta.quantity);
List<String>messages=rq.getMessages(coordinatorMeta.from,
coordinatorMeta.quantity);
longtweetId=coordinatorMeta.from;
for(String message:messages) {
collector.emit(newValues(tx,""+tweetId,message));
tweetId++;
}
}
@Override
publicvoidcleanupBefore(BigInteger txid) {
}
@Override
publicvoidclose() {
rq.close();
}
}
发射器读取源并发射元组到一个流。对于相同的transaction id 和 transaction metadata,发射器总是可以发射相同批次的元组是非常重要的。这样,如果处理一个批次的过程中出错了,Storm可以通过发射器重放相同的transaction id 和 transaction metadata并且确保这个批次被重放了。Storm会增加TransactionAttempt中的attempt id。这样你就可以知道该批次被重放了。
这里emitBatch是一个重要的方法。在这个方法中,使用元数据来作为参数,从Redis中读取tweets。同时增加在Redis中的序列,该序列记录了你到目前为止已读取了多少tweets。当然,还要发射tweets到topology。
getting start with storm 翻译 第八章 part-1的更多相关文章
- getting start with storm 翻译 第八章 part-2
转载请注明出处:http://blog.csdn.net/lonelytrooper/article/details/12435641 The Bolts 首先我们看一下该topology中的标准bo ...
- 大数据入门第十六天——流式计算之storm详解(一)入门与集群安装
一.概述 今天起就正式进入了流式计算.这里先解释一下流式计算的概念 离线计算 离线计算:批量获取数据.批量传输数据.周期性批量计算数据.数据展示 代表技术:Sqoop批量导入数据.HDFS批量存储数据 ...
- The Django Book
The Django Book Table of contents 2.0, English -> Chinese Django book 2.0 的中文翻译. 最近更新 - 贡献者 方便自己也 ...
- 深入理解Magento – 第四章 – 模型和ORM基础
深入理解Magento 作者:Alan Storm 翻译:Hailong Zhang 第四章 – 模型和ORM基础 对于任何一个MVC架构,模型(Model)层的实现都是占据了很大一部分.对于Mage ...
- 深入理解Magento – 第三章 – 布局,块和模板
深入理解Magento 作者:Alan Storm 翻译:Hailong Zhang 第三章 – 布局,块和模板 我们接着研究Magento.根据我们第二章讲的Magento MVC的架构,我们接下来 ...
- 深入理解Magento – 第二章 – Magento请求分发与控制器
深入理解Magento 作者:Alan Storm 翻译:Hailong Zhang 第二章 – Magento请求分发与控制器 Model-View-Controller (MVC) ,模型-视图- ...
- 深入理解Magento - 第一章 - Magento强大的配置系统
深入理解Magento 作者:Alan Storm翻译:zhlmmc 前言第一章 - Magento强大的配置系统第二章 - Magento请求分发与控制器第三章 - 布局,块和模板第四章 - 模型和 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (42) ------ 第八章 POCO之使用POCO
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第八章 POCO 对象不应该知道如何保存它们,加载它们或者过滤它们.这是软件开发中熟 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (46) ------ 第八章 POCO之领域对象测试和仓储测试
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 8-8 测试领域对象 问题 你想为领域对象创建单元测试. 这主要用于,测试特定的数 ...
随机推荐
- 利用ASP.NET AJAX的Timer讓GridView每隔一段時間做到自動換頁的功能
最近在討論區看到這個問題,小弟利用asp.net ajax的timer來實作這個功能 利用timer每隔一段時間,讓gridview自動跳頁並且更新gridview的內容 asp.net(c#) Gr ...
- 测试Kettle在增量更新的时候是否支持级联——不支持(基于mysql)
由于LZ很像整理一下如何使用Kettle对一张表进行增量更新的时候, 同时实现对数据库中的其他表也可以实现相关的更新操作. 第一种方法想使用触发器,即在相应的hop之间添加SQL脚本step然后在st ...
- phpstrom+xdebug调试PHP代码
众所周知开发PHP的IDE种类繁多,然而开发PHP并不能像开发其他语言一样,调试PHP代码对诸多新手来说,搭建调试环境就比较麻烦!其实哈,我发现NuSphere-phped-16.0很强大,集成了很强 ...
- 【转】 KVC/KVO原理详解及编程指南
原文地址:http://blog.csdn.net/wzzvictory/article/details/9674431 前言: 1.本文基本不讲KVC/KVO的用法,只结合网上的资料说说对这种技术的 ...
- mysqldump备份、还原数据库路径名含有空格的处理方法(如:Program Files)
虽然以下的方法也可以解决,不过最简单直接的,还是直接在路径前后加双引号-" ",这个方法简单有效. 首先要说明的是mysqldump.exe在哪里不重要,重要的是要处理好路径中的非 ...
- sublime text 几种常用插件
1.docblockr //文档注释 使用 /** +tab 在函数前就可以 2.SublimeLinter 代码校验插件,支持多种语言,这个是主插件,如果想检测特定的文件需要单独下载 3 ...
- java_设计模式_工厂模式_Factory Pattern(2016-08-04)
工厂模式主要是为创建对象提供了接口.工厂模式按照<Java与模式>中的提法分为三类: (1)简单工厂(Simple Factory)模式,又称静态工厂方法模式(Static Factory ...
- 【BZOJ3673】【可持久化并查集】可持久化并查集 by zky
Description n个集合 m个操作操作:1 a b 合并a,b所在集合2 k 回到第k次操作之后的状态(查询算作操作)3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 0<n ...
- jquery动画效果中,避免持续反应用户的连续点击
一.某些动画效果中,避免持续连续反应用户的连续点击(这标题真不好描述) 意思就是指用户点击速度很快,完成一次效果的时间不能很快结束的话,就会出现用户不点击了,效果还在持续.看下面例子就明白了,手风琴效 ...
- CentOS下命令行和桌面模式的切换方法(转载)
桌面模式和命令行模式的切换方法 用编辑器打开 /etc/inittab 文件(这里用的是vi,你可以选择你喜欢的): #vi /etc/inittab 打开效果图如下: 桌面模式 : 把光标所在 ...