Storm是一个分布式的流处理系统,利用anchor和ack机制保证所有的tuple都被处理成功。如果tuple出错,则可以被重传,但是如何保证出错的tuple只被处理一次呢?换句话说Storm如何保证事物性呢?本节从简单的事物实现入手,最后引出事物型Topology的原理。

一.一致性事物设计

  1.简单设计一,强顺序流

  保证tuple只被处理一次,最简单的方式是将tuple刘变成强顺序的,并且每次只处理一个tuple。从1开始,给给个tuple都加上一个顺序id。在处理tuple的时候,把处理成功的tupleID和计算结果都存储到数据库中。下一个tuple到来的时候,将他和数据库中的id做比较。如果相同说明tuple已经被成功处理,可以忽略,如果不同,根据强顺序性,说明这个tuple没有被处理过,将它的id和计算结果存储到数据库。这种当时的缺点就是每次智能处理一个tuple,没办法实现分布式。

  2.简单设计二,强顺序batch流  

  为了实现分布式,我们可以一次处理一批tuple,称为一个batch。一个batch中的每个tuple可以被并行处理。

  为了保证每个batch只被处理一次,实现方式和上面的一样,只不过数据库中存储的是batch id。batch的中间计算结果先存在局部变量中,当一个batch中所有的tuple都被处理完之后,判断batch id,如果和数据库中的结果不同,则将中间结果更新到数据库。

  如何保证bath中的每个tuple都被处理完呢?可以利用Storm提供的CoordinatedBolt,如下图:
  

  但是强顺序batch流也有局限,每次只能处理一个batch,batch之间无法并行。要想实现真正的分布式事务处理,可以使用storm提供的Transactional Topology。在此之前,我们先详细介绍一下CoordinateBolt的原理。

  3.CoordinateBolt原理

  CoordinateBolt的具体原理如下:

  a.真正执行计算的Bolt外面封装了一个CoordinateBolt。我们将真正执行任务的Bolt称为Real Bolt。

  b.每个CoordinateBolt记录两个值,有哪些Task给我们发送了tuple,以及我们要给那些Task发送信息

  c.Real Bolt发出一个tuple后,其外层的CoordinateBolt会记录下这些tuple发送给了那些Task

  d.等所有的tuple都发送完之后,CoordinateBolt通过另外一个特殊的Stream以emitDirect的方式告诉所有它发送过tuple的Task,它发送了多少tuple给这些task。下游这些task会将这个数字和自己已经接收到的tuple数量对比。如果相等,则说明处理完了所有的tuple。

  e.下游的task会重复上面的步骤,通知其下游

  整个过程如下图所示:

  

  CoordinateBolt主要用于一下场景。1:DRPC,2:Transactional Topology。

  CoordinateBolt对于业务是有入侵的,要使用CoordinateBolt提供的功能,你必须包保证你的每个Bolt发送的每个tuple的第一个field是request-id,所谓我已经处理完我的上游的意思是说当前这个Bolt对于当前这个request-id所需要做的工作做完了。这个request-id在DRPC里面代表一个请求,在Transactional Topology里面代表一个batch。

  4.Transactional Topology

  Storm的Transactional Topology将batch的计算分为两个阶段:process阶段和commit阶段。process阶段可以同时处理多个batch,不用保证顺序性,commit阶段保证batch的强顺序性,并且在commit阶段每次只能处理一个batch。

  下面以storm自带的例子TransactionalGlobalCount,来说明。

   MemoryTransactionalSpout spout = new MemoryTransactionalSpout(DATA, new Fields("word"), PARTITION_TAKE_PER_BATCH);
TransactionalTopologyBuilder builder = new TransactionalTopologyBuilder("global-count", "spout", spout, );
builder.setBolt("partial-count", new BatchCount(), ).noneGrouping("spout");
builder.setBolt("sum", new UpdateGlobalCount()).globalGrouping("partial-count"); LocalCluster cluster = new LocalCluster(); Config config = new Config();
config.setDebug(true);
config.setMaxSpoutPending(); cluster.submitTopology("global-count-topology", config, builder.buildTopology()); Thread.sleep();
cluster.shutdown();
  TransactionalTopologyBuilder共接收了如下的四个参数:

  1.这个Transactional Topology的id,用来在zookeeper中保存当前topology的进度,如果这个topology重启,可以接着之前的进度执行

  2.Spout在这个Topology中的id

  3.一个Transactional Spout。一个Transactional Topology只能有一个Transactional Spout,本例中使用的是MemoryTransactionalSpout,从内存变量DATA中读取数据。

  4.Transactional Spout的并行度(可选)

  下面是BatchCount的定义:

