The source code is the singlejoinexample in the storm-starter of the analysis, aggregating two simple streams: [Id,gender] and [id,age] after join [Id,gender,age]The parsing process is written directly inside the comment, so it is no longer separated.
Singlejoinbolt.java
Package Com.wxt.storm.SingleJoinExample.bolt;
Import java.util.ArrayList;
Import Java.util.HashMap;
Import Java.util.HashSet;
Import java.util.List;
Import Java.util.Map;
Import Java.util.Set;
Import Backtype.storm.Config;
Import Backtype.storm.generated.GlobalStreamId;
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.Fields;
Import Backtype.storm.tuple.Tuple;
Import Backtype.storm.utils.TimeCacheMap;
public class Simplejoinbolt extends Baserichbolt {private Outputcollector _collector;
Private fields _outfields;
Private fields _idfields;
int _numsources;
Map<string,globalstreamid> _fieldlocations; Keep recently active objects in memory//the same ID two data streams are likely not to be emitted at the same point in time, because the bolt is receiving streaming data from two data sources, so the data flow needs to be cached first until all//id the same data source is later aggregated. After the aggregation process is done, the corresponding tuple information is removed from the cache. In Timecachemap<list<object>,map<globalstreamid,tuple>> _penDing
Fields that are passed in are aggregated and will be output by the public simplejoinbolt (field outfields) {this._outfields=outfields; The public void execute (tuple tuple) {//TODO auto-generated Method Stub//Gets the _idfields field from a Tuple if it does not exist in the queue waiting to be processed _pending
, a row of list<object> Id=tuple.select (_idfields) is added;
Globalstreamid streamid=new Globalstreamid (Tuple.getsourcecomponent (), Tuple.getsourcestreamid ());
Prints the source of the current processing tuple spout System.out.println ("tuple Source:" +tuple.getsourcecomponent ()); Prints the current tuple System.out.println ("Received tuples:" +tuple.getfields (). Get (0) + "=" +tuple.getvalues (). Get (0) + "," +tuple.getfields (
). Get (1) + "=" +tuple.getvalues (). Get (1)); If the tuple for this ID does not already exist in the current pending, the record is added to the IF (!_pending.containskey (ID)) {_pending.put (ID, new hashmap<
Globalstreamid,tuple> ());
}//Gets the HashMap object map<globalstreamid,tuple> parts=_pending.get (ID) for the current Globalstreamid from the _pending queue; If Streamid already contains it, throws an exception, receives a tuple of the same ID as two in the same spout, or adds the Streamid to parts if (Parts.containskey (Streamid)) {throw New RunTimeexception ("Received same side of single join Twice");
} parts.put (Streamid, tuple);
If the parts already contains the number of aggregated data sources, remove this record from the _pending queue if (parts.size () ==_numsources) {_pending.remove (ID);
List<object> joinresult=new arraylist<object> ();
for (String outfield:_outfields) {Globalstreamid loc=_fieldlocations.get (outfield);
Joinresult.add (Parts.get (Loc). Getvaluebyfield (outfield));
}//Output aggregation result System.out.print (the tuple with ID value "+id.get (0) +" in the two relational stream has been received, the result of the aggregation is: ");
for (Object Obj:joinresult) {System.out.print (obj+ "");
} System.out.println ();
Multi-anchor _collector.emit (New arraylist<tuple> (Parts.values ()), joinresult);
For (Tuple part:parts.values ()) {_collector.ack (part);
}}else{System.out.println ("Only a tuple with an ID value of" +id+ "is collected from one relationship flow, not a join operation"); }} public void Prepare (MAP conf, topologycontext context, Outputcollector Collector) {//TODO auto-generated method Stub _fieldlocations = new Hashmap<string, GloBalstreamid> ();
This._collector=collector; Creates a Timecachemap object that sets the timeout callback interface for a tuple processing failure when the fail message int timeout= ((number) Conf.get (config.topology_message_timeout_
SECS)). Intvalue (); _pending=new timecachemap<list<object>,map<globalstreamid,tuple>> (timeout,new ExpireCallback ()
);
Record the number of data sources _numsources=context.getthissources (). Size ();
Set<string> Idfields=null;
Traverse different data sources in Topologycontext: Genderspout and Agespout System.out.println (Context.getthissources (). KeySet ()); For (Globalstreamid source:context.getThisSources (). KeySet ()) {//Get the Fields field ID of the public, save it to _idfields.
Context.getcomponentoutputfields (Source.get_componentid (), Source.get_streamid ());
Fields:[id,gender],[id,age] set<string> setfields=new hashset<string> (Fields.toList ());
if (idfields==null) {idfields=setfields;
}else{//Seek intersection Idfields.retainall (Setfields);
System.out.println (Idfields); }//The data source of the field in _outfields is recorded and saved to a hashmap _fielDlocations, in order to get the corresponding field value after aggregation for (string outfield:_outfields) {for (string sourcefield:fields) {if (Outfield.equals (
SourceField)) {_fieldlocations.put (outfield, source); }}}//Print Result: Gender=globalstreamid (componentid=gender-spout,streamid=default)//age=globalstreamid (ComponentI
D=age-spout,streamid=default) System.out.println (_fieldlocations);
} _idfields=new Fields (new arraylist<string> (Idfields));
if (_fieldlocations.size ()!=_outfields.size ()) {throw new RuntimeException ("Cannot find all outfields among sources"); }} public void Declareoutputfields (Outputfieldsdeclarer declarer) {//TODO auto-generated method stub DECLARER.D
Eclare (_outfields); } Private class Expirecallback implements Timecachemap.expiredcallback<list<object>, Map<globalstreamid, tuple>>{public void expire (list<object> key, Map<globalstreamid, tuple> tuples) {//TODO Auto-ge Nerated method stub for (Tuple Tuple:tupLes.values ()) {_collector.fail (tuple);
}
}
}
}
Singlejoinexample.java (topology)
Package com.wxt.storm.SingleJoinExample;
Import Com.wxt.storm.SingleJoinExample.bolt.SimpleJoinBolt;
Import Backtype.storm.Config;
Import Backtype.storm.LocalCluster;
Import Backtype.storm.testing.FeederSpout;
Import Backtype.storm.topology.TopologyBuilder;
Import Backtype.storm.tuple.Fields;
Import backtype.storm.tuple.Values;
Import Backtype.storm.utils.Utils; public class Singlejoinexample {public static void main (string[] args) {feederspout genderspout = new Feederspou
T (new fields ("id", "gender"));
Feederspout agespout = new Feederspout ("id", "age");
Topologybuilder builder = new Topologybuilder ();
Builder.setspout (Commonkeys.gender_spout, genderspout);
Builder.setspout (Commonkeys.age_spout, agespout); Builder.setbolt (Commonkeys.join_bolt, New Simplejoinbolt ("Gender", "Age")). Fieldsgrouping (
Commonkeys.gender_spout, new fields ("IDs")). Fieldsgrouping (Commonkeys.age_spout, new fields ("id")); Config conf = NEW Config ();
Conf.setdebug (TRUE);
Localcluster cluster = new Localcluster ();
Cluster.submittopology (commonkeys.join_topology, Conf, builder.createtopology ());
for (int i = 0; i <; i++) {String gender;
if (i% 2 = = 0) {gender = "male";
} else {gender = "female";
} genderspout.feed (New Values (I, gender));
} for (int i = 9; I >= 0; i--) {Agespout.feed (new Values (i, i + 20));
} utils.sleep (2000);
Cluster.shutdown ();
}
}