大數據篇(一):實時計算(storm集成kafka的流式處理)講解

一、大數據技術背景

大數據的維度分爲五維:大量、高速、多樣、精確、價值

大數據背景下,就是將數據集進行清洗處理,得到根據業務場景相關的各項指標。還可以通過開發分析引擎對各種指標的數據進行批處理作業,統計查詢等。基本包括兩大類型:分佈式批處理以及實時計算。分佈式批處理,可以看成離線處理,將數據收集到1個月一週或者一天進行處理,不要求納秒/毫秒響應,應對不要求實時性的海量數據運算。這裏不做過多講解,等待小T的下一篇博客吧。

這裏重點說下實時計算。

二、什麼是實時處理

對於離線處理,自然就會出現實時處理。對於現實中的很多場景都需要進行實時計算處理,比如電信場景,電商猜你喜歡,收費站的分流處理,廣告行業的精準投放,用戶畫像等等,都離不開實時計算的背景。

實時計算,就是需要一個複雜事件的處理引擎,以低延遲閃電速度進行處理以及查詢結果,在本篇博客中利用storm進行實時計算的解決方案。

三、什麼是storm

storm最初是來自Twitter的一個項目,後來貢獻給Apache成爲頂級開源項目。storm是一個高度可以擴展,分佈式,快速,可靠地實時計算的解決方案的框架。它可以實現創建數據流模型,其中元組持續流過有處理組件集合而成的拓撲結構。可以配置數據來源。本篇講述的數據來源只有kafka。

下面爲storm功能流程圖:

四、安裝部署講解。

好了,接下來進入正題了,小T前面寫了一些概述性的文字,不喜歡看的同學可以直接看接下來的講述。

由於本篇講解的實時計算是基於:flume =>kafka =>storm =>redis進行集成的。

技術流程圖如下所示:

接下來小T給大家講述下各個工具的部署方式:

(1)flume部署安裝:

flume的下載地址:http://flume.apache.org/download.html

下載完成後,將壓縮包上傳至linux服務器指定的目錄下,編輯 conf 文件夾內的 flume-conf.properties.template 文件,修改方式如下所示:

a1.sources = taildir
a1.channels = pv-counter uv-counter
a1.sinks = pv-sink uv-sink

# Describe/configure the source
a1.sources.taildir.type = TAILDIR
a1.sources.taildir.positionFile = /home/bigdata/flume/partition/patition.json
a1.sources.taildir.filegroups = f1
a1.sources.taildir.filegroups.f1 = /data/.*.log
a1.sources.taildir.channels = pv-counter uv-counter

# interceptor
a1.sources.taildir.interceptors = interceptor
a1.sources.taildir.interceptors.interceptor.type = regex_extractor
a1.sources.taildir.interceptors.interceptor.regex = .*(pv_viewer|uv_viewer).*
a1.sources.taildir.interceptors.interceptor.serializers = s1
a1.sources.taildir.interceptors.interceptor.serializers.s1.name = key

# selector
a1.sources.taildir.selector.type = multiplexing
a1.sources.taildir.selector.header = key
a1.sources.taildir.selector.mapping.pv_viewer = pv-counter
a1.sources.taildir.selector.mapping.uv_viewer = uv-counter


a1.channels.pv-counte.type = memory
a1.channels.pv-counte.capacity=10000
a1.channels.pv-counte.byteCapacityBufferPercentage=2000
# uv-sink
a1.channels.uv-sink.type = memory
a1.channels.uv-sink.capacity=10000
a1.channels.uv-sink.byteCapacityBufferPercentage=2000


