Storm的acker确认机制
Storm的acker消息确认机制...
ack/fail消息确认机制(确保一个tuple被完全处理)
在spout中发射tuple的时候需要同时发送messageid,这样才相当于开启了消息确认机制
如果你的topology里面的tuple比较多的话, 那么把acker的数量设置多一点,效率会高一点。
通过config.setNumAckers(num)来设置一个topology里面的acker的数量,默认值是1。
注意: acker用了特殊的算法,使得对于追踪每个spout tuple的状态所需要的内存量是恒定的(20 bytes)
注意:如果一个tuple在指定的timeout(Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS默认值为30秒)时间内没有被成功处理,那么这个tuple会被认为处理失败了。
下面代码中Bolt的execute中模拟消息的正常和失败.
import java.util.Map; import backtype.storm.Config;
import backtype.storm.LocalCluster;
import backtype.storm.spout.SpoutOutputCollector;
import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.TopologyBuilder;
import backtype.storm.topology.base.BaseRichBolt;
import backtype.storm.topology.base.BaseRichSpout;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;
import backtype.storm.utils.Utils; /**
* 数字累加求和
* 先添加storm依赖
*
* @author Administrator
*
*/
public class LocalTopologySumAcker { /**
* spout需要继承baserichspout,实现未实现的方法
* @author Administrator
*
*/
public static class MySpout extends BaseRichSpout{
private Map conf;
private TopologyContext context;
private SpoutOutputCollector collector; /**
* 初始化方法,只会执行一次
* 在这里面可以写一个初始化的代码
* Map conf:其实里面保存的是topology的一些配置信息
* TopologyContext context:topology的上下文,类似于servletcontext
* SpoutOutputCollector collector:发射器,负责向外发射数据(tuple)
*/
@Override
public void open(Map conf, TopologyContext context,
SpoutOutputCollector collector) {
this.conf = conf;
this.context = context;
this.collector = collector;
} int num = 1;
/**
* 这个方法是spout中最重要的方法,
* 这个方法会被storm框架循环调用,可以理解为这个方法是在一个while循环之内
* 每调用一次,会向外发射一条数据
*/
@Override
public void nextTuple() {
System.out.println("spout发射:"+num);
//把数据封装到values中,称为一个tuple,发射出去
//messageid:和tuple需要是一一对应的,可以把messageid认为是数据的主键id,而tuple中的内容就是这个数据.
//messageid和tuple中的消息是一一对应的. 它们之间的关系是需要我们程序员来维护的.
//this.collector.emit(new Values(num++));
this.collector.emit(new Values(num++),num-1);//传递messageid(num-1)参数就表示开启了消息确认机制.
Utils.sleep(1000);
} @Override
public void ack(Object msgId) {
System.out.println("处理成功");
} @Override
public void fail(Object msgId) {
System.out.println("处理失败....."+msgId);
//TODO--可以选择把失败的数据重发,或者单独存储后期进行分析
//重发的方法...this.collector.emit(tuple);//这个tuple可以根据参数msgId来获得...
} /**
* 声明输出字段
*/
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
//给values中的数据起个名字,方便后面的bolt从这个values中取数据
//fields中定义的参数和values中传递的数值是一一对应的
declarer.declare(new Fields("num"));
} } /**
* 自定义bolt需要实现baserichbolt
* @author Administrator
*
*/
public static class MyBolt extends BaseRichBolt{
private Map stormConf;
private TopologyContext context;
private OutputCollector collector; /**
* 和spout中的open方法意义一样
*/
@Override
public void prepare(Map stormConf, TopologyContext context,
OutputCollector collector) {
this.stormConf = stormConf;
this.context = context;
this.collector = collector;
} int sum = 0;
/**
* 是bolt中最重要的方法,当spout发射一个tuple出来,execute也会被调用,需要对spout发射出来的tuple进行处理
*/
@Override
public void execute(Tuple input) {
try{
//input.getInteger(0);//也可以根据角标获取tuple中的数据
Integer value = input.getIntegerByField("num");
if(value == 3){
throw new Exception("value=3异常.....");
}
sum+=value;
System.out.println("和:"+sum);
this.collector.ack(input);//这个表示确认消息处理成功,spout中的ack方法会被调用
}catch(Exception e) {
this.collector.fail(input);//这个表示确认消息处理失败,spout中的fail方法会被调用
e.printStackTrace();
}
} /**
* 声明输出字段
*/
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
//在这没必要定义了,因为execute方法中没有向外发射tuple,所以就不需要声明了。
//如果nextTuple或者execute方法中向外发射了tuple,那么declareOutputFields必须要声明,否则不需要声明
} }
/**
* 注意:在组装topology的时候,组件的id在定义的时候,名称不能以__开头。__是系统保留的
* @param args
*/
public static void main(String[] args) {
//组装topology
TopologyBuilder topologyBuilder = new TopologyBuilder();
topologyBuilder.setSpout("spout1", new MySpout());
//.shuffleGrouping("spout1"); 表示让MyBolt接收MySpout发射出来的tuple
topologyBuilder.setBolt("bolt1", new MyBolt()).shuffleGrouping("spout1"); //创建本地storm集群
LocalCluster localCluster = new LocalCluster();
Config config = new Config();
localCluster.submitTopology("sumTopology", config, topologyBuilder.createTopology());
} }
运行结果:

