大数据入门第十八天——kafka整合flume、storm

一、实时业务指标分析

  1.业务

  业务:
    订单系统---->MQ---->Kakfa--->Storm
    数据:订单编号、订单时间、支付编号、支付时间、商品编号、商家名称、商品价格、优惠价格、支付金额

    统计双十一当前的订单金额,订单数量,订单人数
    订单金额(整个网站,各个业务线,各个品类,各个店铺,各个品牌,每个商品

  架构

支付系统+kafka+storm/Jstorm集群+redis集群
1、支付系统发送mq到kafka集群中,编写storm程序消费kafka的数据并计算实时的订单数量、订单数量
2、将计算的实时结果保存在redis中
3、外部程序访问redis进群中的数据实时展示结果

  流程

1、Spout获取外部数据源,数据源是订单的mq,mq有固定的格式,比如json串。
2、对订单mq进行解析,得到一个对象->JavaBean
        订单编号、订单时间、支付编号、支付时间、商品编号、商家名称、商品价格、优惠价格、支付金额
3、对指标进行计数
        //业务中一个订单包含多个商品,需要对每个商品进行指标计算
        //创建订单和取消订单两种类型,在计算总数据的是考虑将取消订单的金额减掉
        //订单中有拆单的逻辑,该如何计算
4、保存指标数据到Redis

  2.整合flume思路

   flume官网也是对应的kafka的sink配置与讲解http://flume.apache.org/FlumeUserGuide.html#kafka-sink

    (网上的一些例如brokelist等配置已经被标记为deprecated,请参照官网的最新属性配置)

 将采集的日志文件保存到kafka中
  (source) 输入:tail -F xxxx.log
  (channel)存储:内存
  (sink) 输出:kafka

config
  al.source = s1
  a1.channel = c1
  al.sink = k1

  source ==> exec tail -F xxxx.log
  channel ==> RAM
  sink ====> xxx.xxxx.xxxx.KafkaSink //该类必须存放lib目录
  sink.topic = orderMq
  sink.itcast = itcast

   // 实例参照下文

二、整合flume

  1.编写flume配置文件

a1.sources = s1                                                                                                                  
a1.channels = c1                                                                                                                 
a1.sinks = k1                                                                                                                    
                                                                                                                                      
a1.sources.s1.type=exec                                                                                                          
a1.sources.s1.command=tail -F /home/hadoop/kafka.log                                                                                
a1.sources.s1.channels=c1                                                                                                        
a1.channels.c1.type=memory                                                                                                       
a1.channels.c1.capacity=10000                                                                                                    
a1.channels.c1.transactionCapacity=100                                                                                           
                                                                                                                                      
#设置Kafka接收器                                                                                                                    
a1.sinks.k1.type= org.apache.flume.sink.kafka.KafkaSink                                                                          
#设置Kafka的broker地址和端口号                                                                                                      
a1.sinks.k1.brokerList=mini1:9092                                                                                               
#设置Kafka的Topic                                                                                                                   
a1.sinks.k1.topic=topic_1                                                                                                      
#设置序列化方式                                                                                                                     
a1.sinks.k1.serializer.class=kafka.serializer.StringEncoder                                                                                                                                                                                                    
a1.sinks.k1.channel=c1 

  2.开启需要的zk以及kafka服务

[hadoop@mini1 conf]$ start-zk.sh 
[hadoop@mini1 conf]$ start-kafka.sh 

  // 以上两个均以编写一键启动脚本,详情参考相关篇章随笔

  启动flume:

bin/flume-ng agent -c conf -f conf/tail-kafka.conf -n a1  -Dflume.root.logger=INFO,console

  启动消费者查看效果:

bin/kafka-console-consumer.sh --zookeeper mini1:2181 --from-beginning --topic topic_1

  之前都是直接复制命令而不关心参数,通过官网查看可以发现查看完整参数的方法为:(网上找的基本都是这个常用参数的格式)

All of the command line tools have additional options; running the command with no arguments will display usage information documenting them in more detail.

  这样就说明sink到了kafka了!

三、整合storm

  1.maven依赖

    storm整合kafka需要一个中间的依赖Jar

<!-- https://mvnrepository.com/artifact/org.apache.storm/storm-kafka -->
<dependency>
    <groupId>org.apache.storm</groupId>
    <artifactId>storm-kafka</artifactId>
    <version>0.9.5</version>
</dependency>

    这里指出,storm入门中使用的storm的包,应当替换为阿里的更加强大的JStorm:

<!-- https://mvnrepository.com/artifact/com.alibaba.jstorm/jstorm-core -->
        <dependency>
            <groupId>com.alibaba.jstorm</groupId>
            <artifactId>jstorm-core</artifactId>
            <version>2.1.1</version>
            <!--<scope>provided</scope>-->
        </dependency>

    其他相关依赖如下:

 <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.36</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.7.3</version>
        </dependency>

  这样,将数据提前导入redis以后,就可以写出一个demo版本的代码了:(注意MyBolt#exector()方法中需要手动调用collection.ack()进行应答)

package kafkaAndStorm;


import backtype.storm.Config;
import backtype.storm.LocalCluster;
import backtype.storm.StormSubmitter;
import backtype.storm.topology.TopologyBuilder;
import storm.kafka.KafkaSpout;
import storm.kafka.SpoutConfig;
import storm.kafka.ZkHosts;

public class KafkaAndStormTopologyMain {
    public static void main(String[] args) throws Exception{
        TopologyBuilder topologyBuilder = new TopologyBuilder();
        topologyBuilder.setSpout("kafkaSpout",
                new KafkaSpout(new SpoutConfig(
                        new ZkHosts("zk01:2181,zk02:2181,zk03:2181"),
                        "orderMq",
                        "/myKafka",
                        "kafkaSpout")),1);
        topologyBuilder.setBolt("mybolt1",new ParserOrderMqBolt(),1).shuffleGrouping("kafkaSpout");

        Config config = new Config();
        config.setNumWorkers(1);

        //3、提交任务  -----两种模式 本地模式和集群模式
        if (args.length>0) {
            StormSubmitter.submitTopology(args[0], config, topologyBuilder.createTopology());
        }else {
            LocalCluster localCluster = new LocalCluster();
            localCluster.submitTopology("storm2kafka", config, topologyBuilder.createTopology());
        }
    }
}
KafkaAndStormTopologyMain
package kafkaAndStorm;

import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseRichBolt;
import backtype.storm.tuple.Tuple;
import com.google.gson.Gson;
import order.OrderInfo;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

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

/**
 * Created by maoxiangyi on 2016/5/4.
 */
public class ParserOrderMqBolt extends BaseRichBolt {
    private JedisPool pool;
    @Override
    public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
        //change "maxActive" -> "maxTotal" and "maxWait" -> "maxWaitMillis" in all examples
        JedisPoolConfig config = new JedisPoolConfig();
        //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
        config.setMaxIdle(5);
        //控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;
        //如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
        //在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
        config.setMaxTotal(1000 * 100);
        //表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
        config.setMaxWaitMillis(30);
        config.setTestOnBorrow(true);
        config.setTestOnReturn(true);
        /**
         *如果你遇到 java.net.SocketTimeoutException: Read timed out exception的异常信息
         *请尝试在构造JedisPool的时候设置自己的超时值. JedisPool默认的超时时间是2秒(单位毫秒)
         */
        pool = new JedisPool(config, "127.0.0.1", 6379);
    }

    @Override
    public void execute(Tuple input) {
        Jedis jedis = pool.getResource();
        //获取kafkaSpout发送过来的数据,是一个json
        String string = new String((byte[]) input.getValue(0));
        //解析json
        OrderInfo orderInfo = (OrderInfo) new  Gson().fromJson(string, OrderInfo.class);
        //整个网站,各个业务线,各个品类,各个店铺,各个品牌,每个商品
        //获取整个网站的金额统计指标
//        String totalAmount =  jedis.get("totalAmount");
        jedis.incrBy("totalAmount",orderInfo.getProductPrice());
        //获取商品所属业务线的指标信息
        String bid =  getBubyProductId(orderInfo.getProductId(),"b");
//        String bAmout =  jedis.get(bid+"Amout");
        jedis.incrBy(bid+"Amount",orderInfo.getProductPrice());
        jedis.close();
    }

    private String getBubyProductId(String productId,String type) {
//        key:value
        //index:productID:info---->Map
        //  productId-----<各个业务线,各个品类,各个店铺,各个品牌,每个商品>
        Map<String,String> map =  new HashMap<>();
        map.put("b","3c");
        map.put("c","phone");
        map.put("s","121");
        map.put("p","iphone");
        return map.get(type);
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {

    }
}
ParserOrderMqBolt

  有关redis的章节,参考redis随笔http://www.cnblogs.com/jiangbei/p/7255994.html

 

posted @ 2018-03-12 17:44  ---江北  阅读(614)  评论(0编辑  收藏  举报
TOP