a1.sinks.pv-sink.type = org.apache.flume.sink.kafka.KafkaSink
a1.sinks.pv-sink.kafka.topic = pv-counter
a1.sinks.pv-sink.kafka.bootstrap.servers = 127.0.0.1:9092
a1.sinks.pv-sink.kafka.flumeBatchSize = 2000
a1.sinks.pv-sink.kafka.producer.acks = 1
a1.sinks.pv-sink.channel = pv-counter
# 替換相應的kafka的IP
a1.sinks.uv-sink.type = org.apache.flume.sink.kafka.KafkaSink
a1.sinks.uv-sink.kafka.topic = uv-counter
a1.sinks.uv-sink.kafka.bootstrap.servers = 127.0.0.1:9092
a1.sinks.uv-sink.kafka.flumeBatchSize = 2000
a1.sinks.uv-sink.kafka.producer.acks = 1
a1.sinks.uv-sink.channel = uv-sink

配置簡單講解:將 /data 目錄下的 .log結尾的日誌中有pv_viewer|uv_viewer 的進行處理,輸出到kafak中,對應的topic名字爲:pv-counter|uv-counter 。

然後進行執行命令:bin/flume-ng agent --conf conf --conf-file conf/flume-conf.properties --name a1  &

(2)kafka部署安裝

kafka的安裝利用docker進行構建安裝

執行命令:docker pull wurstmeister/kafka,拉取kafka的docker鏡像

然後進行執行運行命令:docker run -d --name kafka --publish 9092:9092 --link zookeeper --env KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 --env KAFKA_ADVERTISED_HOST_NAME=127.0.0.1 --env KAFKA_ADVERTISED_PORT=9092 --volume /etc/localtime:/etc/localtime wurstmeister/kafka:latest

(3)storm安裝(此處就按照demo講解進行單機安裝了)

storm下載地址:https://storm.apache.org/downloads.html

配置環境變量參數,修改/etc/profile文件,複製下面的參數:

export STORM_HOME=/home/storm-2.0.0
export PATH=$PATH:$STORM_HOME/bin

記下來修改 /conf 文件夾內的 storm.yaml,複製下面的配置:

storm.zookeeper.servers:
      - "localhost"


nimbus.host: "master"
storm.local.dir: "/home/storm-2.0.0/data"
ui.port: 8889
supervisor.slots.ports:
- 6700
- 6701
- 6702
- 6703

ok了,配置完成接下來進行逐一命令啓動,命令如下:

storm nimbus & ;
storm supervisor & ;
storm ui & ;

(4)redis部署安裝:

 redis部署同樣利用docker進行簡易部署,部署方式如下:

拉取redis的docker鏡像:docker pull redis

然後運行docker:docker run -p 6378:6379 --name redisRealtime -v /usr/local/redis/etc/redis.conf:/etc/redis/redis.conf -v /usr/local/redis/data:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes

五:實時計算開發

好了,講解好了各種工具的部署完成之後,可以進行storm和kafak集成的項目開發了,接下來就是進行各項作業指標的開發。

整合講解:

前端日誌採集之後,通過flume配置對應的counter名稱,然後輸出到kafak的topic,開發storm和kakfa集成作業,通過topic消費數據,storm利用滑動窗口,進行配置每10秒收集數據,清洗之後,傳遞給下一個bolt進行內存聚合計算處理,然後存儲到redis即可。

附着代碼如下:

package cn.sparticles.service;

import cn.sparticles.service.bolt.DayPVViewsBolt;
import cn.sparticles.service.bolt.PVBolt;
import org.apache.storm.Config;
import org.apache.storm.LocalCluster;
import org.apache.storm.StormSubmitter;
import org.apache.storm.kafka.spout.KafkaSpout;
import org.apache.storm.kafka.spout.KafkaSpoutConfig;
import org.apache.storm.topology.TopologyBuilder;

public class MyKafkaTopology  {

