电商数仓学习笔记(一)

第一章 数据仓库概念

第二章 项目需求及架构设计

项目需求

项目框架

第三章 数据生成模块

目标数据

数据埋点

服务器和jdk准备

编写集群分发脚本xsync

模拟数据

集群日志生成脚本

第四章 数据采集模块

集群所有进程查看脚本

Hadoop安装

LZO压缩配置

LZO创建索引

Hadoop参数调优

Zookeeper安装

ZK集群启停脚本

Kafka安装

Kafka集群安装

安装部署

Kafka集群启动停止脚本

Kafka常用命令

Kafka机器数量计算

Kafka分区数计算

采集日志Flume

日志采集Flume安装

Flume组件选型

日志采集Flume配置

Flume拦截器

测试Flume-Kafka通道

日志采集Flume启动停止脚本

消费Kafka数据Flume

Flume组件选型

消费者Flume配置

Flume时间拦截器

消费者Flume启动停止脚本

Flume内存优化

采集通道启动/停止脚本

常见问题及解决方案


前置知识:linux基本用法并对hadoop生态的基础架构有一定了解

第一章 数据仓库概念

数据仓库( Data Warehouse ),是为企业制定决策,提供数据支持的。可以帮助企业,改进业务流程、提高产品质量等。

数据仓库的输入数据通常包括:业务数据用户行为数据爬虫数据

业务数据:就是各行业在处理事务过程中产生的数据。比如用户在电商网站中登录、下单、支付等过程中,需要和网站后台数据库进行增删改查交互,产生的数据就是业务数据业务数据通常存储在MySQL、Oracle等数据库中。

用户行为数据:用户在使用产品过程中,通过埋点收集与客户端产品交互过程中产生的数据,并发往日志服务器进行保存。比如页面浏览、点击、停留、评论、点赞、收藏等。用户行为数据通常存储在日志文件中。

爬虫数据:通常事通过技术手段获取其他公司网站的数据。不建议使用

电商数仓学习笔记(一)_第1张图片

第二章 项目需求及架构设计

项目需求

  1. 用户行为数据采集平台搭建

  2. 业务数据采集频台搭建

  3. 数据仓库维度建模

  4. 分析,设备、会员、商品、地区、活动等电商核心主题,统计的报表指标近100个

  5. 采用即席查询工具,随时进行指标分析

  6. 对集群性能进行监控,发生异常需要报警

  7. 元数据管理

  8. 质量监控

  9. 权限管理

项目框架

技术选型

主要考虑因素:数据量大小、业务需求、行业内经验、技术成熟度、开发维护成本、总成本预算

  • 数据采集传输:Flume,Kafka,Sqoop,Logstash,DataX

  • 数据存储:Mysql,HDFS,HBase,Redis,MongoDB

  • 数据计算:Hive,Tez,Spark,Flink,Storm

  • 数据查询:Presto,Kylin,Impala,Druid,ClickHouse,Doris

  • 数据可视化:Echarts,Superset,QuickBI,Doris

  • 任务调度:Azkaban,Oozie,DolphinScheduler,Airflow

  • 集群监控:Zabbix,Prometheus

  • 元数据管理:Atlas

  • 权限管理:Ranger,Sentry

系统数据流程设计

电商数仓学习笔记(一)_第2张图片

 

框架版本选型

1)如何选择Apache/CDH/HDP版本

  • Apache:运维麻烦,组件兼容性需要自己调研(一般大厂使用,技术实例雄厚,有专业的运维人员)

  • CDH:国内使用最多的版本,但CM不开源

  • HDP:开源,可以进行二次开发,但没有CDH稳定,国内使用较少

第三章 数据生成模块

目标数据

要收集和分析的数据主要包括页面数据、时间数据、曝光数据、启动数据、错误数据

页面

页面数据主要记录一个页面的用户访问情况,包括访问时间、停留时间、页面路径等信息

电商数仓学习笔记(一)_第3张图片

 电商数仓学习笔记(一)_第4张图片

 电商数仓学习笔记(一)_第5张图片

 电商数仓学习笔记(一)_第6张图片

 

事件

事件数据主要记录应用内一个具体操作行为,包括操作类型、操作对象、操作对象描述等信息

电商数仓学习笔记(一)_第7张图片

 电商数仓学习笔记(一)_第8张图片

 

曝光

曝光数据主要记录页面所曝光的内容,包括曝光对象,曝光类型等信息

电商数仓学习笔记(一)_第9张图片

 电商数仓学习笔记(一)_第10张图片

 

启动

电商数仓学习笔记(一)_第11张图片

 

错误

错误数据记录应用使用

过程中的的错误信息,包括错误编码及错误信息

电商数仓学习笔记(一)_第12张图片

 

数据埋点

主流埋点方式(了解)

目前主流的埋点方式,有代码埋点(前端/后端)、可视化埋点、全埋点三种。

代码埋点是通过调用埋点SDK函数,在需要埋点的业务逻辑功能位置调用接口,上报埋点数据。例如,我们对页面中的某个按钮埋点后,当这个按钮被点击时,可以在这个按钮对应的 OnClick 函数里面调用SDK提供的数据发送接口,来发送数据。

