【Storm】【三】本地模式

1 storm运行模式

1、本地模式(Local Mode): 即Topology(相当于一个任务,后续会详细讲解) 运行在本地机器的单一JVM上,这个模式主要用来开发、调试。
2、远程模式(Remote Mode):在这个模式,我们把我们的Topology提交到集群,在这个模式中,Storm的所有组件都是线程安全的,因为它们都会运行在不同的Jvm或物理机器上,这个模式就是正式的生产模式。

2 需求

对文本文件中的单词进行计数,以下是文本文件中的内容,分隔符为制表符(\t):
storm hadoop hive zookeeper spark
hadoop hadoop storm spark storm
storm spark storm hadoop hadoop

3 storm实现wordcount(本地模式)

我们需要创建一这样的Topology,用一个spout负责读取文本文件,用第一个bolt来解析成单词,用第二个bolt来对解析出的单词计数,整体结构如图所示:

在这里插入图片描述

3.1 搭建基础开发环境

3.1.1 开发工具与JDK

idea
JDK 1.8

3.1.2 创建项目

创建一个java project,项目名为wordcount

在这里插入图片描述

3.1.3 导入相关jar

1)apache-storm-1.0.0.tar.gz压缩包中的lib文件夹下所有的jar

在这里插入图片描述

2)zookeeper的jar
zookeeper-3.4.5-cdh5.14.0.tar.gz压缩包中的zookeeper-3.4.5-cdh5.14.0.jar

3)项目中导入以上的jar

在这里插入图片描述

3.2 编写代码

3.2.1 编写spout读取文本文档

spout需要实现IRichSpout接口,实现以下的方法

在这里插入图片描述

实现代码如下:

package com.storm;

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;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.UUID;

/**
 * 读取txt文档,按行发射给bolt
 * @author Administrator
 *
 */