从结果可以看到Bolt1执行execute成功了通过ack 调用Spout1中的ack方法.... 失败了就通过fail 调用Spout1中的fail 方法 来达到对消息处理成功与否的追踪.
//=============================================================================================
上面的例子是一个Spout 和一个Bolt.....如果对应有1个Spout和2个Bolt 会是什么情况.....
改造上面的代码.....
/**
* 数字累加求和
* 先添加storm依赖
*
* @author Administrator
*
*/
public class LocalTopologySumAcker2 { /**
* spout需要继承baserichspout,实现未实现的方法
* @author Administrator
*
*/
public static class MySpout extends BaseRichSpout{
private Map conf;
private TopologyContext context;
private SpoutOutputCollector collector; /**
* 初始化方法,只会执行一次
* 在这里面可以写一个初始化的代码
* Map conf:其实里面保存的是topology的一些配置信息
* TopologyContext context:topology的上下文,类似于servletcontext
* SpoutOutputCollector collector:发射器,负责向外发射数据(tuple)
*/
@Override
public void open(Map conf, TopologyContext context,
SpoutOutputCollector collector) {
this.conf = conf;
this.context = context;
this.collector = collector;
} int num = 1;
/**
* 这个方法是spout中最重要的方法,
* 这个方法会被storm框架循环调用,可以理解为这个方法是在一个while循环之内
* 每调用一次,会向外发射一条数据
*/
@Override
public void nextTuple() {
System.out.println("spout发射:"+num);
//把数据封装到values中,称为一个tuple,发射出去
//messageid:和tuple需要是一一对应的,可以把messageid认为是数据的主键id,而tuple中的内容就是这个数据
//messageid和tuple之间的关系是需要我们程序员维护的
this.collector.emit(new Values(num++),num-1);//传递messageid参数就表示开启了消息确认机制
Utils.sleep(1000);
} /**
* 声明输出字段
*/
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
//给values中的数据起个名字,方便后面的bolt从这个values中取数据
//fields中定义的参数和values中传递的数值是一一对应的
declarer.declare(new Fields("num"));
} @Override
public void ack(Object msgId) {
System.out.println("处理成功");
} @Override
public void fail(Object msgId) {
System.out.println("处理失败。。"+msgId);
//TODO--可以选择吧失败的数据重发,或者单独存储后期分析
}
} /**
* 自定义bolt需要实现baserichbolt
* @author Administrator
*
*/
public static class MyBolt1 extends BaseRichBolt{
private Map stormConf;
private TopologyContext context;
private OutputCollector collector; /**
* 和spout中的open方法意义一样
*/
@Override
public void prepare(Map stormConf, TopologyContext context,
OutputCollector collector) {
this.stormConf = stormConf;
this.context = context;
this.collector = collector;
} int sum = 0;
/**
* 是bolt中最重要的方法,当spout发射一个tuple出来,execute也会被调用,需要对spout发射出来的tuple进行处理
*/
@Override
public void execute(Tuple input) {
try {
//input.getInteger(0);//也可以根据角标获取tuple中的数据
Integer value = input.getIntegerByField("num");
this.collector.emit(new Values(value+"_1"));
//this.collector.emit(input,new Values(value+"_1"));//新的tuple是new Values(value+"_1") 老的tuple是input
this.collector.ack(input);//确认数据处理成功,spout中的ack方法会被调用
} catch (Exception e) {
this.collector.fail(input);//确认数据处理失败,spout中的fail方法会被调用
e.printStackTrace();
}
} /**
* 声明输出字段
*/
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
//在这没必要定义了,因为execute方法中没有向外发射tuple,所以就不需要声明了。
//如果nextT|uple或者execute方法中向外发射了tuple,那么declareOutputFields必须要声明,否则不需要声明
declarer.declare(new Fields("num_1"));
} } public static class MyBolt2 extends BaseRichBolt{
private Map stormConf;
private TopologyContext context;
private OutputCollector collector; /**
* 和spout中的open方法意义一样
*/
@Override
public void prepare(Map stormConf, TopologyContext context,
OutputCollector collector) {
this.stormConf = stormConf;
this.context = context;
this.collector = collector;
} int sum = 0;
/**
* 是bolt中最重要的方法,当spout发射一个tuple出来,execute也会被调用,需要对spout发射出来的tuple进行处理
*/
@Override
public void execute(Tuple input) {
try {
//input.getInteger(0);//也可以根据角标获取tuple中的数据
String value = input.getStringByField("num_1");
System.out.println(value);
this.collector.fail(input);//确认数据处理成功,spout中的ack方法会被调用
//this.collector.ack(input);//确认数据处理成功,spout中的ack方法会被调用
} catch (Exception e) {
//this.collector.fail(input);//确认数据处理失败,spout中的fail方法会被调用
e.printStackTrace();
}
} /**
* 声明输出字段
*/
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
//在这没必要定义了,因为execute方法中没有向外发射tuple,所以就不需要声明了。
//如果nextT|uple或者execute方法中向外发射了tuple,那么declareOutputFields必须要声明,否则不需要声明
declarer.declare(new Fields("num_1"));
} } /**
* 注意:在组装topology的时候,组件的id在定义的时候,名称不能以__开头。__是系统保留的
* @param args
*/
public static void main(String[] args) {
//组装topology
TopologyBuilder topologyBuilder = new TopologyBuilder();
topologyBuilder.setSpout("spout1", new MySpout());
//.shuffleGrouping("spout1"); 表示让MyBolt接收MySpout发射出来的tuple
topologyBuilder.setBolt("bolt1", new MyBolt1()).shuffleGrouping("spout1");
topologyBuilder.setBolt("bolt2", new MyBolt2()).shuffleGrouping("bolt1"); //创建本地storm集群
LocalCluster localCluster = new LocalCluster();
localCluster.submitTopology("sumTopology", new Config(), topologyBuilder.createTopology());
}
}
上面的代码的大体意思是 Bolt1接收Spout1的输出,接收之后在数据后面加上"_1",然后发送给Bolt2,Bolt2接收到之后直接打印.
在spout2中的execute()方法不管成功还是失败 都调用 this.collector.fail(input); 方法....也就是Spout1发射的数据在Bolt1中处理都成功了,在Bolt2中的处理都失败了.
看Spout1中的哪个方法会被执行.....也就是Spout2中调用的ack或者是fail对tuple的处理状态结果是否有影响.
运行看结果:

