Storm 原理、API

大数据组件使用 总文章 


1.Apache Storm 流式计算框架
	1.Storm 基础
		1.Storm是什么
			Hadoop在处理数据的时候,时效性不够,市场期望能够尽快得到处理后的数据。
			Storm是一个流式计算框架,数据源源不断的产生,源源不断的收集,源源不断的计算。(一条数据一条数据的处理)
			Storm只负责数据的计算,不负责数据的存储。
			2013年前后,阿里巴巴基于storm框架,使用java语言开发了类似的流式计算框架佳作,Jstorm。
			2016年年底 阿里巴巴将源码贡献给了Apache storm,两个项目开始合并,新的项目名字叫做storm2.x。阿里巴巴团队专注flink开发。

		2.Storm架构
			Nimbus:负责资源分配和任务调度。
			Supervisor:负责接受nimbus分配的任务,启动和停止属于自己管理的worker进程。
			Worker:运行具体处理组件逻辑的进程。
			Task:worker中每一个spout/bolt的线程称为一个task。
			      在storm0.8之后,task不再与物理线程对应,同一个spout/bolt的task可能会共享一个物理线程,该线程称为executor。

 

	2.Storm编程模型
		1.Topology拓扑:Storm中运行的一个实时应用程序,因为各个组件间的消息流动形成逻辑上的一个拓扑结构。
		2.Spout:
			在一个topology中 产生源数据流 的组件。通常情况下spout会从外部数据源中读取数据,然后转换为topology内部的源数据。
			Spout是一个主动的角色,其接口中有个nextTuple()函数,storm框架会不停地调用此函数,用户只要在其中生成源数据即可。
		3.Bolt:
			在一个topology中 接收数据,然后执行处理的组件。Bolt可以执行过滤、函数操作、合并、写数据库等任何操作。
			Bolt是一个被动的角色,其接口中有个execute(Tuple input)函数,在接受到消息后会调用此函数,用户可以在其中执行自己想要的操作。
		4.Tuple(元祖/数组):
			一次消息传递的基本单元。本来应该是一个key-value的map,但是由于各个组件间传递的tuple的字段名称已经事先定义好,
			所以tuple中只要按序填入各个value就行了,所以就是一个value list.
		5.Stream流:源源不断传递的tuple就组成了stream。

	3.分组策略
		1.Stream Grouping(流分组):即消息的partition分区 方法。
		2.Stream Grouping(流分组) 定义了一个流 在Bolt任务间该如何被切分。这里有Storm提供的 6个 Stream Grouping(流分组) 类型:
			1.随机分组(Shuffle grouping):
				随机分发 tuple(元祖/数组) 到Bolt的任务,保证每个任务获得相等数量的tuple(元祖/数组)。 
				跨服务器通信,浪费网络资源,尽量不适用。
			2.字段分组(Fields grouping):
				根据指定字段分割数据流,并分组。例如,根据“user-id”字段,相同“user-id”的元组总是分发到同一个任务,
				不同“user-id”的元组可能分发到不同的任务。跨服务器,除非有必要,才使用这种方式。
			3.全部分组(All grouping):tuple(元祖/数组) 被复制到bolt的所有任务。这种类型需要谨慎使用。人手一份,完全不必要。
			4.全局分组(Global grouping):全部Stream流都分配到bolt的同一个任务。明确地说,是分配给ID最小的那个task。  
			5.无分组(None grouping):
				你不需要关心Stream流是如何分组。目前,无分组等效于随机分组。
				但最终,Storm将把无分组的Bolts放到Bolts或Spouts订阅它们的同一线程去执行(如果可能)。
			6.直接分组(Direct grouping):这是一个特别的分组类型。元组生产者决定tuple(元祖/数组)由哪个元组处理者任务接收。  
		3.LocalOrShuffle 分组:优先将数据发送到本地的Task,节约网络通信的资源。



