Storm集成Kafka应用的开发

https://www.cnblogs.com/freeweb/p/5292961.html

目录

引言:

首先编写storm消费kafka的逻辑,

配置kafka提交topology到storm的代码

编写一个类来进行生产


引言:

storm的作用主要是进行流式计算对于源源不断的均匀数据流流入处理是非常有效的,而现实生活中大部分场景并不是均匀的数据流,而是时而多时而少的数据流入,这种情况下显然用批量处理是不合适的,如果使用storm做实时计算的话可能因为数据拥堵而导致服务器挂掉,应对这种情况,使用kafka作为消息队列是非常合适的选择,kafka可以将不均匀的数据转换成均匀的消息流,从而和storm比较完善的结合,这样才可以实现稳定的流式计算,那么我们接下来开发一个简单的案例来实现storm和kafka的结合

storm和kafka结合,就是数据先进入kafka生产者,然后storm作为消费者进行消费,最后将消费后的数据输出或者保存到文件、数据库、分布式存储等

框图:

首先我们保证在服务器上zookeeper、kafka、storm正常运行,建立一个maven项目,在pom.xml写入如下代码:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>kafkastorm</groupId>
  <artifactId>kafkastorm</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>kafkastorm</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.storm</groupId>
        <artifactId>storm-core</artifactId>
        <version>0.9.6</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.kafka</groupId>
        <artifactId>kafka_2.9.2</artifactId>
        <version>0.8.2.2</version>
        <exclusions>
            <exclusion>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
            </exclusion>
            <exclusion>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.apache.storm</groupId>
        <artifactId>storm-kafka</artifactId>
        <version>0.9.6</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <configuration>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
        <executions>
          <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

主要是导入的zookeeper、storm、kafka外部依赖这些叠加起来,还有<plugin>插件便于我们后续对程序进程maven的打包

  和之前一样

首先编写storm消费kafka的逻辑,

MessageScheme类,代码如下:

package net.zengzhiying;

import java.io.UnsupportedEncodingException;
import java.util.List;

import backtype.storm.spout.Scheme;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Values;

public class MessageScheme implements Scheme {

