Storm ACK机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weiyongle1996/article/details/77397055

一、Ack是什么


    为了保证数据能正确的被处理, 对于spout产生的每一个tuple, storm都会进行跟踪。

ack机制即, spout发送的每一条消息:

 在规定的时间内,spout收到Acker的ack响应,即认为该tuple 被后续bolt成功处理
 在规定的时间内,没有收到Acker的ack响应tuple,就触发fail动作,即认为该tuple处理失败,
 或者收到Acker发送的fail响应tuple,也认为失败,触发fail动作

通过Ack机制,spout发送出去的每一条消息,都可以确定是被成功处理或失败处理, 从而可以让开发者采取相应动作。

另外Ack机制还常用于限流作用: 为了避免spout发送数据太快,而bolt处理太慢,常常设置pending数,当spout有等于或超过pending数的tuple没有收到ack或fail响应时,跳过执行nextTuple, 从而限制spout发送数据。


二、Ack原理


Storm中有个特殊的task名叫acker,他们负责跟踪spout发出的每一个Tuple的Tuple树(因为一个tuple通过spout发出了,经过每一个bolt处理后,会生成一个新的tuple发送出去)。当acker(框架自启动的task)发现一个Tuple树已经处理完成了,它会发送一个消息给产生这个Tuple的那个task。对任意大的一个Tuple树,storm只需要恒定的20字节就可以进行跟踪。acker对于每个spout-tuple保存一个ack-val的校验值,它的初始值是0,然后每发射一个Tuple或Ack一个Tuple时,这个Tuple的id就要跟这个校验值异或一下,
并且把得到的值更新为ack-val的新值。那么假设每个发射出去的Tuple都被ack了,那么最后ack-val的值就一定是0。Acker就根据ack-val是否为0来判断是否完全处理,如果为0则认为已完全处理。




三、Ack机制的使用


1.开启Ack机制

1.spout发射tuple的时候指定messageId
2.spout对发射的tuple进行缓存,否则spout无法获取发送失败的数据进行重发,
(这里到底系统里有没有缓存没有成功处理的tuple,比如接口conf.setMaxSpoutPending()是否只缓存了条数还是原始数据还要去查证一下)
3.spout要重写BaseRichSpout的fail和ack方法,spout根据messageId对于成功处理的tuple从缓存队列中删除,对于失败的tuple选择重发或做其它处理;
4.如果使用BasicBolt,BasicOutputCollector在emit新的tuple时自动与源tuple锚定,execute方法结束时源tuple会被自动ack或fail;
使用RichBolt在emit数据的时需显示指定该数据的源tuple,以保持tracker链路,即collector.emit(oldTuple, newTuple);
并且需要在execute执行成功后调用OutputCollector.ack(tuple), 当失败处理时,执行OutputCollector.fail(tuple);
5.设置acker数大于0,conf.setNumAckers(>0);

2.关闭Ack机制

1.在Tuple层面去掉可靠性。在发射Tuple的时候不指定MessageID来达到不不跟踪这个Tuple的目的
2.如果对于一个Tuple树里面的某一部分到底成不成功不是很关心,那么可以在Bolt发射这些Tuple的时候不锚定它们。
这样这些Tuple就不在Tuple树里面,也就不会被跟踪了。
3.把Config.TOPOLOGY_ACKERS设置成0。在这种情况下,Storm会在Spout发射一个Tuple之后马上调用Spout的ack方法,
也就是说这个Tuple树不会被跟踪。

示例:

    public class RandomSentenceSpout extends BaseRichSpout {  
        private SpoutOutputCollector _collector;  
        private Random _rand;  
        private ConcurrentHashMap<UUID, Values> _pending;  
      
        public void open(Map map, TopologyContext topologyContext, SpoutOutputCollector spoutOutputCollector) {  
            _collector = spoutOutputCollector;  
            _rand = new Random();  
            _pending = new ConcurrentHashMap<UUID, Values>();  
        }  
      
        public void nextTuple() {  
            Utils.sleep(1000);  
            String[] sentences = new String[] {  
                    "I write php",  
                    "I learning java",  
                    "I want to learn swool and tfs"  
            };  
            String sentence = sentences[_rand.nextInt(sentences.length)];  
            Values v = new Values(sentence);  
            UUID msgId = UUID.randomUUID();  
            this._pending.put(msgId, v);//spout对发射的tuple进行缓存  
            _collector.emit(v, msgId);//发射tuple时,添加msgId  
      
        }  
      
        public void declareOutputFields(OutputFieldsDeclarer outputFieldsDeclarer) {  
            outputFieldsDeclarer.declare(new Fields("world"));  
        }  
      
        public void ack(Object msgId) {  
            this._pending.remove(msgId);//对于成功处理的tuple从缓存队列中删除  
      
        }  
      
        public void fail(Object msgId) {  
            this._collector.emit(this._pending.get(msgId), msgId);//当消息处理失败了,重新发射,当然也可以做其他的逻辑处理  
      
        }  
    }  
      
    public class SplitSentence extends BaseRichBolt {  
        OutputCollector _collector;  
      
        public void prepare(Map map, TopologyContext topologyContext, OutputCollector outputCollector) {  
            _collector = outputCollector;  
        }  
      
        public void execute(Tuple tuple) {  
            String sentence = tuple.getString(0);  
            for (String word : sentence.split(" "))  
                _collector.emit(tuple, new Values(word));//发射tuple时进行锚定  
      
            _collector.ack(tuple);//对处理完的tuple进行确认  
      
        }  
      
        public void declareOutputFields(OutputFieldsDeclarer outputFieldsDeclarer) {  
            outputFieldsDeclarer.declare(new Fields("word"));  
        }  
    }  

1. Bolt挂掉了,一个tuple没有被ack,storm的超时机制在超时之后会把这个tuple标记为失败,从而可以重新处理。
2. Acker挂掉了: 这种情况下由这个acker所跟踪的所有spout tuple都会超时,也就会被重新处理。
3. Spout挂掉了: 在这种情况下给spout发送消息的消息源负责重新发送这些消息。


参考:

http://blog.csdn.net/cm_chenmin/article/details/52925225

http://blog.csdn.net/tototuzuoquan/article/details/73539952


没有更多推荐了,返回首页