可视化埋点只需要研发人员集成采集 SDK,不需要写埋点代码,业务人员就可以通过访问分析平台的“圈选”功能,来“圈”出需要对用户行为进行捕捉的控件,并对该事件进行命名。圈选完毕后,这些配置会同步到各个用户的终端上,由采集 SDK 按照圈选的配置自动进行用户行为数据的采集和发送。

全埋点是通过在产品中嵌入SDK,前端自动采集页面上的全部用户行为事件,上报埋点数据,相当于做了一个统一的埋点。然后再通过界面配置哪些数据需要在系统里面进行分析。

埋点数据上报时机

买点数据上报时机包括两种方式

  • 方式一,在离开该页面时上传在这个页面产生的所有数据(页面、事件、曝光、错误等)。优点:批处理,减少了服务器接收数据压力。缺点:不是特别及时

  • 方式二:每个事件、动作、错误等,产生后立即发生。优点:响应及时。缺点:对服务器接收数据压力比较大

本次项目采用方式一埋点

埋点数据日志结构

我们的日志结构大致可分为两类,一是普通页面埋点日志,二是启动日志。

普通页面日志结构如下,每条日志包含了,当前页面的页面信息,所有事件(动作)、所有曝光信息以及错误信息。除此之外,还包含了一系列公共信息,包括设备信息,地理位置,应用信息等,即下边的common字段。

(1)普通页面埋点日志格式

{
  "common": {                  -- 公共信息
    "ar": "230000",              -- 地区编码
    "ba": "iPhone",              -- 手机品牌
    "ch": "Appstore",            -- 渠道
    "is_new": "1",--是否首日使用,首次使用的当日,该字段值为1,过了24:00,该字段置为0。
    "md": "iPhone 8",            -- 手机型号
    "mid": "YXfhjAYH6As2z9Iq", -- 设备id
    "os": "iOS 13.2.9",          -- 操作系统
    "uid": "485",                 -- 会员id
    "vc": "v2.1.134"             -- app版本号
  },
"actions": [                     --动作(事件)  
    {
  "action_id": "favor_add",   --动作id
      "item": "3",                   --目标id
      "item_type": "sku_id",       --目标类型
      "ts": 1585744376605           --动作时间戳
    }
  ],
  "displays": [
    {
      "displayType": "query",        -- 曝光类型
      "item": "3",                     -- 曝光对象id
      "item_type": "sku_id",         -- 曝光对象类型
      "order": 1,                      --出现顺序
      "pos_id": 2                      --曝光位置
    },
    {
      "displayType": "promotion",
      "item": "6",
      "item_type": "sku_id",
      "order": 2, 
      "pos_id": 1
    },
    {
      "displayType": "promotion",
      "item": "9",
      "item_type": "sku_id",
      "order": 3, 
      "pos_id": 3
    },
    {
      "displayType": "recommend",
      "item": "6",
      "item_type": "sku_id",
      "order": 4, 
      "pos_id": 2
    },
    {
      "displayType": "query ",
      "item": "6",
      "item_type": "sku_id",
      "order": 5, 
      "pos_id": 1
    }
  ],
  "page": {                       --页面信息
    "during_time": 7648,        -- 持续时间毫秒
    "item": "3",                  -- 目标id
    "item_type": "sku_id",      -- 目标类型
    "last_page_id": "login",    -- 上页类型
    "page_id": "good_detail",   -- 页面ID
    "sourceType": "promotion"   -- 来源类型
  },
"err":{                     --错误
"error_code": "1234",      --错误码
    "msg": "***********"       --错误信息
},
  "ts": 1585744374423  --跳入时间戳
}

(2)启动日志格式

启动日志结构相对简单,主要包含公共信息,启动信息和错误信息

{
  "common": {
    "ar": "370000",
    "ba": "Honor",
    "ch": "wandoujia",
    "is_new": "1",
    "md": "Honor 20s",
    "mid": "eQF5boERMJFOujcp",
    "os": "Android 11.0",
    "uid": "76",
    "vc": "v2.1.134"
  },
  "start": {   
    "entry": "icon",         --icon手机图标  notice 通知   install 安装后启动
    "loading_time": 18803,  --启动加载时间
    "open_ad_id": 7,        --广告页ID
    "open_ad_ms": 3449,    -- 广告总共播放时间
    "open_ad_skip_ms": 1989   --  用户跳过广告时点
  },
"err":{                     --错误
"error_code": "1234",      --错误码
    "msg": "***********"       --错误信息
},
  "ts": 1585744304000
}

服务器和jdk准备

编写集群分发脚本xsync

需求:循环赋值文件到所有节点的相同目录下

需求分析:

rsync命令原始拷贝

rsync -av /opt/module  root@Hadoop103:/opt/

期望脚本

xsync 要同步的文件名

说明:在/home/admin/bin 这个目录下存放的脚本,admin用户可以在系统任何地方直接执行

