事务拓扑是怎么回事?

Storm guarantees that every message will be played through the topology at least once.

Storm has a feature called transactional topologies that let you achieve exactly-once messaging semantics for most computations.

  事务拓扑,保证流入拓扑的数据能够被完整的处理且处理一次

  Acker拓扑,保证流入拓扑的数据能够被完整的处理,但不保证不重复

  普通拓扑,不保证流入拓扑的数据能够被完整的处理;

引入前言

  Storm是一个分布式的流处理系统,利用anchor和ack机制保证所有tuple都被成功处理。如果tuple出错,则可以被重传,但是如何保证出错的tuple只被处理一次呢?Storm提供了一套事务性组件Transaction Topology,用来解决这个问题。

  Transactional Topology目前已经不再维护,由Trident来实现事务性topology,但是原理相同。

1 一致性事务的设计

  Storm如何实现即对tuple并行处理,又保证事务性。本节从简单的事务性实现方法入手,逐步引出Transactional Topology的原理。

1.1 简单设计一:强顺序流

  保证tuple只被处理一次,最简单的方法就是将tuple流变成强顺序的,并且每次只处理一个tuple。从1开始,给每个tuple都顺序加上一个id。在处理tuple的时候,将处理成功的tuple id和计算结果存在数据库中。下一个tuple到来的时候,将其id与数据库中的id做比较。如果相同,则说明这个tuple已经被成功处理过了,忽略它;如果不同,根据强顺序性,说明这个tuple没有被处理过,将它的id及计算结果更新到数据库中。

  以统计消息总数为例。每来一个tuple,如果数据库中存储的id 与当前tuple id不同,则数据库中的消息总数加1,同时更新数据库中的当前tuple id值。如图:

  但是这种机制使得系统一次只能处理一个tuple,无法实现分布式计算

1.2 简单设计二:强顺序batch流

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

  我们要保证一个batch只被处理一次,机制和上一节类似。只不过数据库中存储的是batch id。batch的中间计算结果先存在局部变量中,当一个batch中的所有tuple都被处理完之后,判断batch id,如果跟数据库中的id不同,则将中间计算结果更新到数据库中。

  如何确保一个batch里面的所有tuple都被处理完了呢?可以利用Storm提供的CoordinateBolt。如图:

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

1.3 CoordinateBolt原理

CoordinateBolt具体原理如下:

  • 真正执行计算的bolt外面封装了一个CoordinateBolt。真正执行任务的bolt我们称为real bolt。
  • 每个CoordinateBolt记录两个值:有哪些task给我发送了tuple(根据topology的grouping信息);我要给哪些tuple发送信息(同样根据groping信息)
  • Real bolt发出一个tuple后,其外层的CoordinateBolt会记录下这个tuple发送给哪个task了。
  • 等所有的tuple都发送完了之后,CoordinateBolt通过另外一个特殊的stream以emitDirect的方式告诉所有它发送过tuple的task,它发送了多少tuple给这个task。下游task会将这个数字和自己已经接收到的tuple数量做对比,如果相等,则说明处理完了所有的tuple。
  • 下游CoordinateBolt会重复上面的步骤,通知其下游。

整个过程如图所示:

CoordinateBolt主要用于两个场景:

  • DRPC
  • Transactional Topology

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

1.4 Trasactional Topology

  Storm提供的Transactional Topology将batch计算分为process和commit两个阶段。Process阶段可以同时处理多个batch,不用保证顺序性;commit阶段保证batch的强顺序性,并且一次只能处理一个batch,第1个batch成功提交之前,第2个batch不能被提交。

  还是以统计消息总数为例,以下代码来自storm-starter里面的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“);

TransactionalTopologyBuilder共接收四个参数。

  • 这个Transactional Topology的id。Id用来在Zookeeper中保存当前topology的进度,如果这个topology重启,可以继续之前的进度执行。
  • Spout在这个topology中的id
  • 一个TransactionalSpout。一个Trasactional Topology中只能有一个TrasactionalSpout.在本例中是一个MemoryTransactionalSpout,从一个内存变量(DATA)中读取数据。
  • TransactionalSpout的并行度(可选)。