    public List<Object> deserialize(byte[] arg0) {
        try {
            String msg = new String(arg0, "UTF-8");
            return new Values(msg);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Fields getOutputFields() {
        return new Fields("msg");
    }

}

逻辑很简单,就是对kafka出来的数据转换成字符串,接下来我们想办法来处理strom清洗之后的数据,我们为了简单就把输出保存到一个文件中,Bolt逻辑SenqueceBolt类的代码如下:

package net.zengzhiying;

import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import backtype.storm.topology.BasicOutputCollector;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseBasicBolt;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;

public class SenqueceBolt extends BaseBasicBolt {

    public void execute(Tuple arg0, BasicOutputCollector arg1) {
        String word = (String) arg0.getValue(0);
        String out = "output:" + word;
        System.out.println(out);

        //写文件
        try {
            DataOutputStream out_file = new DataOutputStream(new FileOutputStream("kafkastorm.out"));
            out_file.writeUTF(out);
            out_file.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        arg1.emit(new Values(out));
    }

    public void declareOutputFields(OutputFieldsDeclarer arg0) {
        arg0.declare(new Fields("message"));
    }

}

就是把输出的消息放到文件kafkastorm.out中

  然后我们编写主类,也就是

配置kafka提交topology到storm的代码

,类名为StormKafkaTopo,代码如下:

package net.zengzhiying;

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

import backtype.storm.Config;
import backtype.storm.LocalCluster;
import backtype.storm.StormSubmitter;
import backtype.storm.generated.AlreadyAliveException;
import backtype.storm.generated.InvalidTopologyException;
import backtype.storm.spout.SchemeAsMultiScheme;
import backtype.storm.topology.TopologyBuilder;
import backtype.storm.utils.Utils;
import storm.kafka.BrokerHosts;
import storm.kafka.KafkaSpout;
import storm.kafka.SpoutConfig;
import storm.kafka.ZkHosts;
import storm.kafka.bolt.KafkaBolt;

public class StormKafkaTopo {
    public static void main(String[] args) {
        BrokerHosts brokerHosts = new ZkHosts("192.168.1.216:2181/kafka");

        SpoutConfig spoutConfig = new SpoutConfig(brokerHosts, "topic1", "/kafka", "kafkaspout");

        Config conf = new Config();
        Map<String, String> map = new HashMap<String, String>();

        map.put("metadata.broker.list", "192.168.1.216:9092");
        map.put("serializer.class", "kafka.serializer.StringEncoder");
        conf.put("kafka.broker.properties", map);
        conf.put("topic", "topic2");

        spoutConfig.scheme = new SchemeAsMultiScheme(new MessageScheme());

        TopologyBuilder builder = new TopologyBuilder();
        builder.setSpout("spout", new KafkaSpout(spoutConfig));
        builder.setBolt("bolt", new SenqueceBolt()).shuffleGrouping("spout");
        builder.setBolt("kafkabolt", new KafkaBolt<String, Integer>()).shuffleGrouping("bolt");

        if(args != null && args.length > 0) {
            //提交到集群运行
            try {
                StormSubmitter.submitTopology(args[0], conf, builder.createTopology());
            } catch (AlreadyAliveException e) {
                e.printStackTrace();
            } catch (InvalidTopologyException e) {
                e.printStackTrace();
            }
        } else {
            //本地模式运行
            LocalCluster cluster = new LocalCluster();
            cluster.submitTopology("Topotest1121", conf, builder.createTopology());
            Utils.sleep(1000000);
            cluster.killTopology("Topotest1121");
            cluster.shutdown();
        }



    }
}

注意上面代码的配置,和之前单独运行storm和kafka代码不太一样,配置也很简单,注意区别即可,如果细心的话会注意到这里建了两个topic一个是topic1,一个是topic2,topic1的含义kafka接收生产者过来的数据所需要的topic,topic2是KafkaBolt也就是storm中的bolt生成的topic,当然这里topic2这行配置可以省略,是没有任何问题的,类似于一个中转的东西,另外我们这次测试是上传到服务器执行,本地模式的代码没有执行到,当然原理是一样的

  之前一般网上的教程到这里就完毕了,这样我们会引起一种没有生产者的误区,注意:上面3个类实现的功能是kafka消费者输出的数据被storm消费!生产者的代码可以看成独立的其他来源,可以写在其他项目中,根据数据源的情况来,下面我们为了示例,

编写一个类来进行生产

代码和之前kafka单独的一样:

package net.zengzhiying;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import kafka.javaapi.producer.Producer;
import kafka.producer.KeyedMessage;
import kafka.producer.ProducerConfig;

public class DataProducerInsert {
    private static Producer<Integer,String> producer;
    private final Properties props=new Properties();
    public DataProducerInsert(){
            //定义连接的broker list
            props.put("metadata.broker.list", "192.168.1.216:9092");
            //定义序列化类 Java中对象传输之前要序列化
            props.put("serializer.class", "kafka.serializer.StringEncoder");
            //props.put("advertised.host.name", "192.168.1.216");
            producer = new Producer<Integer, String>(new ProducerConfig(props));
    }
    public static void main(String[] args) {
            DataProducerInsert sp=new DataProducerInsert();
            //定义topic
            String topic="topic1";
            //开始时间统计
            long startTime = System.currentTimeMillis();
            //定义要发送给topic的消息
            String messageStr = "This is a message";
            List<KeyedMessage<Integer, String>> datalist = new ArrayList<KeyedMessage<Integer, String>>();

            //构建消息对象
            KeyedMessage<Integer, String> data = new KeyedMessage<Integer, String>(topic, messageStr);
            datalist.add(data);

            //结束时间统计
            long endTime = System.currentTimeMillis();
            KeyedMessage<Integer, String> data1 = new KeyedMessage<Integer, String>(topic, "用时" + (endTime-startTime)/1000.0);
            datalist.add(data1);

            //推送消息到broker
            producer.send(data);
            producer.close();
    }
}

注意,这里我们定义的topic是topic1,正好和前面的topic1数据源对应,是整个kafka保持一致的topic,也就是说kafka生产者topic和消费者topic是必须名称相同才可以响应,下面简单添加了一点时间统计的代码,也很简单

  另外还要注意kafka配置文件host.name尽量改成ip,和之前说过的一样

 主要的代码就集中在数据源也就是kafka生产者的发送和storm消费后的存储问题,这所有的代码都是在storm和kafka给好的方法内写逻辑

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值