Storm 1.5 有保障机制的数据处理

Storm提供了一种API能够保证spout发送出来的每个tuple都能够执行完整的处理过程。在一个topology中,一个spout数据流会被分割成任意多个数据流,取决于下游bolt的行为。如果发生执行失败会怎样?例如一个负责将数据持久化到数据库的bolt,怎么处理数据库更新失败的情况?

一、spout的可靠性
在Storm中,可靠的消息处理机制是从spout开始的。一个提供了可靠的处理机制的spout需要记录它发射出去的tuple,当下游bolt处理tuple或者子tuple失败时,spout能够重新发射。子tuple可以理解为bolt处理spout发射的原始tuple后,作为结果发射出去的tuple。另外一个视角来看,可以将spout发射的数据流看作一个tuple树的主干,如下图所示:



在图中,实线部分表示从spout发射的原始主干tuple。虚线部分表示的子tuple都是源自于原始tuple。这样产生的图形叫做tuple树。在有保障数据的数据处理过程中,bolt每收到一个tuple,都需要向上游确认应答(ask)者报错。对主干tuple中的一个tuple,如果tuple树上的每个bolt进行了确认应答,spout会调用ask方法来标明这个消息已经完全处理了。如果树种的任何一个bolt处理tuple报错,或者处理超时,spout会调用fail方法。
Storm的Ispout接口定义了三个可靠性相关的API:nextTuple、ask和fail。

public interface ISpout extends Serializable{
    public abstract void open(Map map, TopologyContext topologycontext, SpoutOutputCollector spoutoutputcollector);
    public abstract void close();
    public abstract void activate();
    public abstract void deactivate();
    public abstract void nextTuple();
    public abstract void ack(Object obj);
    public abstract void fail(Object obj);
}

Storm通过调用Ispout的nextTuple()发送一个tuple。为实现可靠的消息处理,首先要给每个发出的tuple带上唯一的ID,并且将ID作为参数传递给SpoutOutputCollector的emit()方法:
collector.emit(new Values("value1","value2"), msgId);

给tuple指定ID告诉Storm系统,无论执行成功还是失败,spout都要接收tuple树上所有节点返回的通知。如果处理成功,spout的ask方法将会对编号是ID的消息应答确认,如果执行失败或者超时,会调用fail方法。

二、bolt的可靠性
bolt要实现可靠的消息处理机制包含两个步骤:
1.当发射衍生的tuple时,需要锚定读入的tuple
2.当处理消息成功或失败时分别确认应答或者报错

锚定一个tuple的意思是,建立读入tuple和衍生出的tuple之间的对应关系,这样下游的bolt就可以通过应答确认、报错和超时来加入到tuple树结构中。
可以通过调用OutputCollector中的emit()的一个重载函数锚定一个或者一组tuple:

collector.emit(tuple, newValues(word));

这里,我们将读入的tuple和发射的新tuple锚定起来,下游的bolt就需要对输出的tuple进行确认应答或者报错。另外一个emit()方法会发射非锚定的tuple:
collector.emit(newValues(word));

非锚定的tuple不会对数据流的可靠性起作用。如果一个非锚定的tuple在下游处理失败,原始的根tuple不会重新发送。
当处理完成或者发送了新tuple之后,可靠数据流中的bolt需要应答读入的tuple:

this.collector.ask(tuple);

如果处理失败,这样的话spout必须发射tuple,bolt就要明确地对处理失败的tuple报错:
this.collector.fail(tuple);

如果是因为超时的原因,或者显示调用OutputCollector.fail()方法,spout都会重新发送原始tuple。

三、可靠的单词计数
为了进一步说明可控性,我们增强SentenceSpout类,支持可靠的tuple发射方式。需要记录所有发送的tuple,并且分配一个唯一的ID。我们需要使用HashMap<UUID, Values>来存储已发送待确认的tuple。每当发送一个新的tuple,分配一个唯一的标识符并且存储在我们的HashMap中。当收到一个确认消息,从待确认列表中删除该tuple。如果收到报错,重新发送tuple:

public class SentenceSpout extends BaseRichSpout{
	private ConcurrentHashMap<UUID, Values> pending;
	private SpoutOutputCollector collector;
	private String[] sentences = { 
			"my dog has fleas", 
			"i like cold beverages",
			"the dog ate my homework",
			"don't have a cow man",
			"i don't think i like fleas"
	};
	private int index = 0;
	
	public void nextTuple() {
		Values values = new Values(sentences[index]);
		UUID msgId = UUID.randomUUID();
		this.pending.put(msgId, values);
		this.collector.emit(values, msgId);
		
		index++;
		if(index >= sentences.length){
			index=0;
		}
		Utils.sleep(1);
	}

	public void open(Map arg0, TopologyContext arg1, SpoutOutputCollector arg2) {
		this.collector = arg2;
		this.pending = new ConcurrentHashMap<UUID, Values>();
	}

	public void declareOutputFields(OutputFieldsDeclarer declarer) {
		declarer.declare(new Fields("sentence"));
	}

	@Override
	public void fail(Object msgId) {
		this.collector.emit(this.pending.get(msgId), msgId);
	}

	@Override
	public void ack(Object msgId) {
		this.pending.remove(msgId);
	}
}

为支持有保障的处理,需要修改bolt,将输出的tuple和输入的tuple锚定,并且应答确认输入的tuple:
public class SplitSentenceBolt extends BaseRichBolt{
	private OutputCollector collector;

	public void execute(Tuple tuple) {
		String sentence = tuple.getStringByField("sentence");
		String[] words = sentence.split(" ");
		for(String word : words){
			this.collector.emit(new Values(word));
		}
		this.collector.ack(tuple);
	}

	public void prepare(Map map, TopologyContext topologycontext,
			OutputCollector outputcollector) {
		this.collector=outputcollector;
	}

	public void declareOutputFields(OutputFieldsDeclarer outputfieldsdeclarer) {
		outputfieldsdeclarer.declare(new Fields("word"));
	}
}


总结:在没有安装和搭建Storm集群的情况下,我们使用Storm的核心API建立了一个简单的分布式计算程序,覆盖了Storm特定集的大部分内容。Storm的本地模式非常强大,简化了开发,提供了开发效率。但要感受Storm真正的威力和水平扩展性,还是需要将程序部署在真实的集群上。


以上来自:

猜你喜欢

转载自margaret0071.iteye.com/blog/2360740
1.5
今日推荐