day09_实时类标签/指标

文章目录

  • day09_实时类标签/指标
  • 一、日志数据实时采集
    • 2、Flume简介
      • 2.3 项目日志数据采集Flume配置
        • 2.3.1 涉及的Flume组件和参数
        • 2.3.2 Nginx日志采集
        • 2.3.3 用户行为日志采集
  • 二、Nginx日志数据统计
    • 1、日志格式说明
    • 2、数据ETL
      • 2.1 日志抽取
        • 2.1.1 正则表达式
        • 2.1.2 基于Spark实现Nginx数据匹配
      • 2.2 字段解析
        • 2.2.1 日期格式转换
        • 2.2.2 IP解析地理位置(了解)
        • 2.2.3 UA解析
      • 2.3 完整代码
      • 2.4 使用Hive读取HDFS数据
    • 3、指标统计
    • 1、尝试进行用户行为日志的数据ETL、指标统计

day09_实时类标签/指标

在这里插入图片描述
在这里插入图片描述

一、日志数据实时采集

2、Flume简介

2.3 项目日志数据采集Flume配置

zookeeper、Kafka的启动命令

启动zookeeper(没有启动的,才需要执行)
/export/server/zookeeper/bin/zkServer.sh start启动Kafka
cd /export/server/kafka/bin
nohup ./kafka-server-start.sh ../config/server.sql 2>&1 &Kafka其他的相关命令
cd /export/server/kafka/bin
查看当前集群有哪些Topic
./kafka-topics.sh --list --bootstrap-server up01:9092
新建Topic(分区数没要求,副本数<=broker节点个数)
./kafka-topics.sh --create --bootstrap-server up01:9092 --topic xtzg_nginx_log
参看Topic的详细信息
./kafka-topics.sh --describe --bootstrap-server up01:9092 --topic xtzg_nginx_log注意: 要提前创建好Kafka的Topic
2.3.1 涉及的Flume组件和参数
  • source
type: 类型,固定值TAILDIR。能同时监控一个目录或者多个文件,也能动态监控每个文件的变化,还支持断点续传,不会出现重复消费问题。
fiilegroups:  以空格分隔的文件组列表。每个文件组表示一组要跟踪的文件。
filegroups.<filegroupName>: 文件组的绝对路径。正则表达式(而不是文件系统模式)只能用于文件名。
positionFile: JSON格式的文件,记录每个文件的inode、绝对路径和最后位置。注意: type的TAILDIR大小写不能随便写
  • channel
type: 类型,固定值 org.apache.flume.channel.kafka.KafkaChannel
kafka.bootstrap.servers: Kafka集群中的broker列表。格式:hostname:port,多个用逗号隔开。
kafka.topic: channel要用的topic
parseAsFlumeEvent: 是否需要对采集到的数据解析为Event对象,然后在内容前面增加topic前缀,会导致后续的内容会有部分缺失的情况。一般是false

补充:

如果采集到的数据最终想要输出到Kafka中,可以直接选择使用Kafka Channel。
注意: Kafka Channel和Kafka Sink,虽然都是将数据输出到Kafka中,但是两者的配置参数有区别
2.3.2 Nginx日志采集

在这里插入图片描述

  • 创建nginx_to_kafka.conf文件

在这里插入图片描述

  • nginx_to_kafka.conf配置文件内容如下
#定义组件
a1.sources = r1
a1.channels = c1#配置source
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups = f1
a1.sources.r1.filegroups.f1 = /export/data/workspace/user_profile/log_generate/datacollection/source_data/access-nginx.*
a1.sources.r1.positionFile = /export/data/flume/nginx_position.json#配置channel
a1.channels.c1.type = org.apache.flume.channel.kafka.KafkaChannel
a1.channels.c1.kafka.bootstrap.servers = up01:9092
a1.channels.c1.kafka.topic = xtzg_nginx_log
a1.channels.c1.parseAsFlumeEvent = false#组装 
a1.sources.r1.channels = c1注意: 1- a1.sources.r1.filegroups.f1该参数值要改成你自己的路径2- 文件的模糊匹配的正则表达式中写的是.*表示匹配任意内容
将上面的配置文件复制到/export/server/flume/conf
cp /export/data/workspace/user_profile/scripts/flume/nginx_to_kafka.conf /export/server/flume/conf
  • 在Kafka上创建topic(前提开启zk,kafka)
cd /export/server/kafka/bin./kafka-topics.sh --create --bootstrap-server up01:9092 --topic xtzg_nginx_log
  • 启动Flume
