这个实例中有一个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. ArrayList和数组间的相互转换

    开发中不免碰到List与数组类型之间的相互转换,举一个简单的例子: package test.test1; import java.util.ArrayList; import java.util.L ...

  2. Android----基于多触控的图片缩放和拖动代码实现

    引自:http://www.codefans.net/articles/584.shtml 一个android中自定义的ImageView控制,可对图片进行多点触控缩放和拖动类,包括了对图片放大和图片 ...

  3. PAT (Advanced Level) 1054. The Dominant Color (20)

    简单题 #include<cstdio> #include<cstring> #include<cmath> #include<vector> #inc ...

  4. BNU OJ 50998 BQG's Messy Code

    #include <cstdio> #define _(l) int l #define ___(l,L) for (_(o)=0,x=l o*2;o<x;o++)O= L o; # ...

  5. 漂亮的HTML表格 - ebirdfighter的日志 - 网易博客

    一个像素边框的表格: Info Header 1 Info Header 2 Info Header 3 Text 1A Text 1B Text 1C Text 2A Text 2B Text 2C ...

  6. mac android studio 更新sdk的解决方案

    试试这个,能解决国内访问Google服务器的困难 启动 Android SDK Manager ,打开主界面,依次选择「Tools」.「Options...」,弹出『Android SDK Manag ...

  7. CodeForces 622D Optimal Number Permutation

    是一个简单构造题. 请观察公式: 绝对值里面的就是 |di-(n-i)|,即di与(n-i)的差值的绝对值. 事实上,对于任何n,我们都可以构造出来每一个i的di与(n-i)的差值为0. 换句话说,就 ...

  8. 一次性能优化,tps从400+到4k+

    项目介绍 路由网关项目watchman ,接收前端http请求转发到后端业务系统,功能安全验证,限流,转发. 使用技术:spring boot+ nreflix zuul,最开始日志使用slf4j+l ...

  9. 3、手把手教你Extjs5(三)MVVM特性的简单说明

    下面我们来看一下自动生成的代码中的MVVM架构的关系.Main是一个可视的控件,MainController是这个控件的控制类,MainModel是这个控件的模型类. 在上面的图片中,左边是Main. ...

  10. iOS 之UICollectionView 之原理介绍

    0. 简介 参考:支持重排的iOS9 UICollectionView 参考:http://www.cnblogs.com/langtianya/p/3902801.html 参考:http://ww ...