2.WordCount案例分析 
	1.功能说明
		设计一个topology,来实现对一个句子里面的单词出现的频率进行统计。
		整个topology分为三个部分:
			RandomSentenceSpout:数据源,在已知的英文句子中,随机发送一条句子出去。
			SplitSentenceBolt:负责将单行文本记录(句子)切分成单词
			WordCountBolt:负责对单词的频率进行累加

	2.导入最新的依赖包
		<dependency>
    			<groupId>org.apache.storm</groupId>
    			<artifactId>storm-core</artifactId>
   			<version>1.1.1</version>
		</dependency>
        		目前<scope>可以使用5个值:
			compile,缺省值,适用于所有阶段,会随着项目一起发布。
			provided,类似compile,期望JDK、容器或使用者会提供这个依赖。如servlet.jar。
			runtime,只在运行时使用,如JDBC驱动,适用运行和测试阶段。
			test,只在测试时使用,用于编译和运行测试代码。不会随项目发布。
			system,类似provided,需要显式提供包含依赖的jar,Maven不会在Repository中查找它。  -->
         	例子:<scope>provided</scope> 
                    <build>
                        <plugins>
                            <plugin>
                                <artifactId>maven-assembly-plugin</artifactId>
                                <configuration>
                                    <descriptorRefs>
                                        <descriptorRef>jar-with-dependencies</descriptorRef>
                                    </descriptorRefs>
                                    <archive>
                                        <manifest>
                                            <mainClass>realtime.WordCountTopology</mainClass>
                                        </manifest>
                                    </archive>
                                </configuration>
                                <executions>
                                    <execution>
                                        <id>make-assembly</id>
                                        <phase>package</phase>
                                        <goals>
                                            <goal>single</goal>
                                        </goals>
                                    </execution>
                                </executions>
                            </plugin>
                            <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <version>3.7.0</version>
                                <configuration>
                                    <source>1.8</source>
                                    <target>1.8</target>
                                </configuration>
                            </plugin>
                        </plugins>
                    </build>

 	3.TopologyMain 驱动类
		package cn.itcast.realtime;
		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 WordCountTopology 
		{
    			public static void main(String[] args) throws InvalidTopologyException, AuthorizationException, AlreadyAliveException 
    			{
            			//1.创建一个job(topology)
            			TopologyBuilder topologyBuilder = new TopologyBuilder();
            			//2.设置job的详细内容
            			topologyBuilder.setSpout("ReadFileSpout",new ReadFileSpout(),1);
				//SentenceSplitBolt 负责将单行文本记录(句子)切分成单词
            			topologyBuilder.setBolt("SentenceSplitBolt",new SentenceSplitBolt(),1).shuffleGrouping("ReadFileSpout");
				//WordCountBolt 负责对单词的频率进行累加
            			topologyBuilder.setBolt("WordCountBolt",new WordCountBolt(),1).shuffleGrouping("SentenceSplitBolt");
            			//准备配置项
            			Config config = new Config();
            			config.setDebug(false);
            			//3.提交job
            			//提交由两种方式:一种本地运行模式、一种集群运行模式。
            			if (args != null && args.length > 0) 
    				{
                				//运行集群模式
                				config.setNumWorkers(1);
               			 	StormSubmitter.submitTopology(args[0],config,topologyBuilder.createTopology());
            			} 
    				else 
	    			{	
					//本地运行模式
                				LocalCluster localCluster = new LocalCluster();
                				localCluster.submitTopology("wordcount", config, topologyBuilder.createTopology());
            			}
   			 }
		}

	4.ReadFileSpout
		package cn.itcast.realtime;
		import org.apache.storm.spout.SpoutOutputCollector;
		import org.apache.storm.task.TopologyContext;
		import org.apache.storm.topology.OutputFieldsDeclarer;
		import org.apache.storm.topology.base.BaseRichSpout;
		import org.apache.storm.tuple.Fields;
		import org.apache.storm.tuple.Values;
		import java.util.Map;

		//Spout 需要继承一个模板
		public class ReadFileSpout extends BaseRichSpout 
		{
    			private SpoutOutputCollector collector;
        			/**
         		 * Map conf:应用程序能够读取的配置文件
         		 * TopologyContext context:应用程序的上下文
         		 * SpoutOutputCollector collector:Spout输出的数据丢给SpoutOutputCollector。
         		 */
        			@Override
        			public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) 
    			{
            			//1、Kafka 连接 / MYSQL 连接 / Redis 连接
            			//2、将SpoutOutputCollector 复制给成员变量
           			this.collector = collector;
        			}

    			//storm框架有个while循环,一直在调用nextTuple()函数
			//Spout是一个主动的角色,其接口中有个nextTuple()函数,storm框架会不停地调用此函数,用户只要在其中生成源数据即可。
        			@Override
        			public void nextTuple() 
    			{
            			// 发送数据,使用collector.emit方法
            			// Values extends ArrayList<Object>
            			collector.emit(new Values("i love u"));
            			try 
				{
                				Thread.sleep(500);
            			} 
    				catch (InterruptedException e) 
	    			{
                				e.printStackTrace();
        				}
    			}

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

	5.SentenceSplitBolt(SentenceSplitBolt 负责将单行文本记录(句子)切分成单词)
		package cn.itcast.realtime;
		import org.apache.storm.task.OutputCollector;
		import org.apache.storm.task.TopologyContext;
		import org.apache.storm.topology.OutputFieldsDeclarer;
		import org.apache.storm.topology.base.BaseRichBolt;
		import org.apache.storm.tuple.Fields;
		import org.apache.storm.tuple.Tuple;
		import org.apache.storm.tuple.Values;
		import java.util.Map;

		//切割单词
		public class SentenceSplitBolt extends BaseRichBolt 
		{
    			private OutputCollector collector;
    			/**
         		 *  初始化方法
         		 *  Map stormConf:应用能够得到的配置文件
         		 *  TopologyContext context:应用程序的上下文
         		 *  OutputCollector collector:数据收集器
         		 */
        			@Override
        			public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) 
    			{
            			this.collector = collector;// 连接数据 连接redis 连接hdfs
        			}

    			//有个while不停的调用execute方法,每次调用都会发一个数据进行来
        			@Override
        			public void execute(Tuple input) 
    			{
    				// String sentence = input.getString(0);
            			// 底层先通过 biaobai 这个字段在map中找到对应的index角标值,然后再valus中获取对应数据。
            			String sentence = input.getStringByField("biaobai");
            			String[] strings = sentence.split(" ");// 切割
            			for (String word : strings) 
    				{
                				collector.emit(new Values(word,1));// 输出数据
            			}
        			}

            		@Override
            		public void declareOutputFields(OutputFieldsDeclarer declarer) 
        			{
                			declarer.declare(new Fields("word","num"));// 声明 输出的是什么字段
            		}
        		}

	6.WordCountBolt(负责对单词的频率进行累加)
		package cn.itcast.realtime;
		import org.apache.storm.task.OutputCollector;
		import org.apache.storm.task.TopologyContext;
		import org.apache.storm.topology.OutputFieldsDeclarer;
		import org.apache.storm.topology.base.BaseRichBolt;
		import org.apache.storm.tuple.Fields;
		import org.apache.storm.tuple.Tuple;
		import java.util.HashMap;
		import java.util.Map;

		//计数
		public class WordCountBolt extends BaseRichBolt 
		{
    			private OutputCollector collector;
    			private HashMap<String, Integer> wordCountMap;

    			/**
         		 * 初始化方法
         		 * Map stormConf:应用能够得到的配置文件
         		 * TopologyContext context:应用程序的上下文
         		 * OutputCollector collector:数据收集器
         		 */
    			@Override
        			public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) 
    			{
            			this.collector = collector; // 连接数据 连接redis 连接hdfs
            			wordCountMap = new HashMap<String, Integer>();
        			}

    			//有个while不停的调用execute方法,每次调用都会发一个数据进行来execute函数中进行处理 
    			@Override
        			public void execute(Tuple input) 
    			{
            			String word = input.getStringByField("word");
            			Integer num = input.getIntegerByField("num");
            			// 先判断这个单词是否出现过
            			if (wordCountMap.containsKey(word)) 
    				{
                				Integer oldNum = wordCountMap.get(word);
                				wordCountMap.put(word, oldNum + num);
            			} 
    				else 
    				{
               			 	wordCountMap.put(word, num);
            			}
            			System.out.println(wordCountMap);
        			}

        			@Override
       			public void declareOutputFields(OutputFieldsDeclarer declarer) 
    			{
            			// 声明 输出的是什么字段
            			declarer.declare(new Fields("fenshou"));
        			}
		}

 	7.Component生命周期
		1.Spout生命周期

 		2.Bolt生命周期

		3.Bolt的两个抽象类
			BaseRichBolt 需要手动调ack方法 
			BaseBasicBolt 由 storm框架自动调ack方法

	8.StreamGrouping
		public Map<List<Integer>, List<MsgInfo>> grouperBatch(List<MsgInfo> batch) 
		{
    			Map<List<Integer>, List<MsgInfo>> ret = new HashMap<List<Integer>, List<MsgInfo>>();
        			//optimize fieldGrouping & customGrouping
        			if (GrouperType.local_or_shuffle.equals(grouptype)) 
    			{
           			ret.put(local_shuffer_grouper.grouper(null), batch);
        			}  
    			else if (GrouperType.global.equals(grouptype)) 
    			{
            			// send to task which taskId is 0
            			ret.put(JStormUtils.mk_list(out_tasks.get(0)), batch);
        			} 
    			else if (GrouperType.fields.equals(grouptype)) 
    			{
            			fields_grouper.batchGrouper(batch, ret);
        			} 
    			else if (GrouperType.all.equals(grouptype)) 
    			{
            			// send to every task
            			ret.put(out_tasks, batch);
        			} 
    			else if (GrouperType.shuffle.equals(grouptype)) 
    			{
            			// random, but the random is different from none
            			ret.put(shuffer.grouper(null), batch);
        			} 
    			else if (GrouperType.none.equals(grouptype)) 
    			{
            			int rnd = Math.abs(random.nextInt() % out_tasks.size());
            			ret.put(JStormUtils.mk_list(out_tasks.get(rnd)), batch);
        			} 
    			else if (GrouperType.custom_obj.equals(grouptype) || GrouperType.custom_serialized.equals(grouptype)) 
    			{
            			for (int i = 0; i < batch.size(); i++ ) 
    				{
                				MsgInfo msg = batch.get(i);
                				List<Integer> out = custom_grouper.grouper(msg.values);
                				List<MsgInfo> customBatch = ret.get(out);
                				if (customBatch == null) 
    					{
                    				customBatch = JStormUtils.mk_list();
                    				ret.put(out, customBatch);
                				}
                				customBatch.add(msg);
            			}
        			} 
			else if (GrouperType.localFirst.equals(grouptype)) 
    			{
        				ret.put(localFirst.grouper(null), batch);
   			} 	
			else 
			{
        				LOG.warn("Unsupportted group type");
    			}
    			return ret;
		}

	9.Tuple是什么    

	10.并行度是什么	

 