cd /export/server/flumebin/flume-ng agent -n a1 -c conf/ -f conf/nginx_to_kafka.conf
  • 查看Kafka中的数据
cd /export/server/kafka/bin./kafka-console-consumer.sh --bootstrap-server up01:9092 --topic xtzg_nginx_log
  • 启动

运行python中的NginxLogSimulationData.py。查看kafka中数据变化,如果看到新增数据则配置成功。确认无误后关停Flume采集任务。

2.3.3 用户行为日志采集
  • 创建user_event_to_kafka.conf文件
    在这里插入图片描述

  • user_event_to_kafka.conf配置文件内容如下

#定义组件
a1.sources = r1
a1.channels = c1#配置source
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups = f1
a1.sources.r1.filegroups.f1 = /export/data/workspace/user_profile/log_generate/datacollection/source_data/user-event.*
a1.sources.r1.positionFile = /export/data/flume/user_event_position.json#配置channel
a1.channels.c1.type = org.apache.flume.channel.kafka.KafkaChannel
a1.channels.c1.kafka.bootstrap.servers = up01:9092
a1.channels.c1.kafka.topic = xtzg_user_event
a1.channels.c1.parseAsFlumeEvent = false#组装 
a1.sources.r1.channels = c1
  • 在Kafka上创建topic(前提开启zk,kafka)
cd /export/server/kafkabin/kafka-topics.sh --create --bootstrap-server up01:9092 --topic xtzg_user_event --partitions 1 --replication-factor 1
  • 启动Flume
cd /export/server/flumebin/flume-ng agent -n a1 -c conf/ -f conf/user_event_to_kafka.conf
  • 查看Kafka中的数据
cd /export/server/kafkabin/kafka-console-consumer.sh --bootstrap-server up01:9092 --from-beginning --topic xtzg_user_event
  • 启动

运行python中的EventSimulationJsonData.py。查看kafka中数据变化,如果看到新增数据则配置成功。确认无误后关停Flume采集任务。

二、Nginx日志数据统计

1、日志格式说明

​ Nginx(发音 恩几可使)是异步框架的网页服务器,也可以用作反向代理、负载平衡器和HTTP缓存。该软件由俄罗斯程序员伊戈尔·赛索耶夫(Игорь Сысоев)开发并于2004年首次公开发布

  • Nginx日志包含access_logerror_log两种类型日志数据。项目中分析的数据为:access_log
  • Nginx开源官网:https://nginx.org/
  • 项目采集Nginx数据格式。以下为一条Nginx日志:
116.85.48.25 - - [12/Nov/2024:11:36:46 +0800] "GET /login.html HTTP/1.1" 404 729 "https://xtx.itcast.cn/referAFriend.html" "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/7.0.18(0x17001233) NetType/WIFI Language/zh_CN" "-"Nginx日志格式说明:116.85.48.25: 用户访问IP地址- - : 用户标识(cookie信息)[14/Jul/2022:17:40:41 +0800]:  访问时间 + 时区GET : 请求方式/css/40.30d6d2b.css: 请求资源HTTP/1.1 : 请求的协议500 : 请求的状态码 (500 服务器错误,  200 成功  302 重定向  404 访问到未知资源)951 : 响应返回的字节大小"https://www.htv.com/official/component?WT.mc_id=3" : 来源的URL(从那个地方跳转到此页面)"Mozilla/5......:  浏览器标识

2、数据ETL

2.1 日志抽取

