You’ll create a spout that sends 100 random transaction IDs, and a bolt that fails for80% of tuples received
Imagine you are processing bank transactions, and youhave the following requirements:
-
If a transaction fails, resend the message.
-
If the transaction fails too many times, terminate the topology.
-
you can find the complete example at ch04-spout examples
可靠的消息传输要在spout中的ack和fail方法中做好控制,同时在bolts中成功调用ack和fail给予spout合理的回应
package banktransactions;
import backtype.storm.Config;
import backtype.storm.LocalCluster;
import backtype.storm.topology.TopologyBuilder;
/**
* To run this topology you should execute this main as:
* java -cp theGeneratedJar.jar banktransactions.Topology
*
* @author StormBook
*
*/
public class Topology {
public static void main(String[] args) throws InterruptedException {
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("transactions-spout", new TransactionsSpouts());
builder.setBolt("random-failure-bolt", new RandomFailureBolt()).
shuffleGrouping("transactions-spout");
LocalCluster cluster = new LocalCluster();
Config conf = new Config();
conf.setDebug(true);
cluster.submitTopology("transactions-test", conf, builder.createTopology());
while(true){
//Will wait for a fail
Thread.sleep(1000);
}
}
}
package banktransactions;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import org.apache.log4j.Logger;
import backtype.storm.spout.SpoutOutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseRichSpout;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Values;
public class TransactionsSpouts extends BaseRichSpout{
private static final Integer MAX_FAILS = 2;
Map<Integer,String> messages;
Map<Integer,Integer> transactionFailureCount;
Map<Integer,String> toSend;
private SpoutOutputCollector collector;
static Logger LOG = Logger.getLogger(TransactionsSpouts.class);
public void ack(Object msgId) {
messages.remove(msgId);//当消息成功处理后从消息栈中删除
LOG.info("Message fully processed ["+msgId+"]");
}
public void close() {
}
public void fail(Object msgId) {
if(!transactionFailureCount.containsKey(msgId))
throw new RuntimeException("Error, transaction id not found ["+msgId+"]");
Integer transactionId = (Integer) msgId;
//Get the transactions fail
Integer failures = transactionFailureCount.get(transactionId) + 1;
if(failures >= MAX_FAILS){
//If exceeds the max fails will go down the topology
throw new RuntimeException("Error, transaction id ["+transactionId+"] has had many errors ["+failures+"]");
}
//If not exceeds the max fails we save the new fails quantity and re-send the message
transactionFailureCount.put(transactionId, failures);
toSend.put(transactionId,messages.get(transactionId));
LOG.info("Re-sending message ["+msgId+"]");
}
public void nextTuple() {
if(!toSend.isEmpty()){
for(Map.Entry<Integer, String> transactionEntry : toSend.entrySet()){
Integer transactionId = transactionEntry.getKey();
String transactionMessage = transactionEntry.getValue();
collector.emit(new Values(transactionMessage),transactionId);
}
/*
* The nextTuple, ack and fail methods run in the same thread, so
* we can considerate the clear method atomic
*/
toSend.clear();
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {}
}
public void open(Map conf, TopologyContext context,
SpoutOutputCollector collector) {
Random random = new Random();
messages = new HashMap<Integer, String>();
toSend = new HashMap<Integer, String>();
transactionFailureCount = new HashMap<Integer, Integer>();
for(int i = 0; i< 100; i++){
messages.put(i, "transaction_"+random.nextInt());
transactionFailureCount.put(i, 0);
}
toSend.putAll(messages);
this.collector = collector;
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("transactionMessage"));
}
}
package banktransactions;
import java.util.Map;
import java.util.Random;
import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseRichBolt;
import backtype.storm.tuple.Tuple;
public class RandomFailureBolt extends BaseRichBolt {
private static final Integer MAX_PERCENT_FAIL = 80;
Random random = new Random();
private OutputCollector collector;
public void execute(Tuple input) {
Integer r = random.nextInt(100);
if(r > MAX_PERCENT_FAIL){
collector.ack(input);
}else{
collector.fail(input);
}
}
public void prepare(Map stormConf, TopologyContext context,
OutputCollector collector) {
this.collector = collector;
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
}
}