3.案例:实时交易数据统计	
	1.架构设计及思路
		支付系统 + kafka + storm/Jstorm集群 + redis集群
		1.支付系统发送 mq 到 kafka集群中,编写 storm程序 消费 kafka的数据 并计算实时的订单数量、订单数量
		2.将计算的实时结果保存在redis中
		3.外部程序访问redis的数据实时展示结果

	2.工程设计
		数据产生:编写kafka数据生产者,模拟订单系统发送 mq
		数据输入:使用 PaymentSpout 消费 kafka中的数据
		数据计算:使用 CountBolt 对数据进行统计
		数据存储:使用 Sava2RedisBolt 对数据进行存储,将结果数据存储到redis中
		数据展示:编写java app客户端,访问redis,对数据进行展示,展示方式为打印在控制台。

		1.获取外部数据源,MQSpout----Open(连接你的RMQ)---nextTuple()-----emit(json)
		2.ParserPaymentInfoBolt()----execute(Tuple)------解析Json----JavaBean
   		  	productId,orderId,time,price(原价,订单价,优惠价,支付价),user,收货地址
   		  	total:原价、total:订单价、total:订单人数……
		3.Save2ReidsBolt,保存相关业务指标
		  问题:在redis中存放整个网站销售的原价,  b:t:p:20160410 ---> value
	   	  redis:String----> value1+value2 + value3 + value4  incrBy
			b:t:p:20160410
			b:t:p:20161111
			b:t:p:20160412

	3.项目依赖
		<!-- storm core-->
		<dependencies>
    			<dependency>
        				<groupId>org.apache.storm</groupId>
        				<artifactId>storm-core</artifactId>
        				<version>1.1.1</version>
        				<scope>provided</scope>
    			</dependency>
    			<!-- storm kafka KafkaSpout-->
        			<!--  use new kafka spout code -->
        			<dependency>
            			<groupId>org.apache.storm</groupId>
            			<artifactId>storm-kafka-client</artifactId>
            			<version>1.1.1</version>
        			</dependency>
        			<dependency>
            			<groupId>org.apache.kafka</groupId>
            			<artifactId>kafka-clients</artifactId>
            			<version>0.10.0.0</version>
        			</dependency>
        			<!-- redis  jedis 依赖-->
        			<dependency>
            			<groupId>redis.clients</groupId>
            			<artifactId>jedis</artifactId>
            			<version>2.8.0</version>
        			</dependency>
        			<!-- json Gson/fastjson-->
        			<dependency>
            			<groupId>com.google.code.gson</groupId>
            			<artifactId>gson</artifactId>
            			<version>2.4</version>
        			</dependency>
    		</dependencies>	

	4.数据生产
		package cn.itcast.realtime.kanban.producer;
		import cn.itcast.realtime.kanban.domain.PaymentInfo;
		import org.apache.kafka.clients.producer.KafkaProducer;
		import org.apache.kafka.clients.producer.ProducerRecord;
		import java.util.Properties;

		public class PaymentInfoProducer 
		{
    			public static void main(String[] args)
			{
        				//1、准备配置文件
        				Properties props = new Properties();
        				props.put("bootstrap.servers", "node01:9092");
        				/**
         			 * 当生产者将ack设置为“全部”(或“-1”)时,min.insync.replicas指定必须确认写入被认为成功的最小副本数。
         			 * 如果这个最小值不能满足,那么生产者将会引发一个异常(NotEnoughReplicas或NotEnoughReplicasAfterAppend)。
         			 * 当一起使用时,min.insync.replicas和acks允许您执行更大的耐久性保证。
         			 * 一个典型的情况是创建一个复制因子为3的主题,将min.insync.replicas设置为2,并使用“全部”选项来产生。
         			 * 这将确保生产者如果大多数副本没有收到写入引发异常。
         			 */
        			 	props.put("acks", "all");
        				//设置一个大于零的值,将导致客户端重新发送任何失败的记录
        				props.put("retries", 0);
        				/**
         			 * 只要有多个记录被发送到同一个分区,生产者就会尝试将记录一起分成更少的请求。
         			 * 这有助于客户端和服务器的性能。该配置以字节为单位控制默认的批量大小。
         			 */
        			 	props.put("batch.size", 16384);
        				/**
           			 *在某些情况下,即使在中等负载下,客户端也可能希望减少请求的数量。
           			 * 这个设置通过添加少量的人工延迟来实现这一点,即不是立即发出一个记录,
           			 * 而是等待达到给定延迟的记录,以允许发送其他记录,以便发送可以一起批量发送
           			 */
          			 props.put("linger.ms", 1);
          			 /**
           			  * 生产者可用于缓冲等待发送到服务器的记录的总字节数。
           			  * 如果记录的发送速度比发送给服务器的速度快,那么生产者将会阻塞,max.block.ms之后会抛出异常。
           			  * 这个设置应该大致对应于生产者将使用的总内存,但不是硬性限制,
           			  * 因为不是所有生产者使用的内存都用于缓冲。
           			  * 一些额外的内存将被用于压缩(如果压缩被启用)以及用于维护正在进行的请求。
           			  */
          			 props.put("buffer.memory", 33554432);
          			 props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
          			 props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        				//2、创建 Kafka Producer
        				KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(props);
        				while (true)
				{
            				//3、发送数据
            				kafkaProducer.send(new ProducerRecord<String, String>("itcast_shop_order",new PaymentInfo().random()));
            				try 
					{
                					Thread.sleep(1000);
            				} 
					catch (InterruptedException e) 
					{
                					e.printStackTrace();
            				}
        				}
    			}
		}		

	5.驱动类
		package cn.itcast.realtime.kanban.Storm;
		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.kafka.spout.KafkaSpout;
		import org.apache.storm.kafka.spout.KafkaSpoutConfig;
		import org.apache.storm.topology.TopologyBuilder;

		//组装应用程序--驱动类
		public class KanBanTopology 
		{
    			public static void main(String[] args) throws InvalidTopologyException, AuthorizationException, AlreadyAliveException 
			{
        				//1、创建一个job(topology)
        				TopologyBuilder topologyBuilder = new TopologyBuilder();
        				//2、设置job的详细内容
        				KafkaSpoutConfig.Builder<String, String> builder = KafkaSpoutConfig.builder("node01:9092","itcast_shop_order");
        				builder.setGroupId("bigdata_kanban_order");
        				KafkaSpoutConfig<String, String> kafkaSpoutConfig = builder.build();
        				topologyBuilder.setSpout("KafkaSpout",new KafkaSpout<String,String>(kafkaSpoutConfig), 1);
        				topologyBuilder.setBolt("ETLBolt",new ETLBolt(),1).shuffleGrouping("KafkaSpout");
        				topologyBuilder.setBolt("ProcessBolt",new ProcessBolt(),1).shuffleGrouping("ETLBolt");
        				//准备配置项
        				Config config = new Config();
        				config.setDebug(false);
        				//3、提交job
        				//提交由两种方式:一种本地运行模式、一种集群运行模式。
        				if (args != null && args.length > 0) 
				{
            				//运行集群模式
            				config.setNumWorkers(1);
            				StormSubmitter.submitTopology(args[0],config,topologyBuilder.createTopology());
        				} 
				else 
				{
					//运行本地运行模式
            				LocalCluster localCluster = new LocalCluster();
            				localCluster.submitTopology("KanBanTopology", config, topologyBuilder.createTopology());
        				}
   			 }
		}

	6.ETLBolt
		package cn.itcast.realtime.kanban.Storm;
		import cn.itcast.realtime.kanban.domain.PaymentInfo;
		import com.google.gson.Gson;
		import org.apache.storm.task.OutputCollector;
		import org.apache.storm.task.TopologyContext;
		import org.apache.storm.topology.OutputFieldsDeclarer;
		import org.apache.storm.topology.base.BaseRichBolt;
		import org.apache.storm.tuple.Fields;
		import org.apache.storm.tuple.Tuple;
		import org.apache.storm.tuple.Values;
		import java.util.Map;

		public class ETLBolt extends BaseRichBolt 
		{
    			private OutputCollector collector;

    			/**
     			 * 初始化方法
     			 * Map stormConf:应用能够得到的配置文件
     			 * TopologyContext context:应用程序的上下文
     			 * OutputCollector collector:数据收集器
     			 */
    			@Override
    			public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) 
			{
        				this.collector = collector;
    			}

    			//有个while不停的调用execute方法,每次调用都会发一个数据进行来。
     			@Override
     			public void execute(Tuple input) 
 			{
         			String json = input.getString(4);
         			json = input.getStringByField("value");
         			// 将json串转成 Java对象
         			Gson gson = new Gson();
         			PaymentInfo paymentInfo = gson.fromJson(json, PaymentInfo.class);
         			// 其它的操作,比如说根据商品id查询商品的一级分类,二级分类,三级分类
         			if(paymentInfo!=null) 
 				{
             				collector.emit(new Values(paymentInfo));
         			}
    			 }

    			@Override
     			public void declareOutputFields(OutputFieldsDeclarer declarer) 
 			{
         			// 声明 输出的是什么字段
         			declarer.declare(new Fields("paymentInfo"));
     			}
 		}

	7.processBolt
		package cn.itcast.realtime.kanban.Storm;
		import cn.itcast.realtime.kanban.domain.PaymentInfo;
		import org.apache.storm.task.OutputCollector;
		import org.apache.storm.task.TopologyContext;
		import org.apache.storm.topology.OutputFieldsDeclarer;
		import org.apache.storm.topology.base.BaseRichBolt;
		import org.apache.storm.tuple.Tuple;
		import redis.clients.jedis.Jedis;
		import java.util.Map;

		public class ProcessBolt extends BaseRichBolt 
		{
    			private Jedis jedis;

    			/**
     			 * 初始化方法
     			 * Map stormConf:应用能够得到的配置文件
     			 * TopologyContext context:应用程序的上下文
     			 * OutputCollector collector:数据收集器
     			 */
        			@Override
        			public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) 
    			{
            			jedis = new Jedis("redis", 6379);
        			}

    			//有个while不停的调用execute方法,每次调用都会发一个数据进行来
    			@Override
     			public void execute(Tuple input) 
 			{
         			//获取上游发送的javabean
         			PaymentInfo value = (PaymentInfo) input.getValue(0);
         			//先计算总数据 来一条算一条
         			jedis.incrBy("kanban:total:ordernum",1);
         			jedis.incrBy("kanban:total:orderPrice",value.getPayPrice());
         			jedis.incrBy("kanban:total:orderuser",1);

         			//计算商家(店铺的销售情况)
        			 	String shopId = value.getShopId();
         			jedis.incrBy("kanban:shop:"+shopId+":ordernum",1);
         			jedis.incrBy("kanban:shop:"+shopId+":orderPrice",value.getPayPrice());
         			jedis.incrBy("kanban:shop:"+shopId+":orderuser",1);

         			//计算每个品类(品类id)一级品类
         			String Level1 = value.getLevel1();
         			jedis.incrBy("kanban:shop:"+Level1+":ordernum",1);
         			jedis.incrBy("kanban:shop:"+Level1+":orderPrice",value.getPayPrice());
         			jedis.incrBy("kanban:shop:"+Level1+":orderuser",1);
     			}

     			@Override
     			public void declareOutputFields(OutputFieldsDeclarer declarer) { }
 		}

	8.看板
		package cn.itcast.realtime.kanban.view;
		import redis.clients.jedis.Jedis;

		public class Kanban 
		{
    			public static void main(String[] args) 
			{
        				Jedis jedis = new Jedis("redis",6379);
        				while (true)
				{
            				System.out.println("kanban:total:ordernum 指标是"+jedis.get("kanban:total:ordernum"));
            				System.out.println("kanban:total:orderPrice指标是"+jedis.get("kanban:total:orderPrice"));
            				System.out.println("kanban:total:orderuser指标是"+jedis.get("kanban:total:orderuser"));
            				System.out.println("---------------------------");
            				System.out.println();
            				try 
					{
                					Thread.sleep(2000);
            				} 
					catch (InterruptedException e) 
					{
                					e.printStackTrace();
            				}
        				}
    			}
		}

