这个实例中有一个KafkaSpout,一个KafkaBolt,一个自定义Bolt QueryBolt。数据流程是KafkaSpout从topic为recommend的消息队列中取出String类型的消息,发送给QueryBolt。QueryBolt不做任何处理,直接转发给KafkaBolt,只把经过的消息存储在list。QueryBolt中自定义了cleanup方法,该方法在topology被杀死时调用,方法中把list中的所有数据打印在"C://"+this+".txt"文件中。KafkaBolt将接收到的数据直接转存在主题为recevier的kafka消息队列中。

        代码结构:
         
        以下是详细代码:
首先是topology.java
import java.util.HashMap;
import java.util.Map;
 
import backtype.storm.Config;
import backtype.storm.LocalCluster;
//import backtype.storm.LocalCluster;
import backtype.storm.StormSubmitter;
import backtype.storm.spout.SchemeAsMultiScheme;
import backtype.storm.topology.TopologyBuilder;
import storm.kafka.BrokerHosts;
import storm.kafka.KafkaSpout;
import storm.kafka.SpoutConfig;
import storm.kafka.ZkHosts;
import storm.kafka.bolt.KafkaBolt;
 
public class topology {
    public static void main(String [] args) throws Exception{
        //配置zookeeper 主机:端口号
        BrokerHosts brokerHosts =new ZkHosts("110.64.76.130:2181,110.64.76.131:2181,110.64.76.132:2181");
        //接收消息队列的主题
        String topic="recommend";
        //zookeeper设置文件中的配置,如果zookeeper配置文件中设置为主机名:端口号 ,该项为空
        String zkRoot="";
        //任意
        String spoutId="zhou";
        SpoutConfig spoutConfig=new SpoutConfig(brokerHosts, topic, zkRoot, spoutId);
        //设置如何处理kafka消息队列输入流
        spoutConfig.scheme=new SchemeAsMultiScheme(new MessageScheme());
        Config conf=new Config();
        //不输出调试信息
        conf.setDebug(false);
        //设置一个spout task中处于pending状态的最大的tuples数量
        conf.put(Config.TOPOLOGY_MAX_SPOUT_PENDING, 1);
        Map<String, String> map=new HashMap<String,String>();
        // 配置Kafka broker地址
        map.put("metadata.broker.list""master:9092,slave1:9092,slave2:9092");
        // serializer.class为消息的序列化类
        map.put("serializer.class""kafka.serializer.StringEncoder");
        conf.put("kafka.broker.properties", map);
        // 配置KafkaBolt生成的topic
        conf.put("topic""receiver");
        TopologyBuilder builder =new TopologyBuilder();
        builder.setSpout("spout"new KafkaSpout(spoutConfig),1);
        builder.setBolt("bolt1"new QueryBolt(),1).setNumTasks(1).shuffleGrouping("spout");
        builder.setBolt("bolt2"new KafkaBolt<String, String>(),1).setNumTasks(1).shuffleGrouping("bolt1");
        if(args.length==0){
            LocalCluster cluster = new LocalCluster();
            //提交本地集群
            cluster.submitTopology("test", conf, builder.createTopology());
            //等待6s之后关闭集群
            Thread.sleep(6000);
            //关闭集群
            cluster.shutdown();
        }
        StormSubmitter.submitTopology("test", conf, builder.createTopology());
    }
}

然后是MessageScheme.java

import java.io.UnsupportedEncodingException;
import java.util.List;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import backtype.storm.spout.Scheme;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Values;
 
public class MessageScheme implements Scheme {
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageScheme.class);
    public List<Object> deserialize(byte[] ser) {
        try {
            //从kafka中读取的值直接序列化为UTF-8的str
            String mString=new String(ser, "UTF-8");
            return new Values(mString);
        catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            LOGGER.error("Cannot parse the provided message");
             
        }
        return null;
    }
 
    public Fields getOutputFields() {
        // TODO Auto-generated method stub
        return new Fields("msg");
    }
 
}

最后是QueryBolt.java

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Vector;
 
 
import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.IRichBolt;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;
 
public class QueryBolt implements IRichBolt {
     
    List<String> list;
    OutputCollector collector;
    public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
         