脚本实现

  • 在/home/admin/bin目录下创建xsync文件

    cd /home/admin/bin
    vim xsync

    脚本中编写如下脚本

    #!/bin/bash
    ​
    #1. 判断参数个数
    if [ $# -lt 1 ]
    then
        echo Not Enough Arguement!
        exit;
    fi
    #2. 遍历集群所有机器
    for host in hadoop102 hadoop103 hadoop104
    do
        echo ====================  $host  ====================
        #3. 遍历所有目录,挨个发送
    ​
        for file in $@
        do
            #4. 判断文件是否存在
            if [ -e $file ]
                then
                    #5. 获取父目录
                    pdir=$(cd -P $(dirname $file); pwd)
    ​
                    #6. 获取当前文件的名称
                    fname=$(basename $file)
                    ssh $host "mkdir -p $pdir"
                    rsync -av $pdir/$fname $host:$pdir
                else
                    echo $file does not exists!
            fi
        done
    done

    修改脚本xsync具有执行权限

chmod +x xsync

测试脚本

xsync /home/admin/bin

SSH无密登录配置

说明:这里面只配置了hadoop102、hadoop103到其他主机的无密登录;因为hadoop102未外配置的是NameNode,hadoop103配置的是ResourceManager,都要求对其他节点无密访问。

(1)hadoop102上生成公钥和私钥

在.ssh目录下

ssh-keygen -t rsa

然后敲三下回车,会生成两个文件私钥和公钥

如果没有.ssh,可以先ssh hadoop103 登录后exit退出

(2)将hadoop193公钥拷贝到要免密登录的目标主机上

ssh-copy-id hadoop102
ssh-copy-id hadoop103
ssh-copy-id hadoop104

(3)hadoop103上生成公钥和密钥

ssh-keygen -t rsa

(4)将hadoop103公钥拷贝到要免密登录的目标机器上

ssh-copy-id hadoop102
ssh-copy-id hadoop103
ssh-copy-id hadoop104

卸载现有jdk

安装jdk

配置环境变量

使用xsync脚本分发jdk

分发环境变量配置文件

在hadoop103和hadoop104上执行source

环境变量配置说明

linux的环境变量可在多个文件中配置,如/etc/prifile,/etc/profile.d/*.sh,~/.bashrc,~/.bash_profile等,下面说明上述几个文件之间的关系和区别。

bash的运行模式可以分为login shell 和non-login shell

例如,通过终端,输入用户名、密码,登录系统后,得到的就是一个login shell

执行 ssh hadoop103 command,在hadoop执行command的就是一个non-login shell

登录shell和分登录shell区别

电商数仓学习笔记(一)_第13张图片

 

这两种shell主要区别在于,它们启动时会加载不同的配置文件,login shell启动时会加载/etc/profile,~/.bash_profile,~/.bashrc。non-login shell启动时会加载~/.bashrc。

而在加载~/.bashrc(实际是~/.bashrc中加载的/etc/bashrc)或/etc/profile时,都会执行如下代码片段

电商数仓学习笔记(一)_第14张图片

 

因此不管是login shell还是non-login shell,启动时都会加载/etc/profile.d/*.sh中的环境变量

模拟数据

使用说明

1)将application.yml gmall2020-mock-log-2021-01-22.jar path.json logback.xml上传到hadoop102的/opt/module/applog目录下

配置文件

(1)application.yml文件

可以根据需求生成对应日期的用户行为日志

内容如下

# 外部配置打开
# 外部配置打开
logging.config: "./logback.xml"
#业务日期  注意:并不是Linux系统生成日志的日期,而是生成数据中的时间
mock.date: "2020-06-14"
​
#模拟数据发送模式
#mock.type: "http"
#mock.type: "kafka"
mock.type: "log"
​
#http模式下,发送的地址
mock.url: "http://hdp1/applog"
​
#kafka模式下,发送的地址
mock:
  kafka-server: "hdp1:9092,hdp2:9092,hdp3:9092"
  kafka-topic: "ODS_BASE_LOG"
​
#启动次数
mock.startup.count: 200
#设备最大值
mock.max.mid: 500000
#会员最大值
mock.max.uid: 100
#商品最大值
mock.max.sku-id: 35
#页面平均访问时间
mock.page.during-time-ms: 20000
#错误概率 百分比
mock.error.rate: 3
#每条日志发送延迟 ms
mock.log.sleep: 10
#商品详情来源  用户查询,商品推广,智能推荐, 促销活动
mock.detail.source-type-rate: "40:25:15:20"
#领取购物券概率
mock.if_get_coupon_rate: 75
#购物券最大id
mock.max.coupon-id: 3
#搜索关键词  
mock.search.keyword: "图书,小米,iphone11,电视,口红,ps5,苹果手机,小米盒子"

(2)path.json 该文件用来配置访问路径

根据需求,可以灵活配置用户点击路径

[
    {"path":["home","good_list","good_detail","cart","trade","payment"],"rate":20 },
    {"path":["home","search","good_list","good_detail","login","good_detail","cart","trade","payment"],"rate":40 },
    {"path":["home","mine","orders_unpaid","trade","payment"],"rate":10 },
    {"path":["home","mine","orders_unpaid","good_detail","good_spec","comment","trade","payment"],"rate":5 },
    {"path":["home","mine","orders_unpaid","good_detail","good_spec","comment","home"],"rate":5 },
    {"path":["home","good_detail"],"rate":10 },
    {"path":["home"  ],"rate":10 }
]

(3)logback配置文件

可配置日志生成路径,修改内容如下



    
    
        
            %msg%n
        
    
​
    
        
            ${LOG_HOME}/app.%d{yyyy-MM-dd}.log
        
        
            %msg%n
        
    
​
    
    
        
        
    
​
    
        
    

3)生成日志

进入到/opt/module/applog执行以下命令

java -jar gmall2020-mock-log-2021-01-22.jar

集群日志生成脚本

在hadoop的/home/admin目录下创建bin目录,这样脚本可以在服务器的任何目录执行

(1)在/home/admin/bin目录下创建脚本 lg.sh

vim lg.sh

(2)脚本内容如下

#!/bin/bash
for i in hadoop102 hadoop103; do
    echo "========== $i =========="
    ssh $i "cd /opt/module/applog/; java -jar gmall2020-mock-log-2021-01-22.jar >/dev/null 2>&1 &"
done 

注:

/opt/module/applog/为jar包及配置文件所在路径

/dev/null代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失

标准输入0:从键盘获取输入/proc/self/fd/0

标准输出1:输出到屏幕(即控制台)/proc/self/fd/1

错误输出2:输出到屏幕(即控制台)/proc/self/fd/2

(3)修改脚本执行权限

chmod u+x lg.sh

(4)将jar包及配置文件上传至hadoop103的/opt/module/applog/路径

(5)启动脚本

lg.sh

(6)分别在hadoop102、hadoop103的/opt/module/applog/log目录上查看生成的数据

ls app.2020-06-14.log

 

 

第四章 数据采集模块

集群所有进程查看脚本

(1)在/home/admin/bin 目录下创建xcall.sh

脚本内容

#! /bin/bash
 
for i in hadoop102 hadoop103 hadoop104
do
    echo --------- $i ----------
    ssh $i "$*"
done

(2)修改脚本执行权限

chmod 777 xcall.sh

(3)启动脚本

xcall.sh jps

Hadoop安装

(1)集群规划

电商数仓学习笔记(一)_第15张图片

 

LZO压缩配置

1)hadoop-lzo编译

hadoop本身并不支持lzo压缩,故需要使用twitter提供的hadoop-lzo开源组件。hadoop-lzo需依赖hadoop和lzo进行编译

2)将编译好后的hadoop-lzo-0.4.20.jar放入hadoop-3.1.3/share/hadoop/common/

cd /opt/module/hadoop-3.1.3/share/hadoop/common

3)同步hadoop-lzo-0.4.20.jar 到hadoop103、hadoop104

xsync hadoop-lzo-0.4.20.jar

4)core-site.xml增加配置支持LZO压缩


    
        io.compression.codecs
        
            org.apache.hadoop.io.compress.GzipCodec,
            org.apache.hadoop.io.compress.DefaultCodec,
            org.apache.hadoop.io.compress.BZip2Codec,
            org.apache.hadoop.io.compress.SnappyCodec,
            com.hadoop.compression.lzo.LzoCodec,
            com.hadoop.compression.lzo.LzopCodec
        
    
​
    
        io.compression.codec.lzo.class
        com.hadoop.compression.lzo.LzoCodec
    

5)同步core-site.xml到hadoop103、hadoop104

xsync core-site.xml

6)启动及查看集群

[hadoop102]  sbin/start-dfs.sh
[hadoop103] sbin/start-yarn.sh

7)测试-数据准备

hadoop fs -mkdir /input
hadoop fs -put README.txt /input

8)测试-压缩

hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount -Dmapreduce.output.fileoutputformat.compress=true -Dmapreduce.output.fileoutputformat.compress.codec=com.hadoop.compression.lzo.LzopCodec  /input /output

LZO创建索引

1)创建LZO文件的索引

LZO压缩文件的可切片特性依赖于其索引,故我们需要手动为LZO压缩文件创建索引。若无索引,则LZO文件的切片只有一个。

hadoop jar /path/to/your/hadoop-lzo.jar com.hadoop.compression.lzo.DistributedLzoIndexer big_file.lzo

2)测试

(1)将bigtable.lzo(200M)上传到集群的根目录

hadoop fs -mkdir /input
hadoop fs -put bigtable.lzo /input

(2)执行wordcount程序

hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount -Dmapreduce.job.inputformat.class=com.hadoop.mapreduce.LzoTextInputFormat /input /output1

电商数仓学习笔记(一)_第16张图片

(3)对上传的LZO文件建索引

hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/common/hadoop-lzo-0.4.20.jar  com.hadoop.compression.lzo.DistributedLzoIndexer /input/bigtable.lzo

电商数仓学习笔记(一)_第17张图片

(4)再次执行WordCount程序

hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount -Dmapreduce.job.inputformat.class=com.hadoop.mapreduce.LzoTextInputFormat /input /output2

电商数仓学习笔记(一)_第18张图片

注:如果遇到如下错误

Container [pid=8468,containerID=container_1594198338753_0001_01_000002] is running 318740992B beyond the 'VIRTUAL' memory limit. Current usage: 111.5 MB of 1 GB physical memory used; 2.4 GB of 2.1 GB virtual memory used. Killing container.
Dump of the process-tree for container_1594198338753_0001_01_000002 :

解决方法:在hadoop102的/opt/module/hadoop-3.1.3/etc/hadoop/yarn-site.xml文件中增加如下配置,然后分发到hadoop103、hadoop104服务器上,并重新启动集群



   yarn.nodemanager.vmem-check-enabled
   false

Hadoop参数调优

1)HDFS参数调优hdfs-site.xml


The number of Namenode RPC server threads that listen to requests from clients. If dfs.namenode.servicerpc-address is not configured then Namenode RPC server threads listen to requests from all nodes.
NameNode有一个工作线程池,用来处理不同DataNode的并发心跳以及客户端并发的元数据操作。
对于大集群或者有大量客户端的集群来说,通常需要增大参数dfs.namenode.handler.count的默认值10。

    dfs.namenode.handler.count
    10

2)YARN参数调优yarn-site.yml

电商数仓学习笔记(一)_第19张图片

Zookeeper安装

1)解压安装

解压安装包到/opt/module/目录下
[software] tar -zxvf zookeeper-3.5.7.tar.gz -C /opt/module/
修改名字
[module]  mv apache-zookeeper-3.5.7-bin/ zookeeper-3.5.7
同步到hadoop103、hadoop104
[module] xsynczookeeper-3.5.7/

2)配置服务器编号

(1)在/opt/module/zookeeper-3.5.7/目录下创建zkData

mkdir zkData

(2)在zkData目录下创建一个myid的文件

vi myid

在文件中添加server对应的编号:2

(3)拷贝配置好的zookeeper到其他机器上

xsync myid

将内容分别修改为3、4

4)配置zoo.cfg文件

(1)重命名/opt/module/zookeeper-3.5.7/conf这个目录下的zoo_sample.cfg为zoo.cfg

mv zoo_sample.cfg zoo.cfg

(2)打开zoo.cfg文件,修改数据存储路径

 vim zoo.cfg
 dataDir=/opt/module/zookeeper-3.5.7/zkData
 增加如下配置
 #######################cluster##########################
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:3888
server.4=hadoop104:2888:3888

(3)同步zoo.cfg配置文件

xsync zoo.cfg

(4)配置参数解读

server.A=B:C:D。

电商数仓学习笔记(一)_第20张图片

5)集群操作

hadoop102,103,104分别启动Zookeeper

[zookeeper-3.5.7] bin/zkServer.sh start

 

集群规划

ZK集群启停脚本

(1)在hadoop102的/home/admin/bin目录下创建脚本

vim zk.sh

脚本内容

#!/bin/bash
​
case $1 in
"start"){
    for i in hadoop102 hadoop103 hadoop104
    do
        echo ---------- zookeeper $i 启动 ------------
        ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh start"
    done
};;
"stop"){
    for i in hadoop102 hadoop103 hadoop104
    do
        echo ---------- zookeeper $i 停止 ------------    
        ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh stop"
    done
};;
"status"){
    for i in hadoop102 hadoop103 hadoop104
    do
        echo ---------- zookeeper $i 状态 ------------    
        ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh status"
    done
};;
esac

(2)

增加脚本权限
chmod u+x zk.sh
Zookeeper集群启动脚本
zk.sh start
Zookeeper集群停止脚本
zk.sh stop

Kafka安装

电商数仓学习笔记(一)_第21张图片

 

Kafka集群安装

安装部署

1)解压 修改文件名 创建logs文件夹

[software] tar -zxvf kafka_2.11-2.4.1.tgz -C /opt/module/
[module]  mv kafka_2.11-2.4.1/ kafka
​
/opt/module/kafka 目录下创建logs文件夹
mkdir logs

2)修改配置文件

cd config/
vi server.properties
修改或添加以下内容
#broker的全局唯一编号,不能重复
broker.id=0
#删除topic功能使能
delete.topic.enable=true
#kafka运行日志存放的路径
log.dirs=/opt/module/kafka/data
#配置连接Zookeeper集群地址
zookeeper.connect=hadoop102:2181,hadoop103:2181,hadoop104:2181/kafka

电商数仓学习笔记(一)_第22张图片

 

 

 

3)配置环境变量

sudo vim /etc/profile.d/my_env.sh
​
#KAFKA_HOME
export KAFKA_HOME=/opt/module/kafka
export PATH=$PATH:$KAFKA_HOME/bin
​
source /etc/profile.d/my_env.sh

4)分发安装包(分发完记得配置其他机器的环境变量)

xsync kafka/

5)分别在hadoop103和hadoop104上修改配置文件/opt/module/kafka/config/server.properties中的broker.id=1、broker.id=2

6)启动集群

bin/kafka-server-start.sh -daemon /opt/module/kafka/config/server.properties

7)关闭集群

 bin/kafka-server-stop.sh

集群规划

 

Kafka集群启动停止脚本

(1)在/home/admin/bin目录下创建脚本kf.sh

vim kf.sh

脚本内容:

#! /bin/bash
​
case $1 in
"start"){
    for i in hadoop102 hadoop103 hadoop104
    do
        echo " --------启动 $i Kafka-------"
        ssh $i "/opt/module/kafka/bin/kafka-server-start.sh -daemon /opt/module/kafka/config/server.properties"
    done
};;
"stop"){
    for i in hadoop102 hadoop103 hadoop104
    do
        echo " --------停止 $i Kafka-------"
        ssh $i "/opt/module/kafka/bin/kafka-server-stop.sh stop"
    done
};;
esac

(2)增加脚本执行权限

chmod u+x kf.sh

(3)kf集群启动和停止

kf.sh start
kf.sh stop

Kafka常用命令

1)查看Kafka Topic列表

 bin/kafka-topics.sh --zookeeper hadoop102:2181/kafka --list

2)创建Kafka Topic

进入到/opt/module/kafka/目录下创建日志主题

bin/kafka-topics.sh --zookeeper hadoop102:2181,hadoop103:2181,hadoop104:2181/kafka  --create --replication-factor 1 --partitions 1 --topic topic_log

3)删除Kafka Topic

bin/kafka-topics.sh --delete --zookeeper hadoop102:2181,hadoop103:2181,hadoop104:2181/kafka --topic topic_log

4)Kafka生产消息

bin/kafka-console-producer.sh \
--broker-list hadoop102:9092 --topic topic_log

5)Kafka消费消息

bin/kafka-console-consumer.sh \
--bootstrap-server hadoop102:9092 --from-beginning --topic topic_log

6)查看Kafka Topic详情

bin/kafka-topics.sh --zookeeper hadoop102:2181/kafka \
--describe --topic topic_log

 

Kafka机器数量计算

Kafka机器数量(经验公式)= 2 *(峰值生产速度 * 副本数 / 100)+ 1

先拿到峰值生产速度,再根据设定的副本数,就能预估出需要部署Kafka的数量

电商数仓学习笔记(一)_第23张图片

 

Kafka分区数计算

电商数仓学习笔记(一)_第24张图片

 

采集日志Flume

电商数仓学习笔记(一)_第25张图片

 

日志采集Flume安装

解压到/opt/module 名字改为flume

tar -zxvf apache-flume-1.9.0-bin.tar.gz -C /opt/module
cd /opt/module
mv apache-flume-1.9.0-bin.tar.gz flume

删除guava-11.0.2.jar以兼容hadoop 3.1.3

rm /opt/module/flume/lib/guava-11.0.2.jar

将flume/conf下的flume-env.sh.template文件修改为flume-env.sh 并配置flume-env.sh文件

mv flume-env.sh.template flume-env.sh
​
vi flume-env.sh
插入以下内容
exprot JAVA_HOME=/opt/module/jdk1.8.0_212

集群规划

 

Flume组件选型

1)Source

(1)Taildir Source相比Exec Source、Spooling Directory Source的优势

 

(2)batchSize大小如何设置

Event 1K左右时,500-1000合适(默认100)

2)Channel

 

日志采集Flume配置

1)Flume配置分析

电商数仓学习笔记(一)_第26张图片

 

Flume直接读log日志的数据,log日志的格式是app.yyyy-mm-dd.log

2)Flume具体配置如下:

在/opt/module/flume/conf目录下创建file-flume-kafka.conf文件

vim file-flume-kafka.conf

配置内容:

#为各组件命名
a1.sources = r1
a1.channels = c1
​
#描述source
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups = f1
a1.sources.r1.filegroups.f1 = /opt/module/applog/log/app.*
a1.sources.r1.positionFile = /opt/module/flume/taildir_position.json
a1.sources.r1.interceptors =  i1
a1.sources.r1.interceptors.i1.type = com.admin.flume.interceptor.ETLInterceptor$Builder
​
#描述channel
a1.channels.c1.type = org.apache.flume.channel.kafka.KafkaChannel
a1.channels.c1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092
a1.channels.c1.kafka.topic = topic_log
a1.channels.c1.parseAsFlumeEvent = false
​
#绑定source和channel以及sink和channel的关系
a1.sources.r1.channels = c1

注:com.admin.flume.interceptor.ETLInterceptor$Builder是自定义的拦截器的全类名。需要根据用户自定义的拦截器做相应修改

Flume拦截器

1)创建Maven工程flume-interceptor

2)创建包名:com.admin.flume.interceptor

是自定义的拦截器的全类名。需要根据用户自定义的拦截器做相应修改

电商数仓学习笔记(一)_第27张图片

 

3)在pom.xml文件中添加如下配置


    
        org.apache.flume
        flume-ng-core
        1.9.0
        provided
    
​
    
        com.alibaba
        fastjson
        1.2.62
    

    
        
            maven-compiler-plugin
            2.3.2
            
                1.8
                1.8
            
        
        
            maven-assembly-plugin
            
                
                    jar-with-dependencies
                
            
            
                
                    make-assembly
                    package
                    
                        single
                    
                
            
        
    

注:scope中provided表示编译时使用该jar包,打包时不用。因为集群上已经有flume的jar包,只是在编译时用一下

4)在com.admin.flume.interceptor包下创建JSONUtils类

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
​
public class JSONUtils {
    public static boolean isJSONValidate(String log){
        try {
            JSON.parse(log);
            return true;
        }catch (JSONException e){
            return false;
        }
    }
}

5)在com.admin.flume.interceptor包下创建LogInterceptor类

import com.alibaba.fastjson.JSON;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
​
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
​
public class ETLInterceptor implements Interceptor {
​
    @Override
    public void initialize() {
​
    }
​
    @Override
    public Event intercept(Event event) {
​
        byte[] body = event.getBody();
        String log = new String(body, StandardCharsets.UTF_8);
​
        if (JSONUtils.isJSONValidate(log)) {
            return event;
        } else {
            return null;
        }
    }
​
    @Override
    public List intercept(List list) {
​
        Iterator iterator = list.iterator();
​
        while (iterator.hasNext()){
            Event next = iterator.next();
            if(intercept(next)==null){
                iterator.remove();
            }
        }
​
        return list;
    }
​
    public static class Builder implements Interceptor.Builder{
​
        @Override
        public Interceptor build() {
            return new ETLInterceptor();
        }
        @Override
        public void configure(Context context) {
​
        }
​
    }
​
    @Override
    public void close() {
​
    }
}

6)打包

 

7)先将打好的包放到hadoop102的/opt/module/flume/lib文件夹下

8)分发Flume到hadoop103、hadoop104

xsync flume/

9)分别在hadoop102、hadoop103上启动Flume

bin/flume-ng agent --name a1 --conf-file conf/file-flume-kafka.conf &

测试Flume-Kafka通道

(1)生成日志

lg.sh

(2)消费Kafka数据,观察控制台是否有数据获取到

[/opt/module/kafka] bin/kafka-console-consumer.sh \
--bootstrap-server hadoop102:9092 --from-beginning --topic topic_log

日志采集Flume启动停止脚本

(1)在/home/admin/bin目录下创建脚本f1.sh

vim f1.sh

脚本内容

#! /bin/bash
​
case $1 in
"start"){
        for i in hadoop102 hadoop103
        do
                echo " --------启动 $i 采集flume-------"
                ssh $i "nohup /opt/module/flume/bin/flume-ng agent --conf-file /opt/module/flume/conf/file-flume-kafka.conf --name a1 -Dflume.root.logger=INFO,LOGFILE >/opt/module/flume/log1.txt 2>&1  &"
        done
};; 
"stop"){
        for i in hadoop102 hadoop103
        do
                echo " --------停止 $i 采集flume-------"
                ssh $i "ps -ef | grep file-flume-kafka | grep -v grep |awk  '{print \$2}' | xargs -n1 kill -9 "
        done
​
};;
esac

说明:

  • nohup,该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。nohup就是不挂起的意思,不挂断地运行命令

  • awk 默认分隔符为空格

  • $2是在“”双引号内部会被解析为脚本的第二个参数,但是这里面想表达的含义是awk的第二个值,所以需要将他转义,用$2表示

  • xargs 表示取出前面命令运行的结果,作为后面命令的输入参数

(2)增加脚本权限 测试启动停止

chmod u+x f1.sh
​
f1.sh start
f1.sh stop

消费Kafka数据Flume

Flume组件选型

1)FileChannel和MemoryChannel区别

电商数仓学习笔记(一)_第28张图片

 

2)FileChannel优化

通过配置dataDirs指向多个路径,每个路径对应不同的硬盘,增大Flume吞吐量。

checkpointDir和backupCheckpointDir也尽量配置在不同硬盘对应的目录中,保证checkpoint坏掉后,可以快速使用backupCheckpointDir恢复数据。

电商数仓学习笔记(一)_第29张图片

 

3)Sink:HDFS Sink

(1)HDFS存入大量小文件,有什么影响?

元数据层面:每个小文件都有一份元数据,其中包括文件路径,文件名,所有者,所属组,权限,创建时间等。所以小文件过多,会占用NameNode服务器大量内存,影响Namenode性能和使用寿命

计算层面:默认情况下MR会对每一个小文件启用一个Map任务计算,非常影响计算性能。同时也影响磁盘寻址时间

(2)HDFS小文件处理

电商数仓学习笔记(一)_第30张图片

 

消费者Flume配置

1)Flume配置分析

电商数仓学习笔记(一)_第31张图片

 

2)Flume的具体配置如下:

在hadoop104的/opt/module/flume/conf目录下创建kafka-flume-hdfs.conf文件

vim kafka-flume-hdfs.conf

配置内容如下:

## 组件
a1.sources=r1
a1.channels=c1
a1.sinks=k1
​
## source1
a1.sources.r1.type = org.apache.flume.source.kafka.KafkaSource
a1.sources.r1.batchSize = 5000
a1.sources.r1.batchDurationMillis = 2000
a1.sources.r1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092,hadoop104:9092
a1.sources.r1.kafka.topics=topic_log
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = com.atguigu.flume.interceptor.TimeStampInterceptor$Builder
​
## channel1
a1.channels.c1.type = file
a1.channels.c1.checkpointDir = /opt/module/flume/checkpoint/behavior1
a1.channels.c1.dataDirs = /opt/module/flume/data/behavior1/
​
​
## sink1
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = /origin_data/gmall/log/topic_log/%Y-%m-%d
a1.sinks.k1.hdfs.filePrefix = log-
a1.sinks.k1.hdfs.round = false
​
#控制生成的小文件
a1.sinks.k1.hdfs.rollInterval = 10
a1.sinks.k1.hdfs.rollSize = 134217728
a1.sinks.k1.hdfs.rollCount = 0
​
## 控制输出文件是原生文件。
a1.sinks.k1.hdfs.fileType = CompressedStream
a1.sinks.k1.hdfs.codeC = lzop
​
## 拼装
a1.sources.r1.channels = c1
a1.sinks.k1.channel= c1

Flume时间拦截器

由于Flume默认会用Linux系统时间,作为输出到HDFS路径的时间。如果数据是23:59分产生的。Flume消费Kafka里面的数据时,有可能已经是第二天了,那么这部分数据会被发往第二天的HDFS路径。我们希望的是根据日志里面的实际时间,发往HDFS的路径,所以下面拦截器作用是获取日志中的实际时间。

解决的思路:拦截json日志,通过fastjson框架解析json,获取实际时间ts。将获取的ts时间写入拦截器header头,header的key必须是timestamp,因为Flume框架会根据这个key的值识别为时间,写入到HDFS。

1)在com.admin.flume.interceptor包下创建TimeStampInterceptor类

import com.alibaba.fastjson.JSONObject;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
​
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
​
public class TimeStampInterceptor implements Interceptor {
​
    private ArrayList events = new ArrayList<>();
​
    @Override
    public void initialize() {
​
    }
​
    @Override
    public Event intercept(Event event) {
​
        Map headers = event.getHeaders();
        String log = new String(event.getBody(), StandardCharsets.UTF_8);
​
        JSONObject jsonObject = JSONObject.parseObject(log);
​
        String ts = jsonObject.getString("ts");
        headers.put("timestamp", ts);
​
        return event;
    }
​
    @Override
    public List intercept(List list) {
        events.clear();
        for (Event event : list) {
            events.add(intercept(event));
        }
​
        return events;
    }
​
    @Override
    public void close() {
​
    }
​
    public static class Builder implements Interceptor.Builder {
        @Override
        public Interceptor build() {
            return new TimeStampInterceptor();
        }
​
        @Override
        public void configure(Context context) {
        }
    }
}

2)重新打包

3)将jar包放到Hadoop102的/opt/module/flume/lib文件夹下

4)分发到hadoop103和hadoop104

xsync flume/

消费者Flume启动停止脚本

(1)在/home/admin/bin目录下创建脚本f2.sh

vim f2.sh

脚本内容:

#! /bin/bash
​
case $1 in
"start"){
        for i in hadoop104
        do
                echo " --------启动 $i 消费flume-------"
                ssh $i "nohup /opt/module/flume/bin/flume-ng agent --conf-file /opt/module/flume/conf/kafka-flume-hdfs.conf --name a1 -Dflume.root.logger=INFO,LOGFILE >/opt/module/flume/log2.txt   2>&1 &"
        done
};;
"stop"){
        for i in hadoop104
        do
                echo " --------停止 $i 消费flume-------"
                ssh $i "ps -ef | grep kafka-flume-hdfs | grep -v grep |awk '{print \$2}' | xargs -n1 kill"
        done
​
};;
esac

(2)增加脚本执行权限

chmod u+x f2.sh

(3)f2集群启动停止

f2.sh start
f2.sh stop

Flume内存优化

1)问题描述:如果启动消费Flume抛出如下异常

ERROR hdfs.HDFSEventSink: process failed
java.lang.OutOfMemoryError: GC overhead limit exceeded

2)解决方案

(1)在hadoop102服务器/opt/module/flume/confflume-env.sh文件中增加如下配置

export JAVA_OPTS="-Xms100m -Xmx2000m -Dcom.sun.management.jmxremote"

(2)同步到hadoop103、hadoop104

3)Flume内存参数设置及优化

采集通道启动/停止脚本

/home/admin/bin创建脚本cluster.sh

vim cluster.sh

 

脚本内容:

#!/bin/bash
​
case $1 in
"start"){
        echo ================== 启动 集群 ==================
​
        #启动 Zookeeper集群
        zk.sh start
​
        #启动 Hadoop集群
        hdp.sh start
​
        #启动 Kafka采集集群
        kf.sh start
​
        #启动 Flume采集集群
        f1.sh start
​
        #启动 Flume消费集群
        f2.sh start
​
        };;
"stop"){
        echo ================== 停止 集群 ==================
​
        #停止 Flume消费集群
        f2.sh stop
​
        #停止 Flume采集集群
        f1.sh stop
​
        #停止 Kafka采集集群
        kf.sh stop
​
        #停止 Hadoop集群
        hdp.sh stop
​
        #停止 Zookeeper集群
        zk.sh stop
​
};;
esac

(2)增加脚本执行权限 启动 停止

chmod u+x cluster.sh
cluster.sh start
cluster.sh stop

kafka如果无法关闭可以在hdp.sh stop后sleep 几秒

电商数仓学习笔记(一)_第32张图片

 

常见问题及解决方案

访问2NN页面hadoop104:9868看不到详细信息

电商数仓学习笔记(一)_第33张图片

(1)F12在控制台查看错误信息

电商数仓学习笔记(一)_第34张图片

(2)找到要修改的文件

cd /opt/module/hadoop-3.1.3/share/hadoop/hdfs/webapps/static
vim dfs-dust.js
​
修改第61行
return new Date(Number(v)).toLocaleString();

 电商数仓学习笔记(一)_第35张图片

 

你可能感兴趣的:(大数据,hadoop)