Storm原理
	1.Storm 任务提交的过程

 


1.读取数据的Spout类:
	1.Spout类要读取topic主题中多个分区数中的数据的话,那么所设置的运行Spout类的线程数则是和topic主题中的分区数应为一致。
	  根据一个分区只能被一个消费者所读取数据的原则,那么有多少个分区就应设置多少个消费者,所以此处就应设置多少个线程运行Spout类
	2.Spout类要读取本地文件中数据的话,应设置1个线程执行Spout类,即Spout类只会读取本地文件一次。
	  如果设置多个线程执行Spout类的话,那么便会有多个线程同时执行多个Spout类的task任务,那么便会读取本地文件多次,
	  便会造成读取多次没必要,一个线程默认有一个Spout类的task任务。

2.处理数据的Blot类:可以设置任意多个线程执行Blot类

3.数据分发的策略:(推荐使用localOrShuffleGrouping)
	localOrShuffleGrouping(推荐):
	优先把“处理完毕后的”tuple元祖数据分发给本worker进程内的任意一个线程中的task任务,因此节约网络通信资源。一个线程中默认只有一个task任务。
	例子:TopologyBuilder对象.setBolt(自定义唯一id字符串, new 自定义Bolt处理类(),线程数).localOrShuffleGrouping(发送数据过来的类对应的自定义唯一id字符串)
 