可以看出都是成功的...这就说明tuple的处理状态和Bolt2中ack或者是fail是没有任何的关系的......只要Bolt1中处理tuple成功了,我们就认为是处理成功了...
如果Bolt1处理失败了就认为是处理失败了.. ...现在Bolt1中发射出去的tuple是无法追踪的.....
能不能在Bolt1发射的数据中也加上一个messageid...这个在Bolt中的 this.collector.emit(new Values(value+"_1")); emit方法中是不支持传入一个messageid的.
但是这样有一种场景是有问题的. 单词计数的例子:

这个Spout后面有两个Bolt 一个SplitBolt 一个CountBolt SplitBolt 切割成一个个的单词 然后再CountBolt中进行汇总....
按照上面在SplitBolt中切割成功了,就算处理成功了...但是有可能切割之后 在CountBolt中有一些Bolt没有收到. 这样最后其实是没有成功的...
而且SpiltBolt中处理的tuple和CountBolt中的tuple之间是有关联的. 后者是在前者之上切割出来的小tuple....
我们想达到两个Bolt都处理成功了才认为是处理成功的...如何做?
上面的代码中已经包括......这里再说明一下:
Spout1中 的 this.collector.emit(input,new Values(value+"_1")); ----> this.collector.emit(input,new Values(value+"_1"));//新的tuple是new Values(value+"_1") 老的tuple是input
在Spout2中还是不管是否异常都调用.. this.collector.fail(input); 看运行结果:

运行都失败了........
这样就达到了上面的"完全处理"的要求....
完全处理:保证一个tuple以及这个tuple衍生的所有tuple都被成功处理.
在storm里面一个tuple被完全处理的意思是: 这个tuple以及由这个tuple所衍生的所有的tuple都被成功处理。
如果把Bolt2的正常对应改为 this.collector.ack(input); 失败对应 this.collector.fail(input);就回复正常了.....

