淘宝双11实时数据分析项目报告

本文详细介绍了如何利用Python、Spark Streaming、Kafka和Flask构建实时数据流处理系统,包括实时省份销量统计、用户行为分析、词频统计,并结合Hive、MySQL和ECharts进行数据批量处理和可视化。展示了从数据采集、流计算到结果展示的完整流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

环境部署

相关环境部署的笔记如下:

  1. zookeeper和spark安装:在zookeeper上搭建Spark集群的小笔记

  2. kafka安装和使用:kafka学习笔记

  3. sbt下载和安装:Linux无坑安装sbt

项目主要架构

数据总体上可以分为静态数据和流数据。对静态数据和流数据的处理,对应着两种截然不同的计算模式:批量计算和实时计算。批量计算以“静态数据”为对象,可以在很充裕的时间内对海量数据进行批量处理,计算得到有价值的信息。Hadoop就是典型的批处理模型,由HDFS和HBase存放大量的静态数据,由MapReduce负责对海量数据执行批量计算。流数据必须采用实时计算,实时计算最重要的一个需求是能够实时得到计算结果,一般要求响应时间为秒级。当只需要处理少量数据时,实时计算并不是问题;但是,在大数据时代,不仅数据格式复杂、来源众多,而且数据量巨大,这就对实时计算提出了很大的挑战。因此,针对流数据的实时计算——流计算,应运而生。

流计算处理过程包括数据实时采集、数据实时计算和实时查询服务。

  • 数据实时采集:数据实时采集阶段通常采集多个数据源的海量数据,需要保证实时性、低延迟与稳定可靠。目前有许多互联网公司发布的开源分布式日志采集系统均可满足每秒数百MB的数据采集和传输需求,如Kafka和Flume等。
  • 数据实时计算:流计算处理系统接收数据采集系统不断发来的实时数据,实时地进行分析计算,并反馈实时结果。
  • 实时查询服务:流计算的第三个阶段是实时查询服务,经由流计算框架得出的结果可供用户进行实时查询、展示或储存。(一般是把计算结果实时地推送给用户)
  1. 实时计算架构
    在这里插入图片描述

    • python读取csv文件,传送数据到kafka
    • spark-streaming和kafka集成
    • spark-streaming读取传送到kafka的数据,进行实时统计,统计后的数据再推送到kafka
    • flask实时接收处理好的kafka数据,利用flask-socketio推送到客户端,进行绘图展示
  2. 批量计算架构

    • 把待分析的数据集导入hive
    • hive编写HQL语句批量分析数据
    • sqoop把hive中分析后的结果导入mysql中
    • flask读取mysql数据,利用echarts绘图进行可视化分析

在这里插入图片描述

具体步骤
流计算步骤

在统计淘宝双11的各省份的实时销量、淘宝双11的实时年龄分布、淘宝双11的各购物行为所占的比重、淘宝双11那天的日活量均采用流计算。

python连接kafka

在这一步中,python读取csv文件并连接kafka,把数据传送过去

需要安装pykafka、pandas等第三方库

pip install pykafka

python连接kafka代码

# coding: utf-8
import pandas as pd
from pykafka import KafkaClient
import json
 

class DataHandle:
    def __init__(self):
        self.path = "data_format/user_log.csv"
        self.double11_path = "data_format/double11_user_log.csv"
        self.double11Buy_path = "data_format/double11Buy_user_log.csv"
    def select_double11AndBuy(self):
        data = pd.read_csv(self.path)
        data.drop_duplicates(inplace = True)
        # #筛选出双11那天的用户购买记录数据并保存
        buy_data = data.loc[data['action'] == 2]
        double11_buyData = buy_data[(buy_data["month"] == 11)&(buy_data["day"] == 11)]
        double11_buyData.to_csv(self.double11Buy_path,index=None)
        #筛选出双11的用户行为记录(点击、收藏、购买、关注)
        double11_data = data[(data["month"] == 11)&(data["day"] == 11)]
        double11_data.to_csv(self.double11_path,index = None)
        return 0
    def get_area_data(self):
        """获得双11那天的省份销量数据"""
        area_data = pd.read_csv(self.double11Buy_path)
        return area_data["province"]
    def get_transform_data(self):
        transform_data = pd.read_csv(self.double11_path)
        return transform_data["action"]
    def get_age_data(self):
        transform_data = pd.read_csv(self.double11Buy_path)
        return transform_data["age_range"]

class operateKafka:
    def __init__(self):
        self.myhosts = "Master:9092"
        self.client = KafkaClient(hosts=self.myhosts)                          
 
    def sendMessage(self, dataList,topic_name):
        topic = self.client.topics[topic_name]
        with topic.get_sync_producer() as producer:
            for data in dataList:
                mydict = str(data)
                python_to_json = json.dumps(mydict, ensure_ascii=False)
                producer.produce((str(python_to_json)).encode())
 
if __name__ == "__main__":
    dataHandle = DataHandle()
    myopKafka = operateKafka()
    dataHandle.select_double11AndBuy()
    #各个地区的销量
    myopKafka.sendMessage(dataHandle.get_area_data().values,"area_data2")
    myopKafka.sendMessage(dataHandle.get_transform_data().values,"action_class")
    myopKafka.sendMessage(dataHandle.get_age_data().values,"age_data")

执行上述代码之后可在kafka集群这边任意一个节点上测试是否能接收到数据,从而判断数据是否成功传送到kafka。(以实时省份销量数据为例)

cd /usr/local/kafka/bin
./kafka-console-consumer.sh --bootstrap-server Master:9092 --topic area_data2 --from-beginning

在这里插入图片描述

spark-streaming集成kafka

