Storm项目03——体会消息确认机制,Spout的可靠性验证

        Spout是Storm数据流的入口,在设计拓扑时,一件很重要的事情就是需要考虑消息的可靠性,如果消息不能被处理而丢失是很严重的问题。
        通过此项目我们可得知,如果在第一个bolt处理的时候出现异常,我们可以让整个数据进行重发,但是如果在第二个bolt处理的时候出现了异常,那么我们也会让对应的spout里的数据重发,这样就会出现事务的问题,我们就需要进行判断或者是进行记录。 如果是数据入库的话,可以与原ID进行比对。 如果是事务的话在编写代码时,尽量就不要进行拆分tuple。 或者使用storm的Trident框架。

 MessageSpout

package com.xnmzdx.storm.spout;

import java.util.Map;

import backtype.storm.spout.SpoutOutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.IRichSpout;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Values;
/**
 * 体会消息确认机制
 * @author zyt
 *
 */
public class MessageSpout implements IRichSpout {

	private static final long serialVersionUID = 7271404214963078223L;

	//spout向外发送数据的连接器collector
	private SpoutOutputCollector collector;
	
	private int index = 0;
	//定义需要被发送的数据(一些单词,以数组的方式存储)
	private String[] subjects = new String[]{
		"groovy,oeacnbase",
		"openfire,restful",
		"flume,activite",
		"hadoop,hbase",
		"spark,sqoop"
	};
	
	//初始化spout的collector
	public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
		this.collector = collector;
	}
	
	/**
	 * 轮询
	 * 遍历存储数据的数组,将每个元素挨个发送出去
	 */
	public void nextTuple() {
		if(index < subjects.length) {
			String sub = subjects[index];
			//发送消息时,添加发送了messageId
			collector.emit(new Values(sub),index);//collector.emit(tuple, messageId)
			//因为添加了messageId,则ack和fail方法当Tuple被正确地处理了或发生了错误时将会被调用
			index++;
		}
	}
	
	/**
	 * 定义发送的数据的字段
	 */
	public void declareOutputFields(OutputFieldsDeclarer declarer) {
		//以字段"subjects"发送数据
		declarer.declare(new Fields("subjects"));
	}
	
	/**
	 * 消息确认机制的体现
	 * 消息发送成功的回调方法
	 */
	public void ack(Object msgId) {
		System.out.println("【消息发送成功!!!】(mesgId=" + msgId + ")");
	}
	
	/**
	 * 消息确认机制的体现
	 * 消息tuple处理失败后的回调方法
	 */
	public void fail(Object msgId) {
		System.out.println("【消息发送失败!!!】(msgId=" + msgId + ")");
		System.out.println("【重发进行中...】");
		//一旦某个bolt中数据处理出现异常,也就是消息发送失败,spout就会重新发送数据
		collector.emit(new Values(subjects[(Integer)msgId]),msgId);
		System.out.println("【重发成功!!!】");
	}

	public void close() {
		
	}

	public void activate() {
		
	}

	public void deactivate() {
		
	}

	public Map<String, Object> getComponentConfiguration() {
		return null;
	}

}

SpliterBolt

package com.xnmzdx.storm.bolt;

import java.util.Map;

import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.IRichBolt;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;

public class SpliterBolt implements IRichBolt{

	private static final long serialVersionUID = 323068030016246800L;

	//Bolt向下发送数据的collector
	private OutputCollector collector;
	