4.setNumTasks(task任务数)
	一个线程中默认只有一个task任务。但可以设置一个线程中跑多个task任务,相当于协程。
	设置一个线程中跑2个task任务:
		TopologyBuilder对象.setBolt(自定义唯一id字符串, new 自定义Bolt处理类(),1).shuffleGrouping(发送数据过来的类得自定义唯一id字符串).setNumTasks(2)
 

本地模式运行 该程序

LocalCluster本地模式运行:
	LocalCluster本地模式指的是 可以本地window执行,也可以把 项目打包为jar包 或 同时把当前项目和所依赖的第三方jar包打包为一个jar包
	放到Linux下的storm集群中执行,但是在Storm UI监控管理页面(192.168.25.100:8088)是无法查看到 本地模式运行的该程序的运行信息

 

1.给“读取数据的”Spout类 设置线程数:
	1.Spout类要读取topic主题中多个分区数中的数据的话,那么所设置的运行Spout类的线程数则是和topic主题中的分区数应为一致。
	  根据一个分区只能被一个消费者所读取数据的原则,那么有多少个分区就应设置多少个消费者,所以此处就应设置多少个线程运行Spout类
	2.Spout类要读取本地文件中数据的话,应设置1个线程执行Spout类,即Spout类只会读取本地文件一次。
	  如果设置多个线程执行Spout类的话,那么便会有多个线程同时执行多个Spout类的task任务,那么便会读取本地文件多次,
	  便会造成读取多次没必要,一个线程默认有一个Spout类的task任务。
 
2.给“处理数据的Blot类 设置线程数:可以设置任意多个线程执行Blot类


 Storm 集群搭建

1.安装准备
	1.安装jdk、安装zookeeper
	  storm是依赖于zookeeper的,搭建storm集群前,必须先把zookeeper集群搭建好
	2.tar -zxvf apache-storm-1.1.1.tar.gz -C /root/storm
	3.启动zookeeper 都必须执行 时间同步命令:ntpdate ntp6.aliyun.com
	4.每台机器都启动zookeeper(启动zookeeper 都必须执行 时间同步命令:ntpdate ntp6.aliyun.com)
		cd /root/zookeeper/bin/
		zkServer.sh start

          查看zookeeper集群状态、主从信息:
		1.cd /root/zookeeper/bin/
		2../zkServer.sh status # 查看状态:一个leader,两个follower
		3.“follower跟随者”的打印结果:
			JMX enabled by default
			Using config: /root/zookeeper/bin/../conf/zoo.cfg
			Mode: follower
		4.“leader领导者”的打印结果:
			JMX enabled by default
			Using config: /root/zookeeper/bin/../conf/zoo.cfg
			Mode: leader

2.修改storm中的配置文件storm.yaml
	1.vim /root/storm/conf/storm.yaml
	2.配置文件storm.yaml:
		storm.zookeeper.servers:# 表示 zookeeper集群的每个主机名/IP地址
          	- "NODE1" 
          	- "NODE2"
          	- "NODE3"
 		# storm集群中作为nimbus的每台linux的主机名/IP地址。多台linux执行命令“storm nimbus”启动nimbus即可提供nimbus高可用,
		# 只有先启动的第一台linux作为主,后启动的每台都作为备份。
     		nimbus.seeds: ["NODE1", "NODE2", "NODE3"] 
     		storm.local.dir: "/root/stormData"  # storm 数据目录
     		ui.port: 8088 # 监控管理storm 的webUI页面的访问端口,不指定的话默认是使用的8080端口进行访问
		# 多台linux执行命令“storm supervisor”启动supervisor,那么当前linux便作为supervisor提供woker进程用于执行task任务
		# 下面配置的是每个woker进程的可用端口号
		# 在storm集群中node2和node3都作为supervisor启动使用的话,那么storm集群中便一共有8个worker进程的端口以供使用
     		supervisor.slots.ports: 
         	- 6700
         	- 6701
         	- 6702
         	- 6703

	3.每台linux都需要 mkdir -p /root/stormData
	4.把 node1下配置好的 storm文件夹 推送到 node2、node3中:
		scp -r /root/storm NODE2:/root 
		scp -r /root/storm NODE3:/root 