2.1.1 正则表达式
Java版本:
(?<ip>\d+\.\d+\.\d+\.\d+) (- - \[)(?<datetime>[\s\S]+)(?<t1>\][\s"]+)(?<request>[A-Z]+) (?<url>[\S]*) (?<protocol>[\S]+)["] (?<code>\d+) (?<sendbytes>\d+) ["](?<refferer>[\S]*) ["](?<useragent>[\S\s]+)["] ["](?<proxyaddr>[\S\s]+)["]Python版本:
(?P<ip>.*?) - - \[(?P<time>.*?)\] "(?P<request>.*?)" (?P<status>.*?) (?P<bytes>.*?) "(?P<referer>.*?)" "(?P<ua>.*?)" "(?P<proxy_address>.*)"
2.1.2 基于Spark实现Nginx数据匹配

代码实现:

from pyspark.sql import SparkSession
import os
import pyspark.sql.functions as F
from pyspark.sql.types import StringTypeos.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'if __name__ == '__main__':# 1- 创建SparkSession对象spark = SparkSession.builder\.appName("nginx_etl")\.master("local[*]")\.config("spark.sql.shuffle.partitions",2)\.getOrCreate()# 2- 数据输入:读取Kafka中的数据""""startingOffsets","earliest":该配置,在实际工作中一般不需要配置。这里是为了开发代码方便"""init_df = spark.readStream.format("kafka")\.option("kafka.bootstrap.servers","192.168.88.166:9092")\.option("subscribe","xtzg_nginx_log")\.option("startingOffsets","earliest")\.load()# 结构化流中不能以show()方式打印数据数据内容# init_df.show()# 3- 数据ETL处理# 3.1- value字段解码的操作"""cast(StringType()):将字段数据类型强制转换为字符串。等同于SQL语句中的cast(value as string)下面两种方式都可以,推荐使用第一种,因为性能更好"""# type_cast_df = init_df.select(init_df.value.cast(StringType()).alias("value"))type_cast_df = init_df.selectExpr("cast(value as string) as value")# 3.2- 通过正则表达式提取Nginx的字段pattern = '(?<ip>\d+\.\d+\.\d+\.\d+) (- - \[)(?<datetime>[\s\S]+)(?<t1>\][\s"]+)(?<request>[A-Z]+) (?<url>[\S]*) (?<protocol>[\S]+)["] (?<code>\d+) (?<sendbytes>\d+) ["](?<refferer>[\S]*) ["](?<useragent>[\S\s]+)["] ["](?<proxyaddr>[\S\s]+)["]'regexp_df = type_cast_df.select(F.regexp_extract("value",pattern,1).alias("ip"),F.regexp_extract("value",pattern,3).alias("datetime"),F.regexp_extract("value",pattern,4).alias("t1"),F.regexp_extract("value",pattern,5).alias("request"),F.regexp_extract("value",pattern,6).alias("url"),F.regexp_extract("value",pattern,7).alias("protocol"),F.regexp_extract("value",pattern,8).alias("code"),F.regexp_extract("value",pattern,9).alias("sendbytes"),F.regexp_extract("value",pattern,10).alias("refferer"),F.regexp_extract("value",pattern,11).alias("useragent"),F.regexp_extract("value",pattern,12).alias("proxyaddr"))# 4- 数据输出,启动流式任务regexp_df.writeStream.format("console").outputMode("append").start().awaitTermination()

运行结果截图:
在这里插入图片描述

可能遇到的错误:
在这里插入图片描述

原因: regexp_extract函数只能传递Java版的正则表达式,不能用Python的

2.2 字段解析

需求:根据nginx日志,ip标识唯一的用户,需要ip分组,统计得到用户访问的pv、uv、区域、状态码、终端设备的操作系统、设备品牌、浏览器、访问时间(年-月-日 时:分:秒)

2.2.1 日期格式转换

Python的datetime函数库

  • 相关函数:
    • strftime(): 把日期对象转成指定的时间格式的字符串
    • strptime(): 把指定格式的日期字符串转换为日期对象
  • 参考文档: https://docs.python.org/zh-cn/3/library/datetime.html#strftime-strptime-behavior
  • 解析格式: %d/%b/%Y:%H:%M:%S %z => %Y-%m-%d %H:%M:%S
    • 28/Jul/2022:16:22:07 +0800 => 日期对象 => 2022-07-28 16:22:07
  • 测试代码

Python方式

from datetime import datetimeif __name__ == '__main__':date_str = "11/Feb/2025:14:34:49 +0800"print(datetime.strptime(date_str, "%d/%b/%Y:%H:%M:%S %z").strftime("%Y-%m-%d %H:%M:%S"))

SparkSQL方式(重点掌握)

regexp_df.withColumn("datetime",F.from_unixtime(F.unix_timestamp("datetime","dd/MMM/yyyy:HH:mm:ss Z"),"yyyy-MM-dd HH:mm:ss"))

在这里插入图片描述

2.2.2 IP解析地理位置(了解)

根据IP解析地理位置

  • 方式一: 使用ip解析地理位置API
    • ip地址:http://opendata.baidu.com/api.php?query=117.136.12.79&co=&resource_id=6006&oe=utf8
    • 像百度地图开发平台 / 高德地图开放平台 … 都会提供IP解析的服务接口
    • 百度地图:https://lbs.baidu.com/faq/api?title=webapi/ip-api-base
    • 高德地图:https://lbs.amap.com/api/webservice/guide/api/ipconfig
    • 其他平台:https://www.nowapi.com/
  • 方式二: (了解)使用geo_ip依赖包和GeoLite2-City.mmdb库
    • 依赖包:geoip2~=4.5.0
    • 下载地址:https://gitcode.com/crownp/geolite2_demo/blob/master/src/main/resources/GeoLite2-City.mmdb
  • IP在线解析测试代码

Python的Requests库的介绍:https://requests.readthedocs.io/en/latest/

#!/usr/bin/env python
# @desc : 
__coding__ = "utf-8"
__author__ = "bytedance"import requestsdef parse_ip(ip_str):params = {"query": ip_str,"co": "","resource_id": "6006","oe": "utf8",}# 发送请求response = requests.get(url="https://opendata.baidu.com/api.php", params=params)# 解析响应内容result = response.json()status = result['status']if status == '0':# 正常try:return result['data'][0]['location'].split(" ")[0]except:return "未知区域"else:return "未知区域"if __name__ == '__main__':ip_str = "127.0.0.1"ip_str = "10.254.1.97"ip_str = "157.148.69.76"area = parse_ip(ip_str)print(area)
2.2.3 UA解析

UA说明

  • UA为useragent简称,特指用户访问系统使用的客户端信息,一般包含操作系统,浏览器,设备品牌信息等
  • UA字符串信息:http://useragentstring.com/
  • 使用,需导入UA解析依赖包:from user_agents import parse
  • UA的作用
    • 1.客户端识别:通过User-Agent,服务器能够识别客户端的类型和版本,从而提供相应的内容和服务。比如,在移动设备上展示适合屏幕大小的网页布局,或在不同浏览器上提供兼容性优化。
    • 2.统计分析:网站和应用开发者可以利用User-Agent来收集客户端的信息,进行用户行为分析和统计。这有助于了解用户使用的设备和偏好,以便进行产品和服务的改进。
    • 3.安全性:User-Agent也可以用于安全验证和防止恶意行为。通过分析User-Agent,服务器可以检测到异常或伪造的请求,并采取相应的安全措施。
  • 测试代码:
from user_agents import parseif __name__ == '__main__':ua_str = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 MQQBrowser/11.0.7 Mobile/15E148 Safari/604.1 QBWebViewUA/2 QBWebViewType/1 WKType/1"result = parse(ua_str)# os操作系统信息print("os----------")print(result.os.family)print(result.os.version)print(result.os.version_string)# brower浏览器信息print("browser----------")print(result.browser.family)print(result.browser.version)print(result.browser.version_string)# device设备信息print("device----------")print(result.device.family)print(result.device.model)

2.3 完整代码

需要将结果数据同时写入到Kafka和HDFS。清洗后的日志,可以用于其他业务分析,具有一定的价值。因为Kafka不能永久保存数据,所以需要把数据存储到HDFS一份。

因为每天都有很多日志,所以需要对日志进行分区。可以通过partitionBy()方法进行分区写入到HDFS。分区的字段需要进行计算。

另外,为了减少小文件生成,可以使用trigger来指定写入的时间间隔。

  • 先创建Kafka的Topic
cd /export/server/kafka/bin
./kafka-topics.sh --create --bootstrap-server up01:9092 --topic dwd_nginx_etl_result
  • 完整代码
from pyspark.sql import SparkSession
import os
import pyspark.sql.functions as F
from pyspark.sql.types import StringType, MapType
import requests
from user_agents import parseos.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'if __name__ == '__main__':# 1- 创建SparkSession对象spark = SparkSession.builder\.appName("nginx_etl")\.master("local[*]")\.config("spark.sql.shuffle.partitions",2)\.getOrCreate()# 配置checkpointLocation路径,推荐使用HDFS路径spark.conf.set("spark.sql.streaming.checkpointLocation", "hdfs://192.168.88.166:8020/xtzg/chk")# 2- 数据输入:读取Kafka中的数据""""startingOffsets","earliest":该配置,在实际工作中一般不需要配置。这里是为了开发代码方便"""init_df = spark.readStream.format("kafka")\.option("kafka.bootstrap.servers","192.168.88.166:9092")\.option("subscribe","xtzg_nginx_log")\.option("startingOffsets","earliest")\.load()# 结构化流中不能以show()方式打印数据数据内容# init_df.show()# 3- 数据ETL处理# 3.1- value字段解码的操作"""cast(StringType()):将字段数据类型强制转换为字符串。等同于SQL语句中的cast(value as string)下面两种方式都可以,推荐使用第一种,因为性能更好"""# type_cast_df = init_df.select(init_df.value.cast(StringType()).alias("value"))type_cast_df = init_df.selectExpr("cast(value as string) as value")# 3.2- 通过正则表达式提取Nginx的字段pattern = '(?<ip>\d+\.\d+\.\d+\.\d+) (- - \[)(?<datetime>[\s\S]+)(?<t1>\][\s"]+)(?<request>[A-Z]+) (?<url>[\S]*) (?<protocol>[\S]+)["] (?<code>\d+) (?<sendbytes>\d+) ["](?<refferer>[\S]*) ["](?<useragent>[\S\s]+)["] ["](?<proxyaddr>[\S\s]+)["]'# 这里不允许使用Python正则表达式,只能使用Java正则表达式# pattern = '(?P<ip>.*?) - - \[(?P<time>.*?)\] "(?P<request>.*?)" (?P<status>.*?) (?P<bytes>.*?) "(?P<referer>.*?)" "(?P<ua>.*?)" "(?P<proxy_address>.*)"'regexp_df = type_cast_df.select(F.regexp_extract("value",pattern,1).alias("ip"),F.regexp_extract("value",pattern,3).alias("datetime"),F.regexp_extract("value",pattern,4).alias("t1"),F.regexp_extract("value",pattern,5).alias("request"),F.regexp_extract("value",pattern,6).alias("url"),F.regexp_extract("value",pattern,7).alias("protocol"),F.regexp_extract("value",pattern,8).alias("code"),F.regexp_extract("value",pattern,9).alias("sendbytes"),F.regexp_extract("value",pattern,10).alias("refferer"),F.regexp_extract("value",pattern,11).alias("useragent"),F.regexp_extract("value",pattern,12).alias("proxyaddr"))# 3.3- 日期时间格式转换datetime_df = regexp_df.withColumn("datetime",F.from_unixtime(F.unix_timestamp("datetime","dd/MMM/yyyy:HH:mm:ss Z"),"yyyy-MM-dd HH:mm:ss"))# 3.4- IP地理位置解析@F.udf(returnType=StringType())def parse_ip(ip_str):params = {"query": ip_str,"co": "","resource_id": "6006","oe": "utf8",}# 发送请求response = requests.get(url="https://opendata.baidu.com/api.php", params=params)# 解析响应内容result = response.json()status = result['status']if status == '0':# 正常try:return result['data'][0]['location'].split(" ")[0]except:return "未知区域"else:return "未知区域"area_df = datetime_df.withColumn("area",parse_ip("ip"))# 3.5- UA解析"""为什么这里用户自定义函数推荐返回字典?方便后续取值"""@F.udf(returnType=MapType(keyType=StringType(),valueType=StringType()))def parse_ua(ua_str):result = parse(ua_str)os = result.os.familybrowser = result.browser.familydevice = result.device.modelreturn {"os":os,"browser":browser,"device":device}ua_df = area_df.withColumn("os",parse_ua("useragent")['os'])\.withColumn("browser", parse_ua("useragent")['browser'])\.withColumn("device", parse_ua("useragent")['device'])# 4- 数据输出,启动流式任务# 4.1- 输出到HDFS# 新增一个分区字段dt_df = ua_df.withColumn("dt",F.split("datetime"," ")[0])# partitionBy表示按照哪个字段进行分区dt_df.writeStream.format("orc").partitionBy("dt")\.option("path","hdfs://192.168.88.166:8020/xtzg/etl/dwd_nginx_etl_result")\.start()# 4.2- 输出到Kafka# 注意:一般将数据内容转换为JSON格式输出到Kafka中,为了后续使用方便# 注意:输出到Kafka中的字段名称只能叫valuekafka_df = ua_df.select(F.to_json(F.struct("ip","datetime","t1","request","url","protocol","code","sendbytes","refferer","useragent","proxyaddr","area","os","browser","device")).alias("value"))kafka_df.writeStream.format("kafka")\.option("kafka.bootstrap.servers","192.168.88.166:9092")\.option("topic","dwd_nginx_etl_result")\.start()# 4.3- 输出到控制台(为了测试)# awaitTermination()只能加在最后一个start()的后面dt_df.writeStream.format("console").outputMode("append").start().awaitTermination()

可能遇到的错误一:
在这里插入图片描述

原因: 结构化流中将数据输出到文件系统中,需要配置checkpointLocation

可能遇到的错误二:
在这里插入图片描述

原因: 输出到Kafka中的字段名称只能叫value

2.4 使用Hive读取HDFS数据

  • 创建表
CREATE external TABLE dwd.dwd_nginx_etl_result (ip string,`datetime` string,t1 string,request string,url string,protocol string,code string,sendbytes string,refferer string,useragent string,proxyaddr string,area string,os string,browser string,device string
)COMMENT 'nginx日志'PARTITIONED BY (dt string)STORED AS ORCLOCATION '/xtzg/etl/dwd_nginx_etl_result'TBLsql ('orc.compress' = 'SNAPPY')
;
  • 同步分区
MSCK REPAIR TABLE dwd.dwd_nginx_etl_result;

3、指标统计

  • 需求
统计实时请求总数(pv)
统计用户数(uv)
统计用户访问所在区域省(类似抖音的位置显示)
统计用户响应状态码
统计用户使用设备终端信息
统计用户操作系统信息
统计用户首次访问系统的时间
统计用户末次访问系统的时间ip: 用户访问系统的唯一地址
pv:访问系统的页面次数
uv:访问系统的用户数
area:访问系统用户来自的区域,根据ip解析出地址位置
status_code:访问系统用户请求http协议响应状态码
device_os:设备终端,从ua中提取手机或电脑的系统
device_brand:设备品牌名称,从ua中提取手机或电脑的品牌
browser_name:访问系统用户使用的浏览器名称
first_access_time:用户首次访问系统的时间
last_access_time:用户首次访问系统的时间
  • Doris建表语句

使用unique模型。

CREATE DATABASE IF NOT EXISTS log_analysis_db;
CREATE TABLE IF NOT EXISTS log_analysis_db.nginx_log_result
(ip varchar(15) comment 'ip地址',pv int comment 'pv数',uv int comment 'uv数',area varchar(50) comment '用户所在区域,根据ip解析',status_code varchar(10) comment '请求响应状态码',device_os varchar(50) comment '设备系统,从ua中提取手机或电脑使用的系统',device_brand varchar(50) comment ',从ua中提取手机或电脑的品牌',browser_name varchar(50) comment '电脑和手机,使用浏览器,记录浏览器简称',first_access_time datetime comment 'nginx日志记录首次访问时间',last_access_time datetime comment 'nginx日志记录末次访问时间'
)
UNIQUE KEY(ip)
DISTRIBUTED BY HASH(ip) BUCKETS 10
sql("replication_num" = "1");
  • 完整代码
from pyspark.sql import SparkSession, DataFrame
import os
import pyspark.sql.functions as F
from pyspark.sql.types import StringTypeos.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'if __name__ == '__main__':# 1- 创建SparkSession对象spark = SparkSession.builder \.appName("nginx_analysis") \.master("local[*]") \.config("spark.sql.shuffle.partitions", 2) \.getOrCreate()# 2- 数据输入:读取Kafka中的数据init_df = spark.readStream.format("kafka") \.option("kafka.bootstrap.servers", "192.168.88.166:9092") \.option("subscribe", "dwd_nginx_etl_result") \.option("startingOffsets", "earliest") \.load()# 3- 数据处理# 3.1- value字段类型转换type_cast_df = init_df.select(init_df.value.cast(StringType()).alias("value"))# 3.2- 从JSON中提取一个个字段"""json_tuple与get_json_object的区别get_json_object:优点:同时能够解析嵌套的JSON缺点:一次只能得到一个字段json_tuple:优点:一次能得到多个字段缺点:针对嵌套JSON,只能一层层解析"""parse_json_df = type_cast_df.select(F.json_tuple("value","ip","datetime","code","area","os","browser","device")\.alias("ip","datetime","status_code","area","device_os","browser_name","device_brand"))# 3.3- 指标统计# F.lit(1)生成一列,每行的数据内容一样,全都是1。与F.col函数作用类似# 因为类似area的这些字段的数据类型是字符串,聚合函数没有太适合的,因此使用firstresult_df = parse_json_df.groupBy("ip").agg(F.count("ip").alias("pv"),F.lit(1).alias("uv"),F.first("area").alias("area"),F.first("status_code").alias("status_code"),F.first("device_os").alias("device_os"),F.first("device_brand").alias("device_brand"),F.first("browser_name").alias("browser_name"),F.min("datetime").alias("first_access_time"),F.max("datetime").alias("last_access_time"))# 4- 数据输出# 4.1- 输出到Dorisdef write_2_doris(batch_df:DataFrame, batch_id):"""将DataFrame输出到Doris中:param batch_df: 有界的DataFrame:param batch_id: 批次ID:return:"""# 注意:一般先用append。如果明确知道要怎么做,那可以再使用overwritebatch_df.write.jdbc(url="jdbc:mysql://192.168.88.166:9030/log_analysis_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false",table="nginx_log_result",mode="append",sql={ 'user' : 'root', 'password' : '123456' })result_df.writeStream.foreachBatch(write_2_doris).outputMode("update").start()# 4.2- 输出到控制台result_df.writeStream.format("console").outputMode("update").start().awaitTermination()
  • 结果数据核对
./kafka-console-producer.sh --broker-list up01:9092 --topic dwd_nginx_etl_result
{"ip":"210.27.147.62","cookie":"- - [","datetime":"2024-11-14 11:11:11","t1":"] \"","request":"GET","url":"/search.html","protocol":"HTTP/1.1","code":"401","sendbytes":"58840","refferer":"https://www.douyin.com/goods-recommend/search.html?keyword=美味\"","useragent":"Mozilla/5.0 (Linux; U; Android 9; zh-CN; MI 9 Build/PKQ1.181121.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.1.6.1096 Mobile Safari/537.36","proxyaddr":"-","area":"广东省广州市","os":"Android","browser":"UC Browser","device":"XiaoMi MI 9","dt":"2024-11-12"}

1、尝试进行用户行为日志的数据ETL、指标统计

提示:核心是如何解析JSON格式,得到一个个独立的字段

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/17404.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot实战:高效获取视频资源

文章目录 前言技术实现SpringBoot项目构建产品选取配置数据采集 号外号外 前言 在短视频行业高速发展的背景下&#xff0c;海量内容数据日益增长&#xff0c;每天都有新的视频、评论、点赞、分享等数据涌现。如何高效、精准地获取并处理这些庞大的数据&#xff0c;已成为各大平…

位图,晶圆MAP 边缘算法

例如这样的一张图: 如果想要求外边缘点&#xff0c;即红色区域,首先遍历所有点位&#xff0c;求出每行每列X轴和Y轴的最大值MAX和最小值MIN。然后再次遍历每个点&#xff0c;判断该点的X值&#xff0c;Y值是否是最大值或者最小值&#xff0c;如果是&#xff0c;那么它就是外边…

【认证授权FAQ】SSL/TLS证书过期导致的CLS认证失败

问题现象 问题分析 属于Agent操作系统的根认证机构过期问题&#xff0c;需要下载CA然后在系统安装。 DigiCert根证书和中间证书将在未来几年过期&#xff0c;一旦证书过期&#xff0c;基于证书颁发的SSL/TLS证书将不再信任&#xff0c;导致网站无法HTTPs访问。需要迁移到新的根…

【安全测试】0基础新手学Web安全测试笔记(一)

文章目录 一、关于账号密码的漏洞二、关于验证码的漏洞三、Burp工具的使用四、渗透测试1. 渗透测试类型2. 脆弱性评估 五、常见的应用安全风险1. 注入2. 失效的身份认证3. 敏感数据泄露4. XML外部实体(XXE)5. 失效的访问控制6. 安全配置错误7. 跨站脚本:(XSS)8. 不安全的反序列…

旅游行业内容管理系统CMS提升网站建设效率与体验

内容概要 在如今快速发展的互联网时代&#xff0c;旅游行业对网站的要求越来越高&#xff0c;内容管理系统&#xff08;CMS&#xff09;的应用不可或缺。以 Baklib 为代表的先进CMS可显著提高旅游网站的建设效率与用户体验。为了满足不断变化的市场需求&#xff0c;这些系统通…

数据库安全、分布式数据库、反规范化等新技术(高软19)

系列文章目录 3.7数据库安全、分布式数据库、反规范化等新技术 前言 本节数据库安全、分布式数据库、反规范化等新技术相关概念与技术。 一、数据库 1.数据库安全 2.数据库备份 二、分布式数据库 1.数据库分布 2.数据仓库 3.数据仓库结构 4.商业智能&#xff08;BI&#xf…

【docker知识】快速找出服务器中占用内存较高的容器

本文由Markdown语法编辑器编辑完成。 1.背景&#xff1a; 近期在处理现场问题&#xff0c;观察服务器时&#xff0c;会遇到某些进程占用较高内存的情况。由于我们的服务&#xff0c;基本上都是以容器的方式在运行&#xff0c;因此就需要找到&#xff0c;到底是哪个容器&#…

【Android开发】华为手机安装包安装失败“应用是非正式版发布版本,当前设备不支持安装”问题解决

问题描述 我们将Debug版本的安装包发送到手机上安装&#xff0c;会发现华为手机有如下情况 解决办法 在文件gradle.properties中粘贴代码&#xff1a; android.injected.testOnlyfalse 最后点击“Sync now”&#xff0c;等待重新加载gradle资源即可 后面我们重新编译Debug安装…

docker 部署nginx,nginx 504

遇到问题 原因&#xff1a; 因为用的docker 部署nginx, docker 应用与服务之间的端口未开放&#xff0c;导致访问不到服务。

【数据结构】(8) 二叉树

一、树形结构 1、什么是树形结构 根节点没有前驱&#xff0c;其它节点只有一个前驱&#xff08;双亲/父结点&#xff09;。所有节点可以有 0 ~ 多个后继&#xff0c;即分支&#xff08;孩子结点&#xff09;。每个结点作为子树的根节点&#xff0c;这些子树互不相交。 2、关于…

牛客网-小美的加法(C++)

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 小美有一个长度为 n 的数组&#xff0c;她想将这个数组进行求和&#xff0c;即 suma1a2...an。 小美可以使用一次魔法&#xff08;也可以不使用&#xff09;&#xff0c;将其中一个加号…

瑞芯微开发板/主板Android调试串口配置为普通串口方法 深圳触觉智能科技分享

本文介绍瑞芯微开发板/主板Android调试串口配置为普通串口方法&#xff0c;不同板型找到对应文件修改&#xff0c;修改的方法相通。触觉智能RK3562开发板演示&#xff0c;搭载4核A53处理器&#xff0c;主频高达2.0GHz&#xff1b;内置独立1Tops算力NPU&#xff0c;可应用于物联…

MongoDB 7 分片副本集升级方案详解(下)

#作者&#xff1a;任少近 文章目录 1.4 分片升级1.5 升级shard11.6 升级shard2,shard31.7 升级mongos1.8重新启用负载均衡器1.9 推荐MongoDB Compass来验证数据 2 注意事项&#xff1a; 1.4 分片升级 使用“滚动”升级从 MongoDB 7.0 升级到 8.0&#xff0c;即在其他成员可用…

【redis】数据类型之bitmaps

Redis的Bitmaps是一种基于字符串的数据结构&#xff0c;用于处理位级别的操作。虽然Bitmaps在Redis中并不是一种独立的数据类型&#xff0c;而是基于字符串实现的&#xff0c;但它们提供了高效的位操作功能&#xff0c;适用于需要处理大量布尔值或二进制数据的场景。 基本概念…

mysql8.0使用MGR实现高可用与利用MySQL Router构建读写分离MGR集群

MGR是MySQL Group Replication的缩写&#xff0c;即MySQL组复制。 在以往&#xff0c;我们一般是利用MySQL的主从复制或半同步复制来提供高可用解决方案&#xff0c;但这存在以下几个比较严重的问题&#xff1a; 主从复制间容易发生复制延迟&#xff0c;尤其是在5.6以前的版本…

【IC】AI处理器核心--第二部分 用于处理 DNN 的硬件设计

第 II 部分 用于处理 DNN 的硬件设计 第 3 章 关键指标和设计目标 在过去的几年里&#xff0c;对 DNN 的高效处理进行了大量研究。因此&#xff0c;讨论在比较和评估不同设计和拟议技术的优缺点时应考虑的关键指标非常重要&#xff0c;这些指标应纳入设计考虑中。虽然效率通常…

Flutter Gradle 命令式插件正式移除,你迁移旧版 Gradle 配置了吗?

在 Flutter 3.29 版本里官方正式移除了 Flutter Gradle Apply 插件&#xff0c;其实该插件自 3.19 起已被弃用&#xff0c;同时 Flutter 团队后续也打算把 Flutter Gradle 从 Groovy 转换为 Kotlin&#xff0c;并将其迁移到使用 AGP&#xff08;Android Gradle Plugin&#xff…

C++类和对象进阶:运算符重载深度详解

C类和对象进阶&#xff1a;运算符重载 前言引入运算符重载定义语法注意事项重载为全局函数重载为成员函数运算符重载的本质 默认赋值运算符重载(默认成员函数)编译器自己生成的赋值运算符重载函数需要自己实现的场景总结默认赋值运算符重载 拷贝构造函数和赋值重载的区分验证 总…

three.js 使用geojson ,实现中国地图区域,边缘流动效果

three.js 使用geojson &#xff0c;实现中国地图区域&#xff0c;边缘流动效果 在线链接&#xff1a;https://threehub.cn/#/codeMirror?navigationThreeJS&classifyexpand&idgeoBorder 国内站点预览&#xff1a;http://threehub.cn github地址: https://github.co…

PortSwigger——WebSockets vulnerabilities

文章目录 一、WebSockets二、Lab: Manipulating WebSocket messages to exploit vulnerabilities三、Lab: Manipulating the WebSocket handshake to exploit vulnerabilities四、Using cross-site WebSockets to exploit vulnerabilities4.1 跨站WebSocket劫持&#xff08;cro…