storm(二)特性、实战、集群脚本

Strom的参考项目

官方例子

一 Spout特性

Spout的tail特性

tail适合文本源
如果是mq(消息队列)源的话,该特性没必要
如果是消息队列,spout只需要执行消息队列的consumer的代码就可以了,相当于就是一个consumer的角色

小结
测试发现正常readLine读到Null后,就结束了。文本再怎么变也不会读取到新数据了,这里应该是Storm做了什么处理

分组策略

stream grouping用来定义一个stream应该如何分配给Bolts上面的多个Executors(多线程、多并发)。
Storm里面有7种类型的stream grouping

  1. Shuffle Grouping: 随机分组,轮询,平均分配。随机派发stream里面的tuple,保证每个bolt接收到的tuple数目大致相同
  2. Fields Grouping:按字段分组,比如按userid来分组,具有同样userid的tuple会被分到相同的Bolts里的一个task,而不同的userid则会被分配到不同的bolts里的task。
  3. All Grouping:广播发送,对于每一个tuple,所有的bolts都会收到。在这里插入图片描述
  4. Global Grouping:全局分组,这个tuple被分配到storm中的一个bolt的其中一个task。再具体一点就是分配给id值最低的那个task。
  5. Non Grouping:不分组,这stream grouping个分组的意思是说stream不关心到底谁会收到它的tuple。目前这种分组和Shuffle grouping是一样的效果。在多线程情况下不平均分配。
  6. Direct Grouping:直接分组,这是一种比较特别的分组方法,用这种分组意味着消息的发送者指定由消息接收者的哪个task处理这个消息。只有被声明为Direct Stream的消息流可以声明这种分组方法。而且这种消息tuple必须使用emitDirect方法来发射。消息处理者可以通过TopologyContext来获取处理它的消息的task的id (OutputCollector.emit方法也会返回task的id)。
  7. Local or shuffle grouping:如果目标bolt有一个或者多个task在同一个工作进程中,tuple将会被随机发送给这些tasks。否则,和普通的Shuffle Grouping行为一致。

读文件案例的问题思考

读文件的方式不可取,只有学习阶段才会用
在这里插入图片描述

读文件的缺陷
在这里插入图片描述

并发度

如下代码表示会有5个spout的实例存在,需要注意会不会重复发数据

builder.setSpout("spout", new RandomSentenceSpout(), 5);

在这里插入图片描述

并发度实际并发数的区别
在这里插入图片描述
work进程只能服务于一个topology

并发度官方例子

在这里插入图片描述
在这里插入图片描述
可以看出默认是平均分配

动态设置并发度

在这里插入图片描述

线程安全问题

参考:Storm的进程、线程、任务数和线程安全问题研究(一篇明白)

小结

  • component 中的成员变量在 prepare/execute(open/nextTuple) 方法中被操作,是线程安全的(实例不会被共享)
  • component 中的静态变量在 prepare/execute(open/nextTuple) 方法中被操作,是非线程安全

二 案例实战-PVUV

在这里插入图片描述

方案一

因为shuffleGrouping轮询分配

//驱动函数中配置如下:
builder.setSpout("PVSpout", new PVSpout(), 1);
builder.setBolt("PVBolt1", new PVBolt1(), 4).shuffleGrouping("PVSpout");
在PVBolt1中输出时
System.err.println("threadid:" + Thread.currentThread().getId() + "  pv:" + pv*4);

  • 优点:简单、计算量小
  • 缺点:稍有误差,但绝大多数场景能接受

方案二

在这里插入图片描述
PVSpout