如果Spout2后面还有Spout3 同样把老的tuple在emit上带上.........
Storm的acker确认机制的更多相关文章
- Storm编程入门API系列之Storm的可靠性的ACK消息确认机制
概念,见博客 Storm概念学习系列之storm的可靠性 什么业务场景需要storm可靠性的ACK确认机制? 答:想要保住数据不丢,或者保住数据总是被处理.即若没被处理的,得让我们知道. publi ...
- storm的acker机制理解
转载请注明原创地址http://www.cnblogs.com/dongxiao-yang/p/6142356.html Storm 的拓扑有一些特殊的称为"acker"的任务,这 ...
- storm 消息确认机制及可靠性
worker进程死掉 在一个节点 kill work进程 比方 kill 2509 对work没有影响 由于会在其它节点又一次启动进程运行topology任务 supervisor进程死掉 supe ...
- Storm消息可靠处理机制
在很多应用场景中,分布式系统的可靠性保障尤其重要.比如电商平台中,客户的购买请求需要可靠处理,不能因为节点故障等原因丢失请求:比如告警系统中,产生的核心告警必须及时完整的知会监控人员,不能因为网络故障 ...
- Storm内部的消息传递机制
作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 一个Storm拓扑,就是一个复杂的多阶段的流式计算.Storm中的组件 ...
- RabbitMQ 消息确认机制
消息确认机制 在之前异常处理部分就已经写了,对于consumer的异常退出导致消息丢失,可以时候consumer的消息确认机制.重复的就不说了,这里说一些不一样的. consumer的消息确认机制 当 ...
- JMS确认机制
JMS中为数不多的重点就是消息的确认机制,下面分别介绍J2EE和Spring的MessageListenerContainer的确认机制 J2EE中JMS确认机制 在JMS规范中一共4种确认方式 AU ...
- AMQ学习笔记 - 16. 确认机制的测试
概述 对Acknowledge机制进行测试. 此处的测试是针对Consumer的确认设计的:对于Producer的确认是透明的,无法提供测试. 测试实例 设计demo,测试三种确认机制. 测试机制 测 ...
- (转)RabbitMQ消息队列(九):Publisher的消息确认机制
在前面的文章中提到了queue和consumer之间的消息确认机制:通过设置ack.那么Publisher能不到知道他post的Message有没有到达queue,甚至更近一步,是否被某个Consum ...
随机推荐
- git,版本控制教程
主要内容 版本回退 工作区和暂存区 管理修改 撤销修改 删除文件 分支管理 *****此处没有深讲***** 一.两条基本查看命名 查看状态命令: git status 查看修改内容命令: g ...
- Ubuntu下配置PHP和CakePHP记录
目前在完成一个PayPal的支付页面,需要有PHP的开发环境,同时,在开发时使用了CakePHP的框架,于是就有了下面的情景. 操作环境: OS:ubuntu-14.04.2-desktop-amd6 ...
- 强大的DataGrid组件[1]
说明:DataGrid组件是Silverlight数据组件中最为常用并且是功能最为强大的数据组件.因此,对开发者而言,深入了解其特性是十分有必要的.本文先介绍该组件的基本特性,接着通过几个简单实例来说 ...
- #微码分享#AES算法的C++包装类
AES为Advanced Encryption Standard的缩写,中文名:高级加密标准,在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准,用来替代DES.基于std:: ...
- X Window(远程桌面)
X Window在位映射屏幕上的一个或多个窗口中运行程序.用户可以在每个窗口中同时运行多个程序,并且可以通过用鼠标在窗口之间进行切换. x服务器的程序在本地工作站上运行,并且管理它的窗口和程序. 每个 ...
- Android FragmentActivity 嵌套 Fragment 调用startActivityForResult返回的requestCode错误
Android FragmentActivity 嵌套 Fragment 调用startActivityForResult返回的requestCode错误 此时,要在调用startActivityFo ...
- 12.equals()方法总结
超类Object中有这个equals()方法,该方法主要用于比较两个对象是否相等.该方法的源码如下: 我们知道所有对象都有表示(内存地址)和状态(数据),看上面代码是用"=="来比 ...
- 水池问题的lua语言算法(面试题分析:我的Twitter技术面试失败了)
twitter面试题内容 “看下面这个图片” “在这个图片里我们有不同高度的墙.这个图片由一个整数数组所代表,数组中每个数是墙的高度.上边的图可以表示为数组[2,5,1,2,3,4,7,7,6]” “ ...
- CentOS下下载软件,不安装的方法
今天来说下在CentOS下下载软件,不安装的方法: 方法一:通过yum自带一个工具:yumdownloader[root@web1 ~]# rpm -qa |grep yum-utils[root@ ...
- NFS Server宕机后,NFS Client主机上df命令挂死
方法1: 使用root用户:Oracle@NDMCDB05:~> su -Password: NDMCDB05:~ # cat /etc/mtab /dev/sda2 / reiserfs rw ...