3.启动storm集群
	1.NODE1、NODE2、NODE3 分别都作为nimbus:
		前台启动:cd /root/storm/bin
			 chmod 777 storm
			 ./storm nimbus
		后台启动:cd /root/storm
			 chmod 777 ./bin/storm
			 nohup bin/storm nimbus >/dev/null 2>&1 &

	  	先启动的NODE1中的nimbus作为主,后启动的NODE2、NODE3中的nimbus作为副本
	  	使用jps命令查看nimbus是否成功启动:一开始会显示config_value 表示正在加载配置,再继续jps查看后会出现nimbus,表示加载配置并启动成功nimbus
 
	2.NODE2、NODE3 分别作为supervisor:(在storm集群中node2和node3都作为supervisor启动使用的话,那么storm集群中便一共有8个worker进程的端口以供使用)
		前台启动:cd /root/storm/bin
			 chmod 777 storm
			 ./storm supervisor
		后台启动:cd /root/storm
			 chmod 777 ./bin/storm
			 nohup bin/storm supervisor >/dev/null 2>&1 & 

	  	多台linux执行命令“storm supervisor”启动supervisor,那么当前linux便作为supervisor提供woker进程用于执行task任务
	  	使用jps命令查看nimbus是否成功启动:一开始会显示config_value 表示正在加载配置,再继续jps查看后会出现Supervisor,表示加载配置并启动成功Supervisor

	3.node1中 启动webUI页面:监控管理storm 
		前台启动:cd /root/storm/bin
			 chmod 777 storm
			 ./storm ui 
		后台启动:cd /root/storm
			 chmod 777 ./bin/storm
			 nohup bin/storm ui >/dev/null 2>&1 &
		使用jps命令查看成功启动webUI页面,会出现core。
		使用 192.168.25.100:8088 或 node1:8088 即可打开 webUI页面 来监控管理storm
	
	4.日志查看工具 
		前台启动:cd /root/storm/bin
			 chmod 777 storm
			 ./storm logviewer
		后台启动:cd /root/storm
			 chmod 777 ./bin/storm
			 nohup bin/storm logviewer >/dev/null 2>&1 & 
		使用jps命令查看成功启动 logviewer的话,会出现logviewer

	5.storm没有提供一键启动,需要自己手动编写一键启动脚本
		1.创建slave文件:(如果脚本无法读取slave文件中的第三行信息的话,则给第四行来一个空行,那么脚本才能顺利读取到第三行信息)
			NODE1
			NODE2
			NODE3

		2.一键启动storm:startstorm.sh
			source /etc/profile
			nohup storm nimbus >/dev/null 2>&1 &
			nohup storm ui >/dev/null 2>&1 &
			cat /export/servers/storm/bin/slave | while read line
			do
			{
			echo $line
			ssh $line "source /etc/profile;nohup storm supervisor >/dev/null 2>&1 &"
			}&
			wait
			done 

		2.一键关闭storm:stopstorm.sh(cut -c 1-6:截取第1个字符到第6字符的进程号,但要注意的是如果进程是7个数字就无法完整获取)
			source /etc/profile
			jps | awk '{ if( $2 == "nimbus" || $2 == "supervisor" ||  $2 == "core" ) print $1 }' |xargs kill -s 9
			cat /export/servers/storm/bin/slave | while read line
			do
			{
			echo $line
			ssh $line "source /etc/profile;jps |grep "supervisor" |cut -c 1-6 |xargs kill -s 9"
			}&
			wait
			done 


linux下的 storm集群中 以集群模式 运行该程序 

StormSubmitter(storm集群模式运行):
	以集群模式运行表示 只能把 项目打包为jar包 或 同时把当前项目和所依赖的第三方jar包打包为一个jar包 放到Linux下的storm集群中执行
	此时可在Storm UI监控管理页面(192.168.25.100:8088)查看到storm集群模式下运行的该程序的运行信息

1.给“读取数据的”Spout类 设置线程数:
	1.Spout类要读取topic主题中多个分区数中的数据的话,那么所设置的运行Spout类的线程数则是和topic主题中的分区数应为一致。
	  根据一个分区只能被一个消费者所读取数据的原则,那么有多少个分区就应设置多少个消费者,所以此处就应设置多少个线程运行Spout类
	2.Spout类要读取本地文件中数据的话,应设置1个线程执行Spout类,即Spout类只会读取本地文件一次。
	  如果设置多个线程执行Spout类的话,那么便会有多个线程同时执行多个Spout类的task任务,那么便会读取本地文件多次,
	  便会造成读取多次没必要,一个线程默认有一个Spout类的task任务。
 
2.给“处理数据的Blot类 设置线程数:可以设置任意多个线程执行Blot类

 

 

linux下的storm集群中 以集群模式 运行该程序:
1.把  stormWordcount-1.0-SNAPSHOT.jar 拷贝到 /root目录下,执行 cd /root
2.执行 ./storm jar 项目名.jar Topology驱动类的全限定名称(包名.类名)
  此处执行:
	cd /root/storm/bin
	./storm jar /root/stormWordcount-1.0-SNAPSHOT.jar cn.itcast.storm.wc.WordCountTopology

以集群模式运行该程序后,会把运行结果信息记录在worker.log文件中
	1.具体路径:/root/storm/logs/workers-artifacts/wordcount1-1-时间/端口号/worker.log 	
	  	    端口号可在监控页面中查看该blot类所运行所在的哪个node节点和在该node节点上所使用的端口号。
	2.例子:/root/storm/logs/workers-artifacts/wordcount1-1-1538035874/6700/worker.log

linux下的Redis安装

1.第一步:先安装C的编译环境:yum install gcc gcc-c++ libstdc++-devel tcl -y
	  因为需要使用gcc进行编译才能安装Redis,Redis是基于C语言开发的。
2.第二步:查看是否安装gcc成功,输入gcc或make是否出现以下提示。

