1、首先我们先做一个假数据文件,方便处理,取名track.log(将这个文件必须放到HelloStorm工程下,不能放到包下)
2、Spout类
package com.demo.test;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Map;
import org.apache.storm.spout.SpoutOutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.IRichSpout;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Values;
public class MySpout implements IRichSpout {
private static final long serialVersionUID = 1L;
FileInputStream fis;
InputStreamReader isr;
BufferedReader br;
SpoutOutputCollector collector = null;
String str = null;
public void nextTuple() {
try {
while ((str = this.br.readLine()) != null) {
// 过滤动作
collector.emit(new Values(str, str.split("\t")[1]));
}
} catch (Exception e) {
}
}
public void close() {
try {
br.close();
isr.close();
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
try {
this.collector = collector;
this.fis = new FileInputStream("track.log");
this.isr = new InputStreamReader(fis, "UTF-8");
this.br = new BufferedReader(isr);
} catch (Exception e) {
e.printStackTrace();
}
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("log", "session_id"));
}
public Map<String, Object> getComponentConfiguration() {
return null;
}
public void ack(Object msgId) {
System.out.println("spout ack:" + msgId.toString());
}
public void activate() {
}
public void deactivate() {
}
public void fail(Object msgId) {
System.out.println("spout fail:" + msgId.toString());
}
}
3、Bolt类
package com.demo.test;
import java.util.Map;
import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.IRichBolt;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Tuple;
public class MyBolt implements IRichBolt {
private static final long serialVersionUID = 1L;
OutputCollector collector = null;
int num = 0;
String valueString = null;
public void cleanup() {
}
public void execute(Tuple input) {
try {
valueString = input.getStringByField("log");
if (valueString != null) {
num++;
System.err.println(input.getSourceStreamId() + " " + Thread.currentThread().getName() + "--id="
+ Thread.currentThread().getId() + " lines :" + num + " session_id:"
+ valueString.split("\t")[1]);
}
collector.ack(input);
// Thread.sleep(2000);
} catch (Exception e) {
collector.fail(input);
e.printStackTrace();
}
}
public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
this.collector = collector;
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields(""));
}
public Map<String, Object> getComponentConfiguration() {
return null;
}
}
4、拓扑类(Topology)
package com.demo.test;
import org.apache.storm.Config;
import org.apache.storm.LocalCluster;
import org.apache.storm.StormSubmitter;
import org.apache.storm.generated.AlreadyAliveException;
import org.apache.storm.generated.AuthorizationException;
import org.apache.storm.generated.InvalidTopologyException;
import org.apache.storm.topology.TopologyBuilder;
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("spout", new MySpout(), 1);
// shuffleGrouping其实就是随机往下游去发,不自觉的做到了负载均衡
builder.setBolt("bolt", new MyBolt(), 2).shuffleGrouping("spout");
// fieldsGrouping其实就是MapReduce里面理解的Shuffle,根据fields求hash来取模
// builder.setBolt("bolt", new MyBolt(), 2).fieldsGrouping("spout", new Fields("session_id"));
// 只往一个里面发,往taskId小的那个里面去发送
// builder.setBolt("bolt", new MyBolt(), 2).globalGrouping("spout");
// 等于shuffleGrouping
// builder.setBolt("bolt", new MyBolt(), 2).noneGrouping("spout");
// 广播
// builder.setBolt("bolt", new MyBolt(), 2).allGrouping("spout");
// Map conf = new HashMap();
// conf.put(Config.TOPOLOGY_WORKERS, 4);
Config conf = new Config();
conf.setDebug(false);
conf.setMessageTimeoutSecs(30);
if (args.length > 0) {
try {
StormSubmitter.submitTopology(args[0], conf, builder.createTopology());
} catch (AlreadyAliveException e) {
e.printStackTrace();
} catch (InvalidTopologyException e) {
e.printStackTrace();
} catch (AuthorizationException e) {
e.printStackTrace();
}
} else {
LocalCluster localCluster = new LocalCluster();
localCluster.submitTopology("mytopology", conf, builder.createTopology());
}
}
}
然后我们进行分析拓扑类中的输出
我们给出的假数据中共有89条记录,因为我们的“水龙头”也就是Spout只有一个,也就是一个进程,所以我们设置它为1,如果你非要设置为2,意思就是相当于把原始数据强行复制了一份(说的不是很对,但意思就是那个)变成了两个进程。然后我们来看setBolt,我们先用了一个shuffleGrouping,这是一个随机分发,其中我们设置了线程为2,我们来看运行结果:
当进程为1时:
builder.setSpout("spout", new MySpout(), 1);
// shuffleGrouping其实就是随机往下游去发,不自觉的做到了负载均衡
builder.setBolt("bolt", new MyBolt(), 2).shuffleGrouping("spout");
运行结果:
这是部分结果,其他结果都一样,因为我们的需求中是截取了假数据中间的一串字符,也就是我们的session_id,我们会看到,线程号只有18和20两个,这是我们设置的线程数为2,而且处理了所有假数据(89条)文件中的数据,而且随机的将不同的数据分发给不同的线程。当然,当我们设置线程为3时,就会把这些数据分发给三个线程(18、20、还有个别的线程号哦)。那么我们来看当进程设置为2时:
builder.setSpout("spout", new MySpout(), 2);
// shuffleGrouping其实就是随机往下游去发,不自觉的做到了负载均衡
builder.setBolt("bolt", new MyBolt(), 2).shuffleGrouping("spout");
细数你会发现这些数据翻了一倍。
接下来我们来看fieldsGrouping,按字段分组,相同的字段会分到同一个线程中,我们先设置它线程为2
builder.setSpout("spout", new MySpout(), 1);
// shuffleGrouping其实就是随机往下游去发,不自觉的做到了负载均衡
// builder.setBolt("bolt", new MyBolt(), 2).shuffleGrouping("spout");
// fieldsGrouping其实就是MapReduce里面理解的Shuffle,根据fields求hash来取模
builder.setBolt("bolt", new MyBolt(), 2).fieldsGrouping("spout", new Fields("session_id"));
我们来看结果:
我们会看到相同的字段会被分到同一个线程中,比如结尾时UYU7的字段,它只会被分到18这个线程中,在20中是找不到的,并不是一个线程只有一个相同字段,而是相同字段会被分到同一个线程。当他的线程为3或者是更多时道理相同。
接下来我们来看globalGrouping,全局分组, 这个tuple被分配到storm中的一个bolt的其中一个task。再具体一点就是分配给id值最低的那个task。我们来看运行结果:
builder.setSpout("spout", new MySpout(), 1);
// shuffleGrouping其实就是随机往下游去发,不自觉的做到了负载均衡
// builder.setBolt("bolt", new MyBolt(), 2).shuffleGrouping("spout");
// fieldsGrouping其实就是MapReduce里面理解的Shuffle,根据fields求hash来取模
// builder.setBolt("bolt", new MyBolt(), 2).fieldsGrouping("spout", new Fields("session_id"));
// 只往一个里面发,往taskId小的那个里面去发送
builder.setBolt("bolt", new MyBolt(), 2).globalGrouping("spout");
我们看到他都会分给线程为18的task中,而不是20中,因为数据小,所以一个就可以都处理完。这就是针对这三个streamgroup的运行演示。