public static class BatchCount extends BaseBatchBolt {
Object _id;
BatchOutputCollector _collector; int _count = ; @Override
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的定义如下:

public static class UpdateGlobalCount extends BaseTransactionalBolt implements ICommitter {
TransactionAttempt _attempt;
BatchOutputCollector _collector; int _sum = ; @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();
} @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;
}
DATABASE.put(GLOBAL_COUNT_KEY, newval);
}
else {
newval = val;
}
_collector.emit(new Values(_attempt, newval.count));
} @Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("id", "sum"));
}
}

   UpdateGlobalCount实现了ICommitter接口,所以Storm会在commit阶段调用finishBatch方法,儿execute方法可以在任何阶段完成。

   在UpdateGlobalCount的finishBatch方法中将当前的txid与数据库中的id作比较。如果相同,则忽略这个batch,如果不同则吧这个batch的计算结果合并到总的结果中,并更新数据库。

  Transactional Topology的运行示意图如下:
  

  
  Transactional 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。

Storm一致性事物的更多相关文章

  1. 集成Spring事物管理

    什么是事物 事物是访问数据库的一个操作序列,数据库应用系统通过事物集来完成对数据库的存取.事物的正确执行使得数据库从一种状态转换为另一种状态. 事物必须服从ISO/IEC所制定的ACID原则.ACID ...

  2. Java事物基础总结

    1.什么是事物? 事物是逻辑上的的一种操作,这个操作过程中的每一个元素要么全部成功,要么全部失败.例如,银行转账过程视为一个事物,转出过程和转入过程要求全部成功或全部失败,通过提交事物或者回滚事物实现 ...

  3. day42 事物,数据库锁

    事物是把一些sql语句作为一个原子性操作,就是说我会写好几条sql语句,然后我想把这好几条的sql语句作为一个整体,然后让这个整体一起去运行,不可以拆分开,就像我们用面粉做一个馒头一样,我需要把这些面 ...

  4. day41 python【事物 】【数据库锁】

    MySQL[五] [事物 ][数据库锁]   1.数据库事物 1. 什么是事务  事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消.也就是事务具有原子性 ...

  5. MySQL 事物和数据库锁

    1.数据库事物 1. 什么是事务  事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消.也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一 ...

  6. Python全栈 MySQL 数据库 (引擎、事物、pymysql模块、orm)

    ParisGabriel              每天坚持手写  一天一篇  决定坚持几年 为了梦想为了信仰    开局一张图     存储引擎(处理表的处理器)     基本操作:         ...

  7. python【事物 】【数据库锁】

    1.数据库事物 1. 什么是事务  事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消.也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一 ...

  8. spring5 源码深度解析----- @Transactional注解的声明式事物介绍(100%理解事务)

    面的几个章节已经分析了spring基于@AspectJ的源码,那么接下来我们分析一下Aop的另一个重要功能,事物管理. 事务的介绍 1.数据库事物特性 原子性多个数据库操作是不可分割的,只有所有的操作 ...

  9. Sql Server数据库设计高级查询

    -------------------------------------第一章  数据库的设计------------------------------------- 软件开发周期:     (1 ...

随机推荐

  1. tp5在apache下能访问,但放到nginx下报404

    index index.php index.html index.htm; if ( -f $request_filename) { break; } if ( !-e $request_filena ...

  2. 查询yum包安装路径

    rpm -ql php71-php yum install json yum install libcurl

  3. cmake重新编译

    删除文件夹下的文件 rm CMakeCache.txt 重新编译即可 安装g++ yum install gcc-c++

  4. DB2数据类型(抄袭)

    数字 数据类型        精度          最小值                                        最大值                       smal ...

  5. vue组件介绍

    https://www.cnblogs.com/Leo_wl/p/5863185.html vue.js说说组件   什么是组件:组件是Vue.js最强大的功能之一.组件可以扩展HTML元素,封装可重 ...

  6. 一个 图片 滚动 飞入的css特效

    @keyframes bounceInLeft { from, 60%, 75%, 90%, to {animation-timing-function: cubic-bezier(0.215, 0. ...

  7. Git客户端命令总结

    一:常用命令 1.先进入项目目录,然后git init:则会为此 项目/目录 创建一个本地仓库(或重新初始化这个本地仓库),可以用ls -a ./看到多了.git目录: 或者git init /hom ...

  8. C#中验证sql语句是否正确(不执行语句)

    SET PARSEONLY检查每个 Transact-SQL 语句的语法并返回任何错误消息,但不编译和执行语句.SET PARSEONLY { ON | OFF }当 SET PARSEONLY 为 ...

  9. 编译hbase-1.2.3源代码

    目录 目录 1 1. 约定 1 2. 安装jdk 1 3. 安装maven 1 4. 网络配置 2 4.1. eclipse 3 4.2. maven 3 5. 从hbase官网下载源代码包: 4 6 ...

  10. post异步请求

    //创建url NSURL *url = [[NSURL alloc] initWithString:@"http://api.hudong.com/iphonexml.do"]; ...