	/**
	 * 初始化
	 */
	public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
		//初始化Bolt的collector
		this.collector = collector;
	}

	private boolean flag = false;
	
	public void execute(Tuple input) {
		try {
			//按字段"subjects"接收MessageSpout发出的数据
			String subjects = input.getStringByField("subjects");
			//以下这段是制造错误,用以验证消息确认机制
//			if(!flag && subjects.equals("flume,activite")) {
//				flag = true;
//				int a = 1/0;
//			}
			//按逗号分割接收到的数据,放入数组中
			String[] words = subjects.split(",");
			//遍历数组,将每一个单词发送出去
			for(String word : words) {
				//注意这里循环发送消息,要携带Tuple对象(也就是input),用于处理异常时重发策略
				collector.emit(input,new Values(word));
			}
			//描记。若发送成功,则调用消息发送成功的回调方法  ack()方法
			collector.ack(input);
		}catch(Exception e) {
			e.printStackTrace();
			//描记。发现异常,即发送失败,则调用消息发送失败的回调方法  fail()方法
			collector.fail(input);
		}
		
	}
	
	public void declareOutputFields(OutputFieldsDeclarer declarer) {
		//按字段"word"发送数据
		declarer.declare(new Fields("word"));
	}

	public void cleanup() {
		
	}

	public Map<String, Object> getComponentConfiguration() {
		return null;
	}

}

WriteBolt

package com.xnmzdx.storm.bolt;

import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;

import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.IRichBolt;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;

public class WriteBolt implements IRichBolt {

	private static final long serialVersionUID = -2447898411180546403L;

	//Bolt向下发送数据的连接器collector
	private OutputCollector collector;
	
	//字符写流
	private FileWriter writer;
	
	/**
	 * 初始化Bolt,即初始化它定义的所有属性
	 */
	public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
		this.collector = collector;
		try {
			//将管道插入文件D:\\message.txt中,即将数据写入该文件中
			writer = new FileWriter("D:\\message.txt");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private boolean flag = false;
	
	public void execute(Tuple input) {
		//接收SpliterBolt发出的数据,这里不是按字段接收的,而是取出每次收到的第一条数据(每次也就收到一条数据)
		String word = input.getString(0);
		try {
			//以下是人为制造Bolt处理数据出错的情况,来测试消息确认机制
			if(!flag && word.equals("hadoop")) {
				flag = true; //让它变成true,是为了spout重新发送数据后,不再进入这个故意制造的错误中,Bolt可以正常处理数据
				int a = 1/0;//此处出错,抛异常
			}
			writer.write(word);
			writer.write("\n");
			writer.flush();
		}catch(Exception e) {
			e.printStackTrace();
			collector.fail(input);//程序运行至此说明抛出异常,即Bolt在处理数据时出现了问题,所以应该调用消息发送失败的回调方法,spout就会重新发送消息
		}
		collector.emit(input,new Values("word"));//这一句我觉得没有用,为什么还要向下发送,这已经是最后一个Bolt了?!将其注释掉后,程序仍能正常运行!!!
		collector.ack(input);//消息发送成功(即Bolt正常处理了数据),调用此方法
	}

	public void cleanup() {
		
	}

	public void declareOutputFields(OutputFieldsDeclarer declarer) {
		
	}

	public Map<String, Object> getComponentConfiguration() {
		return null;
	}

}

MessageTopology

package com.xnmzdx.storm.topology;

import com.xnmzdx.storm.bolt.SpliterBolt;
import com.xnmzdx.storm.bolt.WriteBolt;
import com.xnmzdx.storm.spout.MessageSpout;

import backtype.storm.Config;
import backtype.storm.LocalCluster;
import backtype.storm.topology.TopologyBuilder;

public class MessageTopology {

	public static void main(String[] args) throws InterruptedException {
		//以下是一个topology的常规配置,记住就行!!!
		TopologyBuilder builder = new TopologyBuilder();
		builder.setSpout("spout", new MessageSpout());
		builder.setBolt("split-bolt", new SpliterBolt()).shuffleGrouping("spout");
		builder.setBolt("write-bolt", new WriteBolt()).shuffleGrouping("split-bolt");

		Config config = new Config();
		config.setDebug(true);
		//本地集群模式
		LocalCluster cluster = new LocalCluster();
		cluster.submitTopology("message", config, builder.createTopology());
		Thread.sleep(10000);
		cluster.killTopology("message");
		cluster.shutdown();
	}

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值