    public static void main(String[] args) throws Exception {
        KafkaSpoutConfig.Builder<String,String> builder = KafkaSpoutConfig.builder("127.0.0.1:9092","pv-counter");
        builder.setProp("group.id","testStorm");
        KafkaSpoutConfig<String, String> kafkaSpoutConfig= builder.build();
        TopologyBuilder topologyBuilder = new TopologyBuilder();
        topologyBuilder.setSpout("WordCountFileSpout",new KafkaSpout(kafkaSpoutConfig), 4);
        topologyBuilder.setBolt("PVCount",new PVBolt()).shuffleGrouping("WordCountFileSpout");
        topologyBuilder.setBolt("RedisPVCache",new DayPVViewsBolt()).shuffleGrouping("PVCount");
        Config config = new Config();
        if(args !=null && args.length > 0){
            config.setDebug(false);
            StormSubmitter submitter= new StormSubmitter();
            submitter.submitTopology("kafkaStromTopo", config, topologyBuilder.createTopology());
        }else{
            config.setDebug(true);
            LocalCluster cluster= new LocalCluster();
            cluster.submitTopology("kafkaStromTopo", config, topologyBuilder.createTopology());
        }

    }
}
package cn.sparticles.service.bolt;

import cn.sparticles.utils.RedisUtil;
import com.google.common.collect.Lists;
import org.apache.commons.lang.StringUtils;
import org.apache.storm.Config;
import org.apache.storm.Constants;
import org.apache.storm.topology.BasicOutputCollector;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseBasicBolt;
import org.apache.storm.tuple.Tuple;

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

public class DayPVViewsBolt extends BaseBasicBolt {

    private List<String> list = Lists.newArrayList();

    private String redisKey = "PVDayCount";
    @Override
    public void execute(Tuple input, BasicOutputCollector collector) {
        if (!input.getSourceComponent().equalsIgnoreCase(Constants.SYSTEM_COMPONENT_ID)) {  // 如果收到非系統級別的tuple,統計信息到局部變量mids
            String logStr = input.getStringByField("logStr");
            list.add(logStr);
        }
        else {
            RedisUtil redisUtil = RedisUtil.getInstance();
            String count = redisUtil.get(redisKey);
            if(StringUtils.isBlank(count)){
                count = "0";
            }
            redisUtil.set(redisKey,String.valueOf(Long.valueOf(count) + list.size()));
        }
    }
    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer){
    }

    /**
     * 定時任務進行執行
     * @return
     */
    @Override
    public Map<String, Object> getComponentConfiguration() {
        Map<String, Object> config = new HashMap<>();
        config.put(Config.TOPOLOGY_TICK_TUPLE_FREQ_SECS, 10);
        return config;
    }
}
package cn.sparticles.service.bolt;

import cn.sparticles.model.LabelCommonModel;
import cn.sparticles.utils.FastJsonUtils;
import org.apache.storm.topology.BasicOutputCollector;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseBasicBolt;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Tuple;
import org.apache.storm.tuple.Values;


public class PVBolt extends BaseBasicBolt {

    /**
     * 進行沒10秒一次聚合,聚合完成傳遞下一個bolt進行redis存儲持久化處理
     * @param input
     * @param collector
     */
    @Override
    public void execute(Tuple input, BasicOutputCollector collector) {
        //進行格式化數據處理
        String kafkaTopicMes = (String) input.getValue(4);
        LabelCommonModel labelCommonModel = FastJsonUtils.toBean(kafkaTopicMes, LabelCommonModel.class);
        collector.emit(new Values(labelCommonModel.getLogStr()));
    }
    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer){
        declarer.declare(new Fields("logStr"));
    }


}

執行之後,可以登錄redis看到生成的key-value:

這個是本地運行模式,也可以部署到服務器的storm進行集羣/單機處理,利用maven打包上傳jar包到linux指定的目錄然後執行命令如下:

storm jar storm-realtime.jar  cn.sparticles.service.MyKafkaTopology.java pv

附着上github的源碼地址:https://github.com/Jamsw/storm-realtime,喜歡的朋友給個星就好了哈~

ok,到這裏基礎的實時計算架構也算完成了,希望參考的同學可以針對各自的業務進行企業級或者個人版的開發,接下來有興趣的朋友可以看下一篇講解下 storm滑動窗口原理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章