3.第三步:可联网下载 redis-4.0.11.tar.gz或其他版本的源代码包,并进行安装
	1.wget http://download.redis.io/releases/redis-4.0.10.tar.gz
	2.tar xzf redis-4.0.11.tar.gz(必须把压缩包放到linux解压,不能在window解压好再拖到linxu中,因为这样很多文件都没有权限)
	3.cd redis-4.0.11(进入解压后目录)
	4.make(使用make命令会进行编译)
	5.make install PREFIX=/usr/local/redis(安装到/usr/local/redis目录下)

4.第四步:进入 cd /usr/local/redis/bin 目录下,查看到多个redis的命令文件


Redis服务器端的启动和停止

1.第一步:进入 cd /usr/local/redis/bin目录下,有如下多个redis的命令文件

2.第二步:
	1.前台启动模式:在当前/usr/local/redis/bin目录下启动Redis服务器的命令:./redis-server
	2.后台启动模式:(每次修改完redis.conf请重新启动Redis服务器并再次指定加载该新修改后的redis.conf) 
		1.第一步:把 /redis-3.0.0的源码安装包目录下的 redis.conf 拷贝到 /usr/local/redis/bin 的安装目录下
			  cp /root/redis-4.0.11/redis.conf /usr/local/redis/bin

		2.第二步:修改 vim /usr/local/redis/bin/redis.conf:修改为 daemonize yes,即把原来的 no 改为 yes,设置为后台运行。
			 每次修改完redis.conf请重新启动Redis服务器并再次指定加载该新修改后的redis.conf

			修改redis-server默认绑定的127.0.0.1的IP为具体的IP。
		  	目的:如果redis-server继续使用默认绑定的127.0.0.1的话,那么外部window就无法访问linux内部的redis
				vim /usr/local/redis/bin/redis.conf
				把 bind 127.0.0.1 修改为 bind 192.168.xx.xxx

		4.第三步:在当前 /usr/local/redis/bin 目录下同时指定 redis.conf 启动Redis服务器的命令:
				cd /usr/local/redis/bin 
				./redis-server redis.conf 或者 /usr/local/redis/bin/redis-server /usr/local/redis/bin/redis.conf 
			 即Redis服务器加载并应用该redis.conf中的配置信息再进行启动。

3.第三步:查看是否启动成功 

 

4.第四步:
	1.停止 前台模式下的redis服务器:ctrl + c
	2.停止 后台模式下的redis服务器:
		1.第一种方式:查看到redis-server的进程号,然后执行kill -9 进程号:ps aux|grep redis
		2.第二种方式:在/usr/local/redis/bin目录下,使用redis客户端进行停止redis服务器:./redis-cli shutdown 或 /usr/local/redis/bin/redis-cli shutdown 
		3.第三种方式:执行了./redis-cli进入了redis客户端的输入模式下,执行shutdown命令也可停止redis服务器

 


Redis客户端的启动和停止

1.第一种启动方式:在/usr/local/redis/bin目录下,执行:./redis-cli 或者 /usr/local/redis/bin/redis-cli
2.第二种启动方式:连接远程linux上的redis服务器,执行:./redis-cli -h 远程linux的IP -p 6379
		  默认连接localhost运行在6379端口的redis服务。
			-h:连接的服务器的地址
			-p:服务的端口号
3.第三种启动方式:
	1.加上--raw的作用:
		在客户端中显示查询出的中文数据时,如果以“/16进制”的形式显示中文数据的话,
		那么使用“./redis-cli --raw”在进入客户端,那么查询出的中文数据便能正常以中文字符的形式显示。
	2.例子:./redis-cli --raw 或 /usr/local/redis/bin/redis-cli --raw 或 /usr/local/redis/bin/redis-cli -h 192.168.25.100 --raw
	        ./redis-cli -h 远程linux的IP -p 6379 --raw
	
4.redis客户端下使用ping命令查看redis服务器是否正常运行:


设置Redis密码

1.测试环境忽略Redis的访问密码,生产环境必须配置Redis的访问密码
2..开启Redis的访问密码:
	在/usr/local/redis/bin目录下的redis.conf配置文件中有个参数:“requirepass 密码”。
	redis.conf配置文件中默认配置的访问密码被注释掉了,所以首先要取消注释,然后可以修改设置redis访问密码。
 	修改redis访问密码:requirepass redis访问密码
   	每次修改完redis.conf请重新启动Redis服务器并再次指定加载该新修改后的redis.conf

3.不重启Redis设置密码:
	注意:这种方式设置密码,当redis重启,密码失效。    
	进入redis客户端(./redis-cli)下,执行“config set requirepass 密码”可以进行设置访问密码;
	如果提示需要先输入验证已设置的访问密码的话,那么执行“auth 密码”之后,再执行“config set requirepass 密码”设置新的密码。
	
4.验证密码的命令:“auth 密码”
	在操作Redis数据库中的数据之前,会提示必须先进行密码验证才能继续操作Redis数据库中的数据,所以执行“auth 密码”进行密码验证。
	“auth 密码”命令跟其他redis命令一样,是没有加密的;阻止不了攻击者在网络上窃取你的密码;
	认证层的目标是提供多一层的保护。如果防火墙或者用来保护redis的系统防御外部攻击失败的话,外部用户如果没有通过密码认证还是无法访问redis的。

kafka + storm 的集成

kafka + storm 集成和程序编写的流程

kafka + storm 集成和程序编写的流程:
1.第一步:使用shell命令 创建kafka的topic主题,创建的时候并指定多个分片数/分区数
2.第二步:编写kafka的生产者的java程序 生产大量数据 往kafka的topic主题中存储
3.第三步(测试):使用shell命令 从kafka的topic主题中消费数据 以用于测试是否成功
4.第四步:编写kafka + storm集成的java程序
		1.首先就需要在仅storm-core的storm环境的基础上,再加上依赖storm-kafka-client、kafka-clients 才能完成 kafka + storm集成
		2.class KanBanTopology驱动类:通过KafkaSpoutConfig类连接kafka的IP:端口和topic主题名,以进行消费topic主题中存储的数据
		3.class ParserPaymentBolt解析数据类:从topic主题中取出的数据都是JSON字符串,把JSON字符串转换为JavaBean对象,然后对JavaBean对象进行相应的数据计算
		4.class PaymentIndexProcessBolt存储数据到redis类:把计算好的数据又存放进Redis数据库中

本地模式运行 该程序

LocalCluster本地模式运行:
	LocalCluster本地模式指的是 可以本地window执行,也可以把 项目打包为jar包 或 同时把当前项目和所依赖的第三方jar包打包为一个jar包
	放到Linux下的storm集群中执行,但是在Storm UI监控管理页面(192.168.25.100:8088)是无法查看到 本地模式运行的该程序的运行信息
