8.28笔记
一、大数据计算场景主要分为两种
1.1 离线计算场景
数据产生之后,不是立马处理数据,而是先把数据存放起来,积攒到一定的程度之后统一的进行计算处理操作
适用于我们的数据或者需求对时间要求不高的场景下,要求一个小时、一天、一周、一个月…出一次结果
Hadoop技术、Spark Core、Spark SQL技术
1.2 实时计算场景
数据产生之后,需要立马处理数据,不能等待
适用于我们业务需求对时间要求很高的场景,要求几毫秒或者几百毫秒之间立马算出结果
Spark Streaming、Flink、Storm技术
Hadoop计算因为它的架构设计,因此只能做离线计算;Spark基于内存进行运算的,因此Spark技术既可以胜任离线计算,也可以去做实时计算(不精);Flink/storm技术专门为实时计算设计的
图计算、算法预测等等…
二、一般情况下大数据项目的开发流程
2.1 数据采集存储阶段
将需要使用大数据处理的数据,先使用数据采集技术将数据采集到大数据环境下进行持久化、海量化的保存。
2.2 数据清洗预处理阶段
采集存储的数据并不是都是有价值的数据(价值密度低),可能存在很多的错误的、缺失的、异常的数据,数据需要清洗预处理,清洗的目的是把无用的数据过滤掉,预处理的目的是为了将数据的格式统一起来,便于我们后期的统计分析。
2.3 数据统计分析阶段
从清洗预处理完成的时候基础上对数据进行聚合汇总,统计一些数据中隐含的一些价值信息
2.4 数据挖掘预测阶段
在统计分析的结果之上,可以预测或者继续深入挖掘数据中的更深层次的含义 一般使用到大数据算法(算法工程师需要做的事情 大数据开发有区别的)
2.5 数据迁移阶段
将统计分析的结果迁移到非大数据环境,为后期的操作做准备
2.6 数据可视化阶段
将统计分析完成的结果指标以可视化图表(柱状图、折线图)的形式进行展示。严格意义上来说数据可视化也不是大数据工程师需要做的事情。
三、纯大数据离线计算项目
电商网站用户行为日志分析平台,电信用户通话数据分析平台
电商网站用户行为日志分析平台项目主要是对电商网站产生的用户行为日志数据进行采集存储、清洗预处理、统计分析、数据可视化展示的。
3.1 预备知识
3.1.1 电商网站的概念
专门用来进行网站购物的平台,大数据最开始使用最广泛、最成熟的就是电商网站。项目主要针对的白龙马电商购物网站–公司的子公司
3.1.2 什么是用户的行为日志数据以及用户的行为数据是怎么产生的
用户行为数据不管什么网站都会有用户的行为数据记录,行为数据指的是用户在网站当中进行的一系列动作,背后都会触发一些程序记录用户的行为数据。用户行为数据我们会通过程序一般都记录到日志文件当中
用户行为数据基本都是源源不断的产生的(7*24小时不停止的产生)
网站的用户行为数据记录不是大数据开发工程师的事,而是软件开发人员的工作(前端、后端工作人员) 无非就是产生数据的时候需要和大数据开发人员沟通记录用户的哪些数据而已
3.1.3 用户行为日志数据的组成(记录哪些数据)
-
用户的系统属性信息:用户使用的浏览器信息、用户使用的操作系统、用户的IP地址等等
-
用户的访问信息:用户触发的行为之后访问的网站信息\
-
用户的来源信息:用户行为触发之后访问的网站信息是从哪个网站过来的
-
点击的产品信息:点击的商品或者连接对应的产品的详细信息可以记录的 点击产品的时候,可以从数据库查询商品的详细信息
-
用户的个人信息:点击网站某一个连接的时候,如果你登录了的话,前端请求的时候,会把用户的标识带上,后端可以根据用户标识去查询你在网站注册的信息(用户的性别、出生年月、用户的昵称、手机号、实名认证信息等等)
-
【补充】网站或者软件的组成
-
前端(界面)
- 作用
负责和用户进行交互的
-
技术
-
web网站
html/css/js vue/recat/angular
-
微信小程序
wxml/wxss/js/json
-
手机app软件
uniapp、c语言的网站制作技术,Object-C
-
PC端软件
Java GUI、python、C语言等等界面制作框架
-
-
后端
- 作用
- 负责和前端之间进行交互(接受前端请求、响应前端数据)
- 处理前端所需的业务逻辑(需要连接数据库)
- 技术
- 比较简单的后端技术:nodejs、php(web全栈开发工程师)
- 比较成熟的后端技术:JavaEE(Servelt/JSP、SSM框架、SpringBoot)、Python(不常用)、C/C++
- 作用
-
数据库
-
作用
负责进行数据的保存的
-
技术
- 存储结构化数据的数据库:MySQL oracle、SQL Server
- 存储临时性缓存数据的数据库:Redis
- 存储非结构化或者文档数据:mongodb
-
-
3.2 项目的开发背景和意义
背景:对于一个电商网站而言,大数据统计分析是非常有必要的,通过大数据的统计分析,我们可以得到很多和网站运营发展有关的指标信息。
用户行为数据(可以从不同的纬度进行统计分析)
3.3 项目的数据格式和数据来源问题
《白龙马电商用户行为日志分析平台》数据来源于网站记录的用户行为日志数据,日志数据我们是通过电商网站内嵌的埋点程序以及后端程序记录的。(埋点程序就是指的是网站的一些”暗箱“操作) 来源问题一般我们知道即可,不需要我们自己去完成,来源一般都是软件开发人员完成的。 项目的数据必然是7*24小时不间断产生的
3.3.1 我们项目记录的用户行为格式如下
149.74.183.133 - - 2018-09-24 19:38:17 "GET https://www.bailongma.com/register HTTP/1.0" 300 72815 https://www.bailongma.com/item/a Windows Internet Explorer Tridentwindows 广西 22.48 108.19 39
上面就是我们网站触发了某些行为(点击、浏览等等),记录的一条完整的用户行为数据(每一个字段之间以空格分割的):
149.74.183.133 用户的IP地址(ip可以统计网站的访客数量)
-- 个字段 两个字段一个代表用户的邮箱,一个代表用户的标识 (都是-- 代表的是没有记录)
2018-09-24 19:38:17 两个字段 一个代表日期 一个代表时间 用户行为触发时间 "
GET https://www.bailongma.com/register HTTP/1.0" 三个字段代表用户行为触发之后访问的网站,请求方法 请求URL 请求的协议
300 请求网站给的响应状态码 1xx 2xx(请求成功) 3xx(重定向,请求成功) 4xx(请求失败 404 前端的问题) 5xx(请求失败 后端代码的问题)
72815 请求网站给我们的响应字节数
https://www.bailongma.com/item/a 请求网站对应来源网站
Windows Internet Explorer Tridentwindows n个字段组成的(不确定)代表的是浏览器和操作系统信息
广西 用户请求地址
22.48 用户请求网站时所处的纬度
108.19 用户请求网站时所处的经度
39 用户的年龄信息
用户的系统属性信息、用户信息、访问信息、来源信息
3.4 项目的开发流程和技术选项
本次我们项目主要分为五个阶段开完成:数据采集存储阶段、数据清洗预处理阶段、数据统计分析阶段、数据迁移导出阶段、数据可视化阶段、任务调度阶段。
3.5 项目的统计分析的指标
3.5.1 从终端纬度
不同浏览器的用户使用量
3.5.2 从用户纬度
不同年龄段用户访问量
网站的独立访客数
网站的新老用户数量
3.5.3 从地理纬度
不同省份用户的访问量
3.5.4 从时间纬度
每一年用户的访问量
每一月用户的访问量
每天/每小时用户的访问量
不同季度的用户访问量
3.5.5 从来源纬度
网站站内和站外流量的对比
3.6 项目的前置阶段–数据的来源和产生问题
严格意义上来说不属于大数据环节的一部分,但是如果没有这个阶段,那么大数据就无从谈起
《白龙马电商用户行为日志分析平台》数据来源于我们网站的埋点程序,当用户在白龙马电商网站的界面上触发了某种动作(浏览、点击、鼠标的移入等等),网站的后端会把本次用户的行为以数据的形式记录到一个日志文件中。 149.74.183.133 - - 2018-09-24 19:38:17 “GET https://www.bailongma.com/register HTTP/1.0” 300 72815 https://www.bailongma.com/item/a Windows Internet Explorer Tridentwindows 广西 22.48 108.19 39
只要电商网站不关闭,那么数据源源不断的产生到日志文件当中。意味着网站的用户行为数据是7*24小时源源不断的会记录的。
虽然我们没有网站,但是我们有网站以前产生的脱敏数据,因此我们就可以基于以前产生的脱敏数据,模拟数据产生的过程 只需要按照数据的格式产生一批和脱敏数据格式一致的数据即可。产生的时候增加一点随机性(每分钟产生100条或者每隔10秒产生20条数据)。
package com.sxuek;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 专门用来产生用户行为数据的 而且通过这个类模拟白龙马用户行为数据产生过程
* 120.191.181.178 - - 2018-02-18 20:24:39 "POST https://www.bailongma.com/item/b HTTP/1.1" 203 69172 https://www.bailongma.com/register UCBrowser Webkit X3android 8.0 海南 20.02 110.20 36
* ip地址 两个中划线 日期 时间 用户的请求网站(三个字段组成的) 请求网站的响应码 请求的响应字节数 来源网站 浏览器信息(n个字段) 省份 纬度 经度 年龄
*
* 模拟数据的时候--数据的真实性,IP地址随机生成 时间生成-数据产生的时间 来源网站和请求网址可以从脱敏数据中获取回来
* 浏览器信息(从文件读取)
*/
public class DataGenerator {
//1、定义一个存储IP地址的集合 一会产生模拟数据的时候,模拟数据当中ip地址从集合中随机获取一个
private static List<String> ipList = new ArrayList<>();
//2、定义一个集合,集合存放请求的白龙马的网址 模拟数据当中请求网址时从集合中随机获取一个即可
private static List<String> requestList = new ArrayList<>();
//3、定义一个集合,集合存放来源网站信息,模拟数据的来源网站时候我们可以从集合中随机获取一个即可
private static List<String> refererList = new ArrayList<>();
//4、定义一个集合 存放请求的响应状态码
private static List<String> codeList = new ArrayList<>();
//5、定义一个集合 存放浏览器信息 一会模拟产生数据时,浏览器信息从集合中随机获取
private static List<String> userAgentList = new ArrayList<>();
//6、定义一个集合,集合存放地理位置信息
private static List<String> addressList = new ArrayList<>();
/**
* 初始化方法,初始化方法主要是给我们上面定义的集合先填充一点数据
*/
private static void init(){
/**
* 1、填充状态码集合 一会随机从集合获取一条数据,默认情况下每一条数据的获取概率都是一样
* 如果你想让某一个值获取概率大一点那么可以将这个值在集合多添加几次
*/
codeList.addAll(Arrays.asList("200","203","300","301","200","203","300","301","200","203","300","301","200","203","300","301","400","401","403","500","503"));
/**
* 2、填充浏览器信息集合
*/
userAgentList.add("Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Win64; x64; Trident/4.0)");
userAgentList.add("Mozilla/5.0 (Windows; U; Windows NT 5.2) Gecko/2008070208 Firefox/3.0.1");
userAgentList.add("Mozilla/5.0 (Macintosh; PPC Mac OS X; U; en) Opera 8.0");
userAgentList.add("Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13");
userAgentList.add("Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13");
userAgentList.add("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11");
userAgentList.add("Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400) ");
userAgentList.add("Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0");
userAgentList.add("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11");
/**
* 填充ip地址 请求网站 来源网站 省份地理位置信息 四个集合
* 四个集合的填充不能随便瞎写 集合从脱敏数据文件中读取对应的值填充进来
*/
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader("a.log"));
String line = null;
//这个数据是我们给大家发送的脱敏数据 脱敏数据大数据没法使用 原因是因为是旧数据
while((line = bufferedReader.readLine()) != null){
String[] array = line.split(" ");
//脱敏数据中的IP地址放到ipList集合中
ipList.add(array[0]);
//需要把请求方式 请求网站 请求协议三个字段以空格组合放到requestList集合中
requestList.add(array[5]+" "+array[6]+" "+array[7]);
//来源信息把它加到来源列表当中
refererList.add(array[10]);
refererList.add("https://www.baidu.com/search");
refererList.add("https://www.baidu.com/search");
refererList.add("https://www.baidu.com/search");
refererList.add("https://www.sougou.com/search");
refererList.add("https://www.google.com/search");
//把省份 维度 经度 加到地理位置数据中
addressList.add(array[array.length-4]+" "+array[array.length-3]+" "+array[array.length-2]);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (bufferedReader != null){
try {
bufferedReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
/**
* 程序执行入口
* @param args
*/
public static void main(String[] args) throws IOException, InterruptedException {
//1、填充模拟数据集合
init();
/**
* 2、模拟数据的目的是为了模拟真实的数据产生逻辑,
* 真实场景下 数据是源源不断的产生的。所以我们模拟程序也是源源不断的产生的,不会停止的 除非你手动停止
* 产生数据的时候,数据得有一个存放的一个文件路径 文件中通过IO流写入数据
*/
Scanner scanner = new Scanner(System.in);
System.out.println("请输入网站产生的用户行为日志数据文件的路径");
String path = scanner.next();
//定义IO输出流 用于模拟一会数据产生之后输出到日志文件的的过程
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(path));
//随机类 用于产生随机数的
Random random = new Random();
//定义时间格式类 用于格式化时间的
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
while (true){
/**
* 真实情况下 虽然数据是7*24小时产生的,但是并不是每时每刻都在产生数据,
* 而是会间断性的产生的 比如每隔1-10s 产生10-50条数据
* 尤其是在凌晨12:00 -6:00的时候 数据产生的非常缓慢
*/
//1、先获取数据产生的一个时间
Calendar calendar = Calendar.getInstance();
boolean judgeNight = isJudgeNight(calendar);
// num代表一次产生num条数据
int num = 0;
// time代表产生一次数据 休息多长时间
int time = 0;
if (judgeNight){
//代表是凌晨的时间
num = random.nextInt(10);
time = 30000+random.nextInt(60001);
}else{
//代表的是非凌晨的时间
num = random.nextInt(50);
time = 1000+ random.nextInt(20001);
}
/**
* for循环代表产生num条数据
*/
for (int i = 0; i < num; i++) {
/**
* 获取数据对应的值 然后拼接 输出即可
*/
//1、获取ip地址 [0,ipList.size()-1]
String ip = ipList.get(random.nextInt(ipList.size()));
//2、获取数据的生成时间
Date date = new Date();
//2023-08-28 18:00:00
String dataGenTime = sdf.format(date);
//3、随机获取请求的网址--行为触发之后请求的网址
String request = requestList.get(random.nextInt(requestList.size()));
//4、随机获取一个状态码
String code = codeList.get(random.nextInt(codeList.size()));
//5、随机产生一个响应字节数
int bytes = random.nextInt(100000);
//6、随机获取一个来源网站
String referer = refererList.get(random.nextInt(refererList.size()));
//7、随机获取一个浏览器信息
String userAgent = userAgentList.get(random.nextInt(userAgentList.size()));
//8、随机获取一个地理位置信息
String address = addressList.get(random.nextInt(addressList.size()));
//9、随机产生一个年龄
int age = 18+ random.nextInt(71);
//组装数据 可以使用StringBuffer完成 数据和数据之间一定要以空格分割
String data = ip+" - - "+dataGenTime+" "+request+" "+code+" "+bytes+" "+referer+" "+userAgent+" "+address+" "+age;
//将数据输出
bufferedWriter.write(data);
//写出一个换行符 保证一条用户行为数据独占一行
bufferedWriter.newLine();
//bufferWriter是处理流 输出数据必须加flush
bufferedWriter.flush();
}
//生成num条数据之后 间隔time时间之后再继续生成
Thread.sleep(time);
System.out.println("间隔了"+time+"秒之后生成了"+num+"条数据");
}
}
/**
* 方法是用来判断是否为凌晨的时间
* @param cal
* @return
*/
public static boolean isJudgeNight(Calendar cal){
//先获取当前的时间
Date currentTime = cal.getTime();
//先获取当前日期下的凌晨时间段 两个时间 一个是开始的时间 一个是结束的时间
//开始的时间是当天的00:00:00 结束时间 06:00:00
cal.set(Calendar.HOUR_OF_DAY,0);
cal.set(Calendar.MINUTE,0);
cal.set(Calendar.SECOND,0);
//获取当前时间对应的凌晨的开始时间
Date startTime = cal.getTime();
cal.set(Calendar.HOUR_OF_DAY,6);
cal.set(Calendar.MINUTE,0);
cal.set(Calendar.SECOND,0);
//获取当前时间对应的结束时间
Date endTime = cal.getTime();
if (currentTime.after(startTime) && currentTime.before(endTime)){
return true;
}else{
return false;
}
}
}