package com.atguigu.storm.pv;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
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 PVSpout implements IRichSpout{

	private static final long serialVersionUID = 1L;
	private SpoutOutputCollector collector ;
	private BufferedReader reader;
	
	@SuppressWarnings("rawtypes")
	@Override
	public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
		this.collector = collector;
		
		try {
			reader = new BufferedReader(new InputStreamReader(new FileInputStream("e:/website.log"),"UTF-8"));
			
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}

	@Override
	public void close() {
		
		try {
			if (reader != null) {
				reader.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void activate() {
		
	}

	@Override
	public void deactivate() {
		
	}

	private String str;
	
	@Override
	public void nextTuple() {
		
		try {
			while((str = reader.readLine()) != null){
				
				collector.emit(new Values(str));
				
				Thread.sleep(500);
			}
		} catch (Exception e) {
			
		}
	}

	@Override
	public void ack(Object msgId) {
	}
	

	@Override
	public void fail(Object msgId) {
		
	}

	@Override
	public void declareOutputFields(OutputFieldsDeclarer declarer) {
		declarer.declare(new Fields("log"));
	}

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


创建数据处理pvbolt1

package com.atguigu.storm.pv;

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;
import org.apache.storm.tuple.Values;

public class PVBolt1 implements IRichBolt {
	
	private static final long serialVersionUID = 1L;
	private OutputCollector collector;
	private long pv = 0;

	@SuppressWarnings("rawtypes")
	@Override
	public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
		this.collector = collector;
	}

	@Override
	public void execute(Tuple input) {
		// 获取传递过来的数据
		String logline = input.getString(0);

		// 截取出sessionid
		String session_id = logline.split("\t")[1];

		// 根据会话id不同统计pv次数
		if (session_id != null) {
			pv++;
		}

		// 提交
		collector.emit(new Values(Thread.currentThread().getId(), pv));

		System.err.println("threadid:" + Thread.currentThread().getId() + "  pv:" + pv);
	}

	@Override
	public void declareOutputFields(OutputFieldsDeclarer declarer) {
		declarer.declare(new Fields("thireadID", "pv"));

	}

	@Override
	public void cleanup() {

	}

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


创建PVSumBolt(只有task只有一个),所以没有线程安全问题。怎么最后还是单线程了??

package com.atguigu.storm.pv;
import java.util.HashMap;
import java.util.Iterator;
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.Tuple;

public class PVSumBolt implements IRichBolt {

	private static final long serialVersionUID = 1L;
	private Map<Long, Long> counts = new HashMap<>();

	@SuppressWarnings("rawtypes")
	@Override
	public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {

	}

	@Override
	public void execute(Tuple input) {
		Long threadID = input.getLong(0);
		Long pv = input.getLong(1);

		counts.put(threadID, pv);

		long word_sum = 0;

		Iterator<Long> iterator = counts.values().iterator();

		while (iterator.hasNext()) {
			word_sum += iterator.next();
		}

		System.err.println("pv_all:" + word_sum);//那岂不是该线程处理的并发量还是很大
	}

	@Override
	public void cleanup() {
	}

	@Override
	public void declareOutputFields(OutputFieldsDeclarer declarer) {

	}

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



驱动类

package com.atguigu.storm.pv;
import org.apache.storm.Config;
import org.apache.storm.LocalCluster;
import org.apache.storm.StormSubmitter;
import org.apache.storm.topology.TopologyBuilder;

public class PVMain {

	public static void main(String[] args) {
		TopologyBuilder builder = new TopologyBuilder();
		
		builder.setSpout("PVSpout", new PVSpout(), 1);
		builder.setBolt("PVBolt1", new PVBolt1(), 4).shuffleGrouping("PVSpout");
		builder.setBolt("PVSumBolt", new PVSumBolt(), 1).shuffleGrouping("PVBolt1");
		
		Config conf = new Config();
		
		conf.setNumWorkers(2);
		
		if (args.length > 0) {
			try {
				StormSubmitter.submitTopology(args[0], conf, builder.createTopology());
			} catch (Exception e) {
				e.printStackTrace();
			} 
		}else {
			LocalCluster cluster = new LocalCluster();
			cluster.submitTopology("pvtopology", conf, builder.createTopology());
		}
	}
}

三 集群启动和停止

在这里插入图片描述

日志查看

在这里插入图片描述

ssh无密码访问

因为这个功能本质是写一个远程脚本
参考:实现scp拷贝时无需输入密码

发布了82 篇原创文章 · 获赞 1 · 访问量 1957

猜你喜欢

转载自blog.csdn.net/m0_38060977/article/details/103647706