        list=new ArrayList<String>();
        this.collector=collector;
         
    }
 
    public void execute(Tuple input) {
        // TODO Auto-generated method stub
        String str=(String) input.getValue(0);
        //将str加入到list
        list.add(str);
        //发送ack
        collector.ack(input);
        //发送该str
        collector.emit(new Values(str));
    }
 
    public void cleanup() {//topology被killed时调用
        //将list的值写入到文件
        try {
            FileOutputStream outputStream=new FileOutputStream("C://"+this+".txt");
            PrintStream p=new PrintStream(outputStream);
            p.println("begin!");
            p.println(list.size());
            for(String tmp:list){
                p.println(tmp);
            }
            p.println("end!");
            try {
                p.close();
                outputStream.close();
            catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
             
        catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
 
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
     
        declarer.declare(new Fields("message"));
         
    }
 
    public Map<String, Object> getComponentConfiguration() {
        // TODO Auto-generated method stub
        return null;
    }
 
}

问题1:zkRoot如何设置?非常重要,设置错误无法正确从kafka消息队列中取出数据。

观察 server.properties 文件:
zookeeper.connect=master:2181,slave1:2181,slave2:2181
此时zkRoot="";
如果zookeeper.connect=master:2181,slave1:2181,slave2:2181/ok
此时zkRoot等于"/ok"
问题2:为什么KafkaSpout启动之后,不能从头开始读起,而是自动跳过了kafka消息队列之前的内容,只处理KafkaSpout启动之后消息队列中新增的值?
因为KafkaSpout默认跳过了Kafka消息队列之前就存在的值,如果要从头开始处理,那么需要设置spoutConfig.forceFromStart=true,即从offset最小的开始读起。
 
附录:KafkaSpout中关于 SpoutConfig的相关定义
SpoutConfig继承自KafkaConfig。由于SpoutConfig和KafkaConfig所有的instance field全是public, 因此在使用构造方法后,可以直接设置各个域的值。
 
public class SpoutConfig extends KafkaConfig implements Serializable {
    public List<String> zkServers = null//记录Spout读取进度所用的zookeeper的host
    public Integer zkPort = null;//记录进度用的zookeeper的端口
    public String zkRoot = null;//进度信息记录于zookeeper的哪个路径下
    public String id = null;//进度记录的id,想要一个新的Spout读取之前的记录,应把它的id设为跟之前的一样。
    public long stateUpdateIntervalMs = 2000;//用于metrics,多久更新一次状态。
 
    public SpoutConfig(BrokerHosts hosts, String topic, String zkRoot, String id) {
        super(hosts, topic);
        this.zkRoot = zkRoot;
        this.id = id;
    }
}
public class KafkaConfig implements Serializable {
 
    public final BrokerHosts hosts; //用以获取Kafka broker和partition的信息
    public final String topic;//从哪个topic读取消息
    public final String clientId; // SimpleConsumer所用的client id
 
    public int fetchSizeBytes = 1024 1024//发给Kafka的每个FetchRequest中,用此指定想要的response中总的消息的大小
    public int socketTimeoutMs = 10000;//与Kafka broker的连接的socket超时时间
    public int fetchMaxWait = 10000;   //当服务器没有新消息时,消费者会等待这些时间
    public int bufferSizeBytes = 1024 1024;//SimpleConsumer所使用的SocketChannel的读缓冲区大小
    public MultiScheme scheme = new RawMultiScheme();//从Kafka中取出的byte[],该如何反序列化
    public boolean forceFromStart = false;//是否强制从Kafka中offset最小的开始读起
    public long startOffsetTime = kafka.api.OffsetRequest.EarliestTime();//从何时的offset时间开始读,默认为最旧的offset
    public long maxOffsetBehind = 100000;//KafkaSpout读取的进度与目标进度相差多少,相差太多,Spout会丢弃中间的消息
    public boolean useStartOffsetTimeIfOffsetOutOfRange = true;//如果所请求的offset对应的消息在Kafka中不存在,是否使用startOffsetTime
    public int metricsTimeBucketSizeInSecs = 60;//多长时间统计一次metrics
 
    public KafkaConfig(BrokerHosts hosts, String topic) {
        this(hosts, topic, kafka.api.OffsetRequest.DefaultClientId());
    }
 
    public KafkaConfig(BrokerHosts hosts, String topic, String clientId) {
        this.hosts = hosts;
        this.topic = topic;
        this.clientId = clientId;
    }
 
}

kafkaspout以及kafkabolt的最简实例的更多相关文章

  1. 最简实例演示asp.net5中用户认证和授权(4)

    上篇: 最简实例演示asp.net5中用户认证和授权(3) 上面我们把自定义认证和授权的相关的最小基础类和要实现的接口都实现了,下面就是如何来进行认证和授权的配置. 首先我们要告诉系统,我们的用户和角 ...

  2. 最简实例演示asp.net5中用户认证和授权(3)

    上接: 最简实例演示asp.net5中用户认证和授权(2) 在实现了角色的各种管理接口后,下一步就是实现对用户的管理,对用户管理的接口相对多一些,必须要实现的有如下三个: 1 public inter ...

  3. 最简实例演示asp.net5中用户认证和授权(2)

    上接最简实例演示asp.net5中用户认证和授权(1) 基础类建立好后,下一步就要创建对基础类进行操作的类了,也就是实现基础类的增删改查(听起来不太高大上),当然,为了使用asp.net5的认证机制, ...

  4. 最简实例演示asp.net5中用户认证和授权(1)

    asp.net5中,关于用户的认证和授权提供了非常丰富的功能,如果结合ef7的话,可以自动生成相关的数据库表,调用也很方便. 但是,要理解这么一大堆关于认证授权的类,或者想按照自己项目的特定要求对认证 ...

  5. 最简实例说明wait、notify、notifyAll的使用方法

    wait().notify().notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态. 这三个方法最终调用的都是jvm级的native方法.随着jvm运行平台的不同可能有些 ...

  6. inheritprototype原型继承封装及综合继承最简实例

    1.inheritprototype.js ;(function(){    var s = {        inheritObject:function(o){//对象继承封装           ...

  7. Express极简实例

    假设已创建一个Express工程,否则请参考express工程环境准备 修改app.js var express = require('express'); var app = express(); ...

  8. Servlet(1):基础概念/最简实例

    Servlet 生命周期(1) init()方法初始化Servlet对象  它在第一次创建Servlet时被调用,在后续每次不同用户请求时不再调用.(2) service()方法来处理客户端的请求  ...

  9. kafka主题offset各种需求修改方法

    简要:开发中,常常因为需要我们要认为修改消费者实例对kafka某个主题消费的偏移量.具体如何修改?为什么可行?其实很容易,有时候只要我们换一种方式思考,如果我自己实现kafka消费者,我该如何让我们的 ...

随机推荐

  1. 表达式语言--在MVC中应用表达式语言

    之前讲解的MVC设计模式中一直有DAO存在,而且所有的对象都保存在VO之中,那么这时如果将一个VO传递到JSP文件中,那么JSP需要导入VO包,如果使用表达式语言的话,导入VO包就没有任何意义了. V ...

  2. textbox文本键盘全选

    private void textBox1_KeyDown(object sender, KeyEventArgs e)        {            if (e.Modifiers == ...

  3. js 鼠标事件

    <html><head lang="en"> <meta charset="UTF-8"> <title>< ...

  4. 填写信息的文章区域text_area

    <!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>& ...

  5. linux操作命令实验

    实验内容:文件操作与用户操作实验 实验设备(环境):电脑.Vmware WorkStation 实验步骤: 一.创建新用户bob 目的:练习useradd命令 二.为新用户bob设置口令 目的:练习p ...

  6. PAT (Advanced Level) 1045. Favorite Color Stripe (30)

    最长公共子序列变形. #include<iostream> #include<cstring> #include<cmath> #include<algori ...

  7. Django之路:简介以及环境

     (sudo) pip install Django 或者 (sudo) pip install Django==1.6.10 或者 pip install Django==1.7.6 Windows ...

  8. BLDC(无刷直流电机)应用相关

    1.基于XC866的直流无刷电机简易正弦波控制 http://blog.gkong.com/hushunlin_219521.ashx 2.无刷直流电机的PWM调制方式介绍 http://blog.g ...

  9. javascript 中 function bind()

    Function bind() and currying <%-- All JavaScript functions have a method called bind that binds t ...

  10. 函数之局部变量和使用global语句

    局部变量当你在函数定义内声明变量的时候,它们与函数外具有相同名称的其他变量没有任何关系,即变量名称对于函数来说是 局部 的.这称为变量的 作用域 .所有变量的作用域是它们被定义的块,从它们的名称被定义 ...