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确认机制的更多相关文章

  1. Storm编程入门API系列之Storm的可靠性的ACK消息确认机制

    概念,见博客 Storm概念学习系列之storm的可靠性  什么业务场景需要storm可靠性的ACK确认机制? 答:想要保住数据不丢,或者保住数据总是被处理.即若没被处理的,得让我们知道. publi ...

  2. storm的acker机制理解

    转载请注明原创地址http://www.cnblogs.com/dongxiao-yang/p/6142356.html Storm 的拓扑有一些特殊的称为"acker"的任务,这 ...

  3. storm 消息确认机制及可靠性

    worker进程死掉 在一个节点 kill work进程 比方 kill 2509  对work没有影响 由于会在其它节点又一次启动进程运行topology任务 supervisor进程死掉 supe ...

  4. Storm消息可靠处理机制

    在很多应用场景中,分布式系统的可靠性保障尤其重要.比如电商平台中,客户的购买请求需要可靠处理,不能因为节点故障等原因丢失请求:比如告警系统中,产生的核心告警必须及时完整的知会监控人员,不能因为网络故障 ...

  5. Storm内部的消息传递机制

    作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 一个Storm拓扑,就是一个复杂的多阶段的流式计算.Storm中的组件 ...

  6. RabbitMQ 消息确认机制

    消息确认机制 在之前异常处理部分就已经写了,对于consumer的异常退出导致消息丢失,可以时候consumer的消息确认机制.重复的就不说了,这里说一些不一样的. consumer的消息确认机制 当 ...

  7. JMS确认机制

    JMS中为数不多的重点就是消息的确认机制,下面分别介绍J2EE和Spring的MessageListenerContainer的确认机制 J2EE中JMS确认机制 在JMS规范中一共4种确认方式 AU ...

  8. AMQ学习笔记 - 16. 确认机制的测试

    概述 对Acknowledge机制进行测试. 此处的测试是针对Consumer的确认设计的:对于Producer的确认是透明的,无法提供测试. 测试实例 设计demo,测试三种确认机制. 测试机制 测 ...

  9. (转)RabbitMQ消息队列(九):Publisher的消息确认机制

    在前面的文章中提到了queue和consumer之间的消息确认机制:通过设置ack.那么Publisher能不到知道他post的Message有没有到达queue,甚至更近一步,是否被某个Consum ...

随机推荐

  1. Python处理微信利器——itchat

    接触itchat是一个偶然,上知乎刷出一个有意思的文章.于是乎运行源码,调错加上查阅博客,发现itchat大有可为. 知乎链接:https://zhuanlan.zhihu.com/p/2578293 ...

  2. Write Markdown Syntax Online Document with Sphinx and Pandoc

    There is no doubt that we have to write doc while we are developing software. But How do you write d ...

  3. 从npm到vue和nodejs

    https://www.npmjs.com.cn/ vue和nodejs Windows 下安装NPM:https://www.cnblogs.com/interdrp/p/6779973.html ...

  4. 移动端 - Android客户端性能测试常见指标

    rom版本的性能测试 一般关注功耗(不过 rom 版本的功耗测试跟应用的功耗测试会有所差异,当然只是用例设计方面的差异,工具仍然采用安捷伦电源仪进行) 应用的性能测试 包括很多测试项,如启动时间.内存 ...

  5. 准备用有人云平台和tlink.io云平台和电脑做云转发

    初步想的是用有人做国网电表转发,用tlink.io做综合采集模块转发,耗时一天 然后用tlink.io的做二次前端开发,耗时两天 用有人做二次前端开发,耗时两天 最后可以试试用常见的OPC公网转发到这 ...

  6. MySQl数据约束练习

    默认值约束   1 CREATE TABLE USER( 2 uid INT, 3 uname VARCHAR(10), 4 address VARCHAR(30) DEFAULT '山东省' 5 ) ...

  7. Java 判断字符串能否转化为数字的三种方法

    用JAVA自带的函数 public static boolean isNumeric(String str){ for (int i = str.length();--i>=0;){ if (! ...

  8. hdu 4911 求逆序对数+树状数组

    http://acm.hdu.edu.cn/showproblem.php?pid=4911 给定一个序列,有k次机会交换相邻两个位置的数,问说最后序列的逆序对数最少为多少. 实际上每交换一次能且只能 ...

  9. python之随机验证码

    一.内置函数 chr ------把数字转换为字母 ord-------把字母转换为数字 n = chr(65) print (n) m = ord("A") print(m) 二 ...

  10. 冲刺博客NO.3

    今天做了什么:参考网上的一些登录界面,发现了Android studio自带loginActivity.做了基础登录界面 不停地上网搜,各种不会.  在短信验证功能上通过在Mob.com的集成文档和官 ...