public class WordReader implements IRichSpout {
    /**
     * 序列化
     */
    private static final long serialVersionUID = 1L;
    private FileInputStream is;
    private InputStreamReader isr;
    private BufferedReader br;
    private String line = "";
    private SpoutOutputCollector collector;
    /**
     * 此方法用于声明当前Spout的Tuple发送流的域名字,即一个
     backtype.storm.tuple.Fields对象。
     * 这个对象和public void nextTuple()接口中emit的
     backtype.storm.tuple.Values
     * 共同组成了一个元组对象(backtype.storm.tuple.Tuple)
     * 供后面接收该数据的Blot使用
     * 运行TopologyBuilder的createTopology()时调用此方法
     */
    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("line"));
    }
    /**
     * 运行TopologyBuilder的createTopology()时调用此方法
     * 用于输出特定于Spout和Bolt实例的配置参数值对
     * 此方法用于声明针对当前组件的特殊的Configuration配置,在需要的情况下
     会进行配置。
     */
    @Override
    public Map<String, Object> getComponentConfiguration() {
        //设置Topology中当前组件的线程数量上限为3
        // Map<String, Object> ret = new HashMap<String, Object>();
        // ret.put(Config.TOPOLOGY_MAX_TASK_PARALLELISM, 3);
        // return ret;
        return null;
    }
    /**
     * 当一个Task被初始化的时候会调用此open方法。
     * 在这里要将SpoutOutputCollector spoutOutputCollector对象保存下来,
     * 供后面的public void nextTuple()接口使用,还可以执行一些其他的操作。
     * 例如这里将txt文档转换成流,也就是初始化操作。
     * 里面接收了三个参数,第一个是创建Topology时的配置,
     * 第二个是所有的Topology数据,第三个是用来把Spout的数据发射给bolt
     */
    @Override
    public void open(Map conf, TopologyContext context,
                     SpoutOutputCollector collector) {
        try {
            this.collector = collector;
            this.is = new FileInputStream("D:\\words.txt");
            this.isr = new InputStreamReader(is,"utf-8");
            this.br = new BufferedReader(isr);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 当thread运行完spout实例的open()方法之后,
     * 该spout实例处于deactivated(失效)模式,
     * 过段时间会变成activated(激活)模式,此时会调用Spout实例的activate()
     方法
     */
    @Override
    public void activate() {
    }
    /**
     * 接口该接口实现具体的读取数据源的方法。
     * 当该方法被调用时,要求SpoutOutputCollector喷射tuple。
     * 在本例中,nextTuple()方法是负责对txt文档转换成的流进行逐行读取。
     * 将获取的数据emit(发射)出去。
     * emit的对象是通过public Values createValues(String line)方法
     * 生成的backtype.storm.tuple.Values对象。
     * 该方法从数据源的一行数据中,选取的若干个目标值组成一个backtype.storm.tuple.Values对象。
     * 这个对象中可以存储不同类型的对象,例如你可以同时将String对象,
     * Long对象存取在一个backtype.storm.tuple.Values中emit出去。
     * 实际上只要实现了Storm要求的序列化接口的对象都可以存储在里面。
     * emit该值得时候需要注意,
     * 他的内容要和declareOutputFields中声明的backtype.storm.tuple.Fields
     对象相匹配,必须一一对应。
     * 他们被共同组成一个backtype.storm.tuple.Tuple元组对象,被后面接收该
     数据流的对象使用。
     */
    @Override
    public void nextTuple() {
        try {
            while ((this.line = this.br.readLine()) != null) {
                //数据过滤
                //发射数据
                this.collector.emit(new
                        Values(this.line), UUID.randomUUID());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 从此spout发射的带有messageID的tuple处理成功时调用此方法。
     * 该方法的一个典型实现是把消息从队列中移走,避免被再次处理。
     */
    @Override
    public void ack(Object msgId) {
        System.out.println("OK:" + msgId);
    }
    /**
     * 从此spout发射的带有messageID的tuple处理失败时调用此方法。
     */
    @Override
    public void fail(Object msgId) {
        System.out.println("FAIL:" + msgId);
    }
    /**
     * topology终止时,执行此方法
     * 在本例子中,可以在这个阶段释放资源
     */
    @Override
    public void close() {
        try {
            this.br.close();
            this.isr.close();
            this.is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 在Topology运行过程中,通过客户端执行deactivate命令,
     * 禁用指定的Topology时,被禁用的Topology的spout实例会变成
     deactivate(失效),
     * 并且会调用spout实例deactivate()方法
     */
    @Override
    public void deactivate() {
    }
}

3.2.2 编写bolt解析spout发射的每行数据

bolt需要实现IRichBolt接口,实现以下的方法

在这里插入图片描述

实现代码如下:

package com.storm;

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;

import java.util.Map;

/**
 * 解析spout发射的tuple,拆分成一个个单词发射给下一个bolt
 * @author Administrator
 *
 */
public class WordNormalizer implements IRichBolt {
    /**
     * 序列化
     */
    private static final long serialVersionUID = 1L;
    private OutputCollector collector;
    private String line;
    private String[] words;
    /**
     * 当这个组件的task在集群中的一台worker内被初始化的时候,该函数被调用。
     * 它向bolt提供了该bolt执行的环境
     * (
     * 里面接收了三个参数,
     * 第一个是创建Topology时的配置,
     * 第二个是所有的Topology数据,
     * 第三个是用来把Bolt的数据发射给下一个bolt
     * )
     * 在这里要将OutputCollector collector对象保存下来
     */
    @Override
    public void prepare(Map stormConf, TopologyContext context,
                        OutputCollector collector) {
        this.collector = collector;
    }
    /**
     * 处理输入的一个单一tuple。
     * 每次接收到元组时都会被调用一次,还会再发布若干个元组
     */
    @Override
    public void execute(Tuple input) {
        // line = (String)input.getValue(0);
        line = (String)input.getValueByField("line");//与上面等价
        words = line.split("\t");
        for(String word : words){
            collector.emit(new Values(word));
        }
        //成功,提示从此spout喷出的带有messageID的tuple已被完全处理,把消息从队列中移走,避免被再次处理。
        this.collector.ack(input);
    }
    /**
     * Topology执行完毕的清理工作,比如关闭连接、释放资源等操作都会写在这里
     * topology终止时,执行此方法
     * 注意:
     * 由于cleanup方法并不可靠,它只在local mode下生效,Storm集群模式下
     cleanup不会被调用执行。很多资源得不到释放
     * 所以,在kill topology之前,先deactivate相应的topology。
     * bolt中判断接收的数据为”shutDown”就调用cleanup()方法。在cleanup()
     方法中释放需要释放的资源。
     */
    @Override
    public void cleanup() {
    }
    /**
     * 此方法用于声明当前bolt的Tuple发送流的域名字,即一个
     backtype.storm.tuple.Fields对象。
     * 这个对象和public void execute(Tuple input)接口中emit的
     backtype.storm.tuple.Values
     * 共同组成了一个元组对象(backtype.storm.tuple.Tuple)
     * 供后面接收该数据的Blot使用
     */
    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("word"));
    }
    /**
     * 用于输出特定于Spout和Bolt实例的配置参数值对
     * 此方法用于声明针对当前组件的特殊的Configuration配置,在需要的情况下会进
     行配置。
     */
    @Override
    public Map<String, Object> getComponentConfiguration() {
        return null;
    }
}

3.2.3 编写bolt对上一个bolt发射的单词进行计数

bolt同样实现IRichBolt

具体代码如下:

package com.storm;

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;

import java.util.HashMap;
import java.util.Map;

/**
 * 接收WordNormalizer发射的单词,进行计数
 * @author Administrator
 *
 */
public class WordCounter implements IRichBolt {
    /**
     * 序列化
     */
    private static final long serialVersionUID = 1L;
    Integer id;
    String name;
    Map<String, Integer> counters;
    private OutputCollector collector;
    @Override
    public void prepare(Map stormConf, TopologyContext context,
                        OutputCollector collector) {
        this.counters = new HashMap<String, Integer>();
        this.collector = collector;
        this.name = context.getThisComponentId();
        this.id = context.getThisTaskId();
    }
    @Override
    public void execute(Tuple input) {
        String word = input.getString(0);
        //计数
        if(!counters.containsKey(word)){
            counters.put(word, 1);
        }else{
            Integer c = counters.get(word) + 1;
            counters.put(word, c);
        }
        //成功,提示从此spout喷出的带有messageID的tuple已被完全处理,把消息从队列中移走,避免被再次处理。
        this.collector.ack(input);
    }
    /**
     * Topology执行完毕的清理工作,比如关闭连接、释放资源等操作都会写在这
     里
     * topology终止时,执行此方法
     * 注意:
     * 由于cleanup方法并不可靠,它只在local mode下生效,Storm集群模式
     下cleanup不会被调用执行。很多资源得不到释放
     * 所以,在kill topology之前,先deactivate相应的topology。
     * bolt中判断接收的数据为”shutDown”就调用cleanup()方法。在
     cleanup()方法中释放需要释放的资源。
     *
     * 因为这只是个Demo,我们用它来打印我们的计数器
     */
    @Override
    public void cleanup() {
        System.out.println("-- Word Counter [" + name + "-" + id + "] --");
        for(Map.Entry<String, Integer> entry : counters.entrySet()){
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        counters.clear();
    }
    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
    //不再向外发射数据,此处不写代码
    }
    @Override
    public Map<String, Object> getComponentConfiguration() {
        return null;
    }
}

3.2.4 编写storm执行主类(本地模式)

在执行主类中对Topology进行设置,包括spout、bolt、分组、配置、运行模式的设置

具体代码实现:

package com.storm;


import org.apache.storm.Config;
import org.apache.storm.LocalCluster;
import org.apache.storm.topology.TopologyBuilder;
import org.apache.storm.tuple.Fields;
import org.apache.storm.utils.Utils;

/**
 * storm执行主类
 * @author Administrator
 *
 */
public class WordCountTopologyMain {
    public static void main(String[] args) {
        //定义一个Topology
        TopologyBuilder builder = new TopologyBuilder();
        //第3个参数设置并行度
        // builder.setSpout("spout", new WordReader(), 1);
        //2个参数,默认并行度为
        builder.setSpout("word-reader", new WordReader());
        builder.setBolt("word-normalizer", new
                WordNormalizer()).shuffleGrouping("word-reader");
        builder.setBolt("word-counter", new
                WordCounter(),2).fieldsGrouping("word-normalizer", new Fields("word"));
        //配置
        Config conf = new Config();
        conf.put("wordsFile", "E:/test.txt");
        conf.setDebug(false);
        //本地提交模式,例如eclipse执行main方法
        LocalCluster localCluster = new LocalCluster();
        localCluster.submitTopology("mytopology", conf,
                builder.createTopology());
        Utils.sleep(15000);
        //停止本地运行
        localCluster.shutdown();
    }
}

3.3 本地模式运行

直接在开发工具上运行执行主类的main方法,我这里是使用eclipse运行WordCountTopologyMain的main方法,执行结果如下:

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我知道了,以下是我的回答: 猫是很多人喜欢的宠物之一,但在养猫时也需要注意它们的健康。有时候,猫身上可能会出现一些外部寄生虫,比如蜱虫,这会对它们的健康造成影响。那么,当我们发现猫身上长了蜱虫时该怎么办呢? 首先,我们需要确认猫身上是否真的有蜱虫。我们可以仔细观察猫的皮毛,如果发现有小黑点在动,就有可能是蜱虫。另外,我们也可以用手摸一下猫的皮毛,如果感觉到像粉尘一样的小颗粒,也有可能是蜱虫。 确认之后,我们需要及时采取措施。最好的方法是带着猫到宠物诊所或兽医那里,由专业人员给猫清除蜱虫。在清除过程中,注意保持猫的安静,防止它们乱动,也可以给它们提供一些适当的饮食和水分。 如果无法立即到诊所或兽医处,我们也可以自行清除蜱虫。这需要一些简单的工具,比如镊子、棉签、酒精等。首先用镊子夹住蜱虫的身体,尽量避免夹住蜱虫的头部,然后慢慢拔出它。拔出之后,可以用酒精或棉签在伤口处消毒,帮助预防感染。 尽管我们可以自行清除蜱虫,但最好还是到专业机构去清除。这样可以更加有效地清除蜱虫,并且可以确保清除时不会对猫造成伤害。同时,在平时养猫过程中,我们也需要注意定期清洗猫的皮毛,以防止一些外部寄生虫的滋生。 总之,当我们发现猫身上长了蜱虫时,应该立即采取措施进行清除。如果可以,最好到专业机构去处理,这样可以更加安全有效。同时,也需要在平时养猫过程中注意清洁,以预防外部寄生虫的滋生。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

BirdMan98

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

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

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

打赏作者

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

抵扣说明:

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

余额充值