1.第一步:
	1.使用shell命令 创建kafka的topic主题,创建的时候并指定多个分片数/分区数
		cd /root/kafka
		格式:bin/kafka-topics.sh --create --zookeeper zookeeper的IP:2181 --replication-factor 副本数 --partitions 分片数/分区数 --topic 主题名
		例子:bin/kafka-topics.sh --create --zookeeper NODE1:2181 --replication-factor 1 --partitions 3 --topic payment
		      partitions 3:此处设置分片数/分区数为3,根据一个partition分区只能被一个消费者进行消费,
				  此处KafkaSpoutConfig类作为消费者负责连接partition分区进行获取数据。
				  因为有 3个分片数/分区数,那么作为消费者的KafkaSpoutConfig类和后面的Blot处理类每个类便都需要设置为3个线程执行。

	2.查看当前服务器中的所有 topic
			cd /root/kafka
			格式:bin/kafka-topics.sh --list --zookeeper zookeeper的IP:2181
			例子:bin/kafka-topics.sh --list --zookeeper NODE1:2181

2.第二步:编写kafka的生产者的java程序 生产大量数据 往kafka的topic主题中存储
	1.定义JavaBean类class Payment:定义了 订单支付的信息

	2.编写kafka的生产者:根据Payment类生产大量订单相关信息存到topic主题“payment”中(直接本地运行该程序)

3.第三步(测试):使用shell命令 从kafka的topic主题payment中消费数据 以用于测试是否成功
	cd /root/kafka
	格式:bin/kafka-console-consumer.sh --zookeeper zookeeper的IP:2181 --from-beginning --topic 主题名
	例子:bin/kafka-console-consumer.sh --zookeeper NODE1:2181 --from-beginning --topic payment

4.编写kafka + storm集成的java程序
	1.首先就需要在仅storm-core的storm环境的基础上,再加上依赖storm-kafka-client、kafka-clients 才能完成 kafka + storm集成
		1.如果storm集群上也提供了storm-core.jar,那么需要在项目编译的时候排除掉项目中所依赖的storm-core,只需要设置为provided,
                    目的为防止jar包冲突:storm集群中和编译打包后的项目jar包 两者中都同时存在storm-core.jar的话便会发生jar包冲突
		2.provided的使用注意:
			1.如果仅把项目单独打包为一个jar包放到linux中的storm集群中运行的话,就会直接使用storm集群中的storm-core.jar。
                        	2.但是如果同时把项目和项目所依赖的storm-core.jar一起打包为一个jar包放到linux中的storm集群中运行的话,
                            就要注意是否会发生jar包冲突,因为项目所依赖的jar包和storm集群环境中的jar包会发生冲突。

此处storm-core.jar包冲突的话,报该错误信息 Caused by: java.io.IOException: Found multiple defaults.yaml resources
需要在仅storm-core的storm环境的基础上,再加上依赖storm-kafka-client、kafka-clients 才能完成 kafka + storm集成

需要同时把项目和项目所依赖的storm-core.jar一起打包为一个jar包的话,需要配置以下插件信息

 

1.给“读取数据的”Spout类 设置线程数:
	1.Spout类要读取topic主题中多个分区数中的数据的话,那么所设置的运行Spout类的线程数则是和topic主题中的分区数应为一致。
	  根据一个分区只能被一个消费者所读取数据的原则,那么有多少个分区就应设置多少个消费者,所以此处就应设置多少个线程运行Spout类
	2.Spout类要读取本地文件中数据的话,应设置1个线程执行Spout类,即Spout类只会读取本地文件一次。
	  如果设置多个线程执行Spout类的话,那么便会有多个线程同时执行多个Spout类的task任务,那么便会读取本地文件多次,
	  便会造成读取多次没必要,一个线程默认有一个Spout类的task任务。
 
2.给“处理数据的Blot类 设置线程数:可以设置任意多个线程执行Blot类

 

	2.class KanBanTopology驱动类:
		通过KafkaSpoutConfig类连接kafka的IP:端口和topic主题名,以进行消费topic主题中存储的数据

	3.class ParserPaymentBolt解析数据类:
		从topic主题中取出的数据都是JSON字符串,把JSON字符串转换为JavaBean对象,然后对JavaBean对象进行相应的数据计算

	4.class PaymentIndexProcessBolt存储数据到redis类:
		把计算好的数据又存放进Redis数据库中
		报错问题:java.net.SocketTimeoutException: Read timed out
		分析:当使用一个redis连接,没有使用redis连接池的话,一旦遇到大量数据要存入到redis数据库的话,便会出现以上问题,无法快速地把大量数据存入到redis数据库
		解决:使用redis连接池

	5.以本地运行模式 运行该程序后,就能在Redis中查看到结果数据,下面是redis的相关操作
		查看所有key:keys *
		查看所有key的数量:DBSIZE
		删除所有key:FLUSHALL

linux下的 storm集群中 以集群模式 运行该程序 

StormSubmitter(storm集群模式运行):
	以集群模式运行表示 只能把 项目打包为jar包 或 同时把当前项目和所依赖的第三方jar包打包为一个jar包 放到Linux下的storm集群中执行
	此时可在Storm UI监控管理页面(192.168.25.100:8088)查看到storm集群模式下运行的该程序的运行信息
stormKafka-1.0-SNAPSHOT.jar:仅把当前项目打包为jar包,不包括所依赖的第三方jar包
stormKafka-1.0-SNAPSHOT-jar-with-dependencies.jar:同时把当前项目和所依赖的第三方jar包打包为一个jar包

 

linux下的storm集群中 以集群模式 运行该程序:
1.把 stormKafka-1.0-SNAPSHOT-jar-with-dependencies.jar 拷贝到 /root目录下,执行 cd /root
2.执行 ./storm jar 项目名.jar Topology驱动类的全限定名称(包名.类名)
  此处执行:
	cd /root/storm/bin
	./storm jar /root/stormKafka-1.0-SNAPSHOT-jar-with-dependencies.jar cn.itcast.realtime.storm.kanban.KanBanTopology
3.以集群运行模式 运行该程序后,就能在Redis中查看到结果数据,下面是redis的相关操作
		查看所有key:keys *
		查看所有key的数量:DBSIZE
		删除所有key:FLUSHALL

class KanBanTopology驱动类:
		通过KafkaSpoutConfig类连接kafka的IP:端口和topic主题名,以进行消费topic主题中存储的数据

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

あずにゃん

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值