下面是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方法的最后一个参数是batch id,在Transactional Tolpoloyg里面这id是一个TransactionAttempt对象。

  Transactional Topology里发送的tuple都必须以TransactionAttempt作为第一个field,storm根据这个field来判断tuple属于哪一个batch。

  TransactionAttempt包含两个值:一个transaction id,一个attempt id。transaction id的作用就是我们上面介绍的对于每个batch中的tuple是唯一的,而且不管这个batch replay多少次都是一样的。attempt id是对于每个batch唯一的一个id, 但是对于同一个batch,它replay之后的attempt id跟replay之前就不一样了, 我们可以把attempt id理解成replay-times, storm利用这个id来区别一个batch发射的tuple的不同版本。

  execute方法会为batch里面的每个tuple执行一次,你应该把这个batch里面的计算状态保持在一个本地变量里面。对于这个例子来说, 它在execute方法里面递增tuple的个数。

  最后, 当这个bolt接收到某个batch的所有的tuple之后, finishBatch方法会被调用。这个例子里面的BatchCount类会在这个时候发射它的局部数量到它的输出流里面去。

下面是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方法中,将当前的transaction id与数据库中存储的id做比较。如果相同,则忽略这个batch;如果不同,则把这个batch的计算结果加到总结果中,并更新数据库。

Transactional Topolgy运行示意图如下:

下面总结一下Transactional Topology的一些特性:

  • Transactional Topology将事务性机制都封装好了,其内部使用CoordinateBolt来保证一个batch中的tuple被处理完。
  • TransactionalSpout只能有一个,它将所有tuple分为一个一个的batch,而且保证同一个batch的transaction id始终一样。
  • BatchBolt处理batch在一起的tuples。对于每一个tuple调用execute方法,而在整个batch处理完成的时候调用finishBatch方法。
  • 如果BatchBolt被标记成Committer,则只能在commit阶段调用finishBolt方法。一个batch的commit阶段由storm保证只在前一个batch成功提交之后才会执行。并且它会重试直到topology里面的所有bolt在commit完成提交。
  • Transactional Topology隐藏了anchor/ack框架,它提供一个不同的机制来fail一个batch,从而使得这个batch被replay。

5.2 Trident介绍

  Trident是Storm之上的高级抽象,提供了joins,grouping,aggregations,fuctions和filters等接口。如果你使用过Pig或Cascading,对这些接口就不会陌生。

  Trident将stream中的tuples分成batches进行处理,API封装了对这些batches的处理过程,保证tuple只被处理一次。处理batches中间结果存储在TridentState对象中。

  Trident事务性原理这里不详细介绍,有兴趣的读者请自行查阅资料。

参考:

http://xumingming.sinaapp.com/736/twitter-storm-transactional-topolgoy/

http://xumingming.sinaapp.com/811/twitter-storm-code-analysis-coordinated-bolt/

https://github.com/nathanmarz/storm/wiki/Trident-tutorial

参考链接:

Storm官方文档:Transactional Topologies

徐明明博客:Twitter Storm: Transactional Topolgoy简介

