一、大数据技术背景

大数据的维度分为五维:大量、高速、多样、精确、价值

大数据背景下,就是将数据集进行清洗处理,得到根据业务场景相关的各项指标。还可以通过开发分析引擎对各种指标的数据进行批处理作业,统计查询等。基本包括两大类型:分布式批处理以及实时计算。分布式批处理,可以看成离线处理,将数据收集到1个月一周或者一天进行处理,不要求纳秒/毫秒响应,应对不要求实时性的海量数据运算。这里不做过多讲解,等待小T的下一篇博客吧。

这里重点说下实时计算。

二、什么是实时处理

对于离线处理,自然就会出现实时处理。对于现实中的很多场景都需要进行实时计算处理,比如电信场景,电商猜你喜欢,收费站的分流处理,广告行业的精准投放,用户画像等等,都离不开实时计算的背景。

实时计算,就是需要一个复杂事件的处理引擎,以低延迟闪电速度进行处理以及查询结果,在本篇博客中利用storm进行实时计算的解决方案。

三、什么是storm

storm最初是来自Twitter的一个项目,后来贡献给Apache成为顶级开源项目。storm是一个高度可以扩展,分布式,快速,可靠地实时计算的解决方案的框架。它可以实现创建数据流模型,其中元组持续流过有处理组件集合而成的拓扑结构。可以配置数据来源。本篇讲述的数据来源只有kafka。

下面为storm功能流程图:

实时数据开发架构图 实时数据处理是指什么_kafka

四、安装部署讲解。

好了,接下来进入正题了,小T前面写了一些概述性的文字,不喜欢看的同学可以直接看接下来的讲述。

由于本篇讲解的实时计算是基于:flume =>kafka =>storm =>redis进行集成的。

技术流程图如下所示:

实时数据开发架构图 实时数据处理是指什么_kafka_02

接下来小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:

实时数据开发架构图 实时数据处理是指什么_实时数据开发架构图_03

这个是本地运行模式,也可以部署到服务器的storm进行集群/单机处理,利用maven打包上传jar包到linux指定的目录然后执行命令如下:

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

附着上github的源码地址:https://github.com/Jamsw/storm-realtime,喜欢的朋友给个星就好了哈~

ok,到这里基础的实时计算架构也算完成了,希望参考的同学可以针对各自的业务进行企业级或者个人版的开发,接下来有兴趣的朋友可以看下一篇讲解下 storm滑动窗口原理。