Spark Streaming的基本原理是将实时输入数据流以时间片(秒级)为单位进行拆分,然后经Spark引擎以类似批处理的方式处理每个时间片数据,能做到秒级响应,不能做到毫秒级响应。Spark Streaming实际上是仿照流计算,并不是真正实时的流计算框架。

kafka安装:kafka学习笔记,为和下面的spark-streaming-kafka集成jar包适配,建议装0.8或者0.10版本的kafka(分情况,看spark版本)。

下载spark-streaming-kafka的jar包:

spark2.3.0版本以下(不包括2.3.0)的可以下载spark-streaming-kafka-0-8的集成:spark-streaming-kafka-0-8,选择与自己scala和spark版本相对应的jar包下载

spark2.3.0以上版本(包括2.3.0)的可以下载spark-streaming-kafka-0-10以上的集成:spark-streaming-kafka-0-10

把下载的jar包拷贝到/usr/local/spark/jars下

在/usr/local/spark/jars目录下,新建一个kafka目录,把kafka安装目录的libs目录下的所有jar文件复制到/usr/local/spark/jars/kafka目录下,

cd /usr/local/kafka/libs
cp ./* /usr/local/spark/jars/kafka

下面测试spark和kafka环境是否连通

启动spark-shell

cd /usr/local/spark/bin
 ./spark-shell --master spark://Master:7077

在spark-shell里引入如下jar包

import org.apache.spark.streaming.kafka010._

若引入jar包不出错,说明kafka和spark环境已经连通

编写并运行spark-streaming程序(实时词频统计)

scala的官方API文档:https://www.scala-lang.org/api/2.11.12/#scala.package,此文档对应的scala版本是2.11.12,也可以到:https://docs.scala-lang.org/api/all.html 查找与自己的scala版本相对应的API文档。

spark的官方API文档:http://spark.apache.org/docs/2.4.5/api/scala/index.html#org.apache.spark.package,此文档对应的scala版本是2.11.12,也可以到:http://spark.apache.org/docs/ 查找与自己的spark版本相对应的API文档。

创建项目spark_connect_kafka

cd /usr/local/spark/mycode
mkdir spark_connect_kafka
cd spark_connect_kafka
mkdir -p src/main/scala

编写scala代码

vi kafkaCount.scala
package org.apache.spark.examples.streaming

import java.util.HashMap
import org.apache.kafka.clients.producer.{
   
   KafkaProducer, ProducerConfig, ProducerRecord}
import org.apache.kafka.clients.consumer.ConsumerConfig
import org.apache.kafka.common.serialization.StringDeserializer
import org.json4s._
import org.json4s.jackson.Serialization
import org.json4s.jackson.Serialization.write
import org.apache.spark.SparkConf
import org.apache.spark.streaming._
import org.apache.spark.streaming.Interval
import org.apache.spark.streaming.kafka010._

object KafkaWordCount {
   
   
  //数据格式化时需要
  implicit val formats = DefaultFormats
  def main(args: Array[String]): Unit={
   
   
    //判断参数输入是否齐全,当不齐全时退出程序
    if (args.length < 4) {
   
   
      System.err.println("Usage: KafkaWordCount <brokers> <groupId> <topics>")
      System.exit(1)
    }
      
    /* 输入的四个参数分别代表着
    * 1. brokers为要消费的topics所在的broker地址
    * 2. group为消费者所在的组
    * 3. topics该消费者所消费的topics
    * 4. topics2为要推送到kafka的数据分析结果的topics
    */
    //把输入的4个参数封装成数组
    val Array(brokers, groupId, topics,topics2) = args
    val sparkConf = new SparkConf().setAppName("KafkaWordCount")
    val ssc = new StreamingContext(sparkConf, Seconds(1))
    //把检查点写入本地磁盘文件,不写入hadoop不必启动hadoop
    ssc.checkpoint("file:///home/hadoop/data/checkpoint")

    //一个consumer-group可以消费多个topic,这里把topics按照“,”分割放进set里(虽然我们一般只是传进去一个topic)
    val topicsSet = topics.split(",").toSet
      
    val kafkaParams = Map[String, Object](
      ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> brokers,
      ConsumerConfig.GROUP_ID_CONFIG -> groupId,
      "auto.offset.reset" -> "earliest",
      ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> classOf[StringDeserializer],
      ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> classOf[StringDeserializer])
    //创建连接Kafka的消费者链接
    val messages = KafkaUtils.createDirectStream[String, String](
      ssc,
      LocationStrategies.PreferConsistent,
      ConsumerStrategies.Subscribe[String, String](topicsSet, kafkaParams))

	//获取输入的每行数据,将输入的每行用空格分割成一个个word(这里的代码逻辑是词频统计的逻辑)
    val lines = messages.map(_.value)
    val words = lines.flatMap(_.split(" "))

    // 对每一秒的输入数据进行reduce,然后将reduce后的数据发送给Kafka
    val wordCounts = words.map(x => (x, 1L))
        .reduceByKeyAndWindow(_+_,_-_, Seconds(3), Seconds(1), 3).foreachRDD(rdd => {
   
   
          if(rdd.count !=0 ){
   
   
               //创建kafka生产者配置文件(数据结构为HashMap,初始为空,不断添加配置)
               val props = new HashMap[String, Object]()
               props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "Master:9092")
               //key序列号方式
               props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
               "org.apache.kafka.common.serialization.StringSerializer")
               //value序列号方式
               props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
               "org.apache.kafka.common.serialization.StringSerializer")
               //实例化一个Kafka生产者
               val producer = new KafkaProducer[String, String](props)
               //rdd.colect即将rdd中数据转化为数组,然后write函数将rdd内容转化为json格式
               val str = write(rdd
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值