Transactional topologies —— 事务拓扑的更多相关文章

  1. spring@Transactional注解事务不回滚不起作用无效的问题处理

    这几天在项目里面发现我使用@Transactional注解事务之后,抛了异常居然不回滚.后来终于找到了原因. 如果你也出现了这种情况,可以从下面开始排查. 一.特性先来了解一下@Transaction ...

  2. @Transactional注解事务不回滚不起作用无效

     写在前面 数据库Mysql8.0 添加@Transactional注解后事务并未起作用. 修改表的引擎后ok了.(详看下面转载内容) ================================ ...

  3. @Transactional spring 事务失效(转载)

    原文地址:http://hwak.iteye.com/blog/1611970 1. 在需要事务管理的地方加@Transactional 注解.@Transactional 注解可以被应用于接口定义和 ...

  4. @Transactional注解事务不起作用

    @Transactional注解事务不起作用 问题:今天在项目中碰到一个事务问题,使用@Transactional注解事务,抛出异常不会滚. 解决一:https://blog.csdn.net/u01 ...

  5. @Transactional(事务讲解)和springboot 整合事务

    概述 事务在编程中分为两种:声明式事务处理和编程式事务处理 编程式事务处理:编码方式实现事务管理,常与模版类TransactionTemplate(推荐使用) 在业务代码中实现事务. 可知编程式事务每 ...

  6. spring @transactional 注解事务

    1.在spring配置文件中引入<tx:>命名空间 <beans xmlns="http://www.springframework.org/schema/beans&qu ...

  7. Spring注解之@Transactional对于事务异常的处理

    spring对于事务异常的处理 unchecked   运行期Exception   spring默认会进行事务回滚       比如:RuntimeException checked       用 ...

  8. Spring 多数据源 @Transactional 注解事务管理

    在 Spring,MyBatis 下两个数据源,通过 @Transactional 注解 配置简单的事务管理 spring-mybatis.xml <!--******************* ...

  9. @Transactional spring事务无效的解决方案

    关于@Transactional注解 一般都认为要注意以下三点 1 .在需要事务管理的地方加@Transactional 注解.@Transactional 注解可以被应用于接口定义和接口方法.类定义 ...

随机推荐

  1. CUDA 6.5 && VS2013 && Win7:创建CUDA项目

    运行环境: Win7+VS2013+CUDA6.5 1.创建win32空项目 2.右键项目解决方案-->生成项目依赖项-->生成自定义 3.右键项目解决方案-->属性-->配置 ...

  2. PHP快速抓取快递信息

    <?php header("Content-type:text/html;charset=utf-8"); /** * Express.class.php 快递查询类 * @ ...

  3. 每天一个命令day1【diff 命令】(具体实例看下一节)

    diff 命令是 linux上非常重要的工具,用于比较文件的内容,特别是比较两个版本不同的文件以找到改动的地方.diff在命令行中打印每一个行的改动.最新版本的diff还支持二进制文件.diff程序的 ...

  4. 《转载》三年建站之路走得一事无成 今来A5撞墙反思

    本文转载自A5站的蚕丝被.如果给站长带来不便之处,请联系博主. 时间过得真快,记得上一次在A5写文章已经是一年前的事了,这其中是有原因的,今天就跟大家来聊聊三年来个人失败经历的撞墙反思,也给一些有着同 ...

  5. 1.7 逆序数与归并排序[inversion pairs by merge sort]

    [本文链接] http://www.cnblogs.com/hellogiser/p/inversion-pairs-by-merge-sort.html [题目] 编程之美1.7光影切割问题可以进一 ...

  6. Java for LeetCode 148 Sort List

    Sort a linked list in O(n log n) time using constant space complexity. 解题思路: 归并排序.快速排序.堆排序都是O(n log ...

  7. ext树表+ZeroClipboard复制链接功能

    效果图:

  8. NEFU 1142 表哥的面包

    表哥的面包 Problem:1142 Time Limit:1000ms Memory Limit:65535K Description 可爱的表哥遇到了一个问题,有一个长为N(1≤N≤10^18)的 ...

  9. VS2010 error C3861: “exit”: 找不到标识符

    #include <stdlib.h> 可以解决问题

  10. 记录远程桌面登录者的IP和MAC

    WINDOWS 2003 远程桌面不能记录登陆IP真是件头痛的事,本方法可以记录登陆者IP,具体的操作步骤如下: 1.建立一个存放日志的目录,如C盘下建立一个RDP的目录“C:/RDP”. 2.然后在 ...