TransactionalGlobalCount :统计多个分区的单词数总和。此处3个分区,共计19个单词。分区的概念:学习MQ,kafka分区等等,主要用于提高消息read吞吐量。
MemoryTransactionalSpout
MemoryTransactionalSpout spout = new MemoryTransactionalSpout(DATA, new Fields("word"),PARTITION_TAKE_PER_BATCH);
参数说明: 第一个 分区数据,第二个 下一个Bolt接收Stream数field名称, 第三个参数指定每个batch的最大tuple数量
TransactionalGlobalCount Topology:TransactionalTopologyBuilder 将batch的计算分为两个阶段:process阶段和commit阶段。process阶段可以同时处理多个batch,不用保证顺序性,commit阶段保证batch的强顺序性,并且在commit阶段每次只能处理一个batch。
public static void main(String[] args) throws Exception {
MemoryTransactionalSpout spout = new MemoryTransactionalSpout(DATA, new Fields("word"),
PARTITION_TAKE_PER_BATCH);
TransactionalTopologyBuilder builder = new TransactionalTopologyBuilder("global-count", "spout", spout, 3);
builder.setBolt("partial-count", new BatchCount(), 5).noneGrouping("spout"); //shuffle group一样的,但在同一个Executor内存中运行
builder.setBolt("sum", new UpdateGlobalCount()).globalGrouping("partial-count"); //dreict min task id 的 bolt task 中运行
Config config = new Config();
config.setDebug(true);
config.setMaxSpoutPending(3);
// StormSubmitter.submitTopology("global-count-topology", config,
// builder.buildTopology());
LocalCluster localCluster = new LocalCluster();
localCluster.submitTopology("global-count-topology", config, builder.buildTopology());
}
MemoryTransactionalSpout --》 BatchCount --》UpdateGlobalCount
BatchCount:按照每批Txid,统计word出现次数。Object id:transaction id
public static class BatchCount extends BaseBatchBolt<Object> {
Object _id;
BatchOutputCollector _collector;
int _count = 0;
public void prepare(Map conf, TopologyContext context, BatchOutputCollector collector, Object id) {
_collector = collector;
_id = id;
}
@Override
public void execute(Tuple tuple) {
_count++;
}
@Override
public void finishBatch() {
_collector.emit(new Values(_id, _count));
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("id", "count"));
}
}
BatchCount的prepare方法最后一个参数是batchid,在Transactional Topology里面这个id是一个TransactionAttempt对象,Transactional Topology里面发送的tuple,读必须以TransactionAttempt作为第一个field,Storm根据这个field爱判断tuple属于哪个batch。
TransactionAttempt里面包含两个值,一个是txid,两外一个是attemptid,txid就是上面我们介绍的对于每一个batch里的tuple是唯一的。attemptid是每个batch唯一的一个ID,但是对每一个batch,它reply之后的attemptid和reply之前的attemptid是不一样的,我们可以吧attemptid理解为reply-times,Storm利用这个ID来区别一个batch发射的tuple的版本。
execute方法会为batch中的每一个tuple执行一次,你应该把这个batch里面的计算状态保持在一个本地变量里面。对于这个例子来说,它在execute方法里面递增tuple的个数。
最后,当这个Bolt接收到每个batch的所有的tuple之后,finishBatch方法会被执行。
UpdateGlobalCount :UpdateGlobalCount实现了ICommitter接口,所以Storm会在commit阶段调用finishBatch方法,而execute方法可以在任何阶段完成。
public static class UpdateGlobalCount extends BaseTransactionalBolt implements ICommitter {
TransactionAttempt _attempt;
BatchOutputCollector _collector;
int _sum = 0;
boolean error = true;
@Override
public void prepare(Map conf, TopologyContext context, BatchOutputCollector collector,
TransactionAttempt attempt) {
_collector = collector;
_attempt = attempt;
}
@Override
public void execute(Tuple tuple) {
_sum += tuple.getInteger(1);
}
@Override
public void finishBatch() {
Value val = DATABASE.get(GLOBAL_COUNT_KEY);
Value newval;
if (val == null || !val.txid.equals(_attempt.getTransactionId())) { //数据有新的更新
newval = new Value();
newval.txid = _attempt.getTransactionId();
if (val == null) {
newval.count = _sum;
} else {
newval.count = _sum + val.count; //数据库已经记录上一个txid 的单词次数出现总和
}
DATABASE.put(GLOBAL_COUNT_KEY, newval);
} else {
newval = val;
}
_collector.emit(new Values(_attempt, newval.count));
System.err.println("val sum:" + newval.count);
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("id", "sum"));
}
}
在UpdateGlobalCount的finishBatch方法中将当前的txid与数据库中的id作比较。
如果相同,则忽略这个batch,说明已经完成统计,
如果不同则吧这个batch的计算结果合并到总的结果中,并更新数据库。
Transactional Topology的运行示意图如下:
Transnational Topology特性:
1.Transactional Topology将事物机制封装好,其内部使用CoordinateBolt保证一个batch中的tuple被处理完2.TransactionalSpout只有一个,它将所有的tuple分组为一个一个的batch,而且保证同一个batch的txid始终一样
3.BatchBolt处理一个batch中的每一个tuple,对每一个tuple调用execute方法,并在所有的tuple都处理完后调用finishBatch方法
4.如果batchBolt实现了ICommitter接口,则只能在commit阶段调用finishBatch。