大数据_网络日志流量分析案例

  • 技术和架构
    • 数据处理流程
    • 系统的架构
    • 数据展现
  • 数据采集
    • Nginx日志数据内容样式示例
    • Flume配置
  • 数据预处理
    • 目的
    • 初步处理
      • 开发mr程序
      • 脚本
      • 效果
    • 点击流模型数据梳理/visit信息表
      • 开发mr程序
      • 脚本
      • 效果
  • 数据仓库设计
    • 简述
    • 表结构
    • 实现
      • 创建ODS层数据表(ods_weblog_origin,ods_click_stream_visit,ods_click_stream_visit)
      • 导入数据脚本(ods_weblog_origin,ods_click_stream_visit,ods_click_stream_visit)
  • 流量统计分析
    • 分析示例
    • 实现示例
      • 创建ODS层明细宽表(ods_weblog_detail)
      • 导入数据脚本(ods_weblog_detail)
  • 统计数据导出
    • Mysql创建表
    • Sqoop同步数据
  • Azkaban工作流调度
    • 开发工具类
    • 定义数据每日上传的目录
    • 根据上面目录改造执行编写的MR程序
    • 程序打成jar包
    • 开发azkaban调度脚本
      • 初始化脚本
      • 第一个MR程序执行(1.job)
      • 第二个MR程序执行(2.job)
      • 第二个MR程序执行(3.job)
      • hive表数据加载
      • hive表数据分析
      • 分析结果通过sqoop导出
      • 所有文件打包
      • 定时执行
  • 数据可视化实现

技术和架构

数据处理流程

网站流量日志数据分析是一个纯粹的数据分析项目,其整体流程基本上就是依据数据的处理流程进行。有以下几个大的步骤:

1、数据采集
数据采集概念,目前行业会有两种解释:一是数据从无到有的过程(web服务器打印的日志、自定义采集的日志等)叫做数据采集;另一方面也有把通过使用Flume等工具把数据采集到指定位置的这个过程叫做数据采集。
关于具体含义要结合语境具体分析,明白语境中具体含义即可。

2、数据预处理
通过mapreduce程序对采集到的原始日志数据进行预处理,比如清洗,格式整理,滤除脏数据等,并且梳理成点击流模型数据。

3、数据入库
将预处理之后的数据导入到HIVE仓库中相应的库和表中。

4、数据分析
项目的核心内容,即根据需求开发ETL分析语句,得出各种统计结果。

5、数据展现
将分析所得数据进行数据可视化,一般通过图表进行展示。
在这里插入图片描述

系统的架构

相对于传统的BI数据处理,流程几乎差不多,但是因为是处理大数据,所以流程中各环节所使用的技术则跟传统BI完全不同:

数据采集:定制开发采集程序,或使用开源框架Flume
数据预处理:定制开发mapreduce程序运行于hadoop集群
数据仓库技术:基于hadoop之上的Hive
数据导出:基于hadoop的sqoop数据导入导出工具
数据可视化:定制开发web程序(echarts)
整个过程的流程调度:hadoop生态圈中的azkaban工具
在这里插入图片描述
系统的数据分析不是一次性的,而是按照一定的时间频率反复计算,因而整个处理链条中的各个环节需要按照一定的先后依赖关系紧密衔接,即涉及到大量任务单元的管理调度,所以,项目中需要添加一个任务调度模块。

数据展现

数据展现的目的是将分析所得的数据进行可视化,以便运营决策人员能更方便地获取数据,更快更简单地理解数据。
市面上有许多开源的数据可视化软件、工具。比如Echarts.

数据采集

在网站web流量日志分析这种场景中,对数据采集部分的可靠性、容错能力要求通常不会非常严苛,因此使用通用的flume日志采集框架完全可以满足需求。

Nginx日志数据内容样式示例

58.215.204.118 - - [18/Sep/2013:06:51:35 +0000] "GET /wp-includes/js/jquery/jquery.js?ver=1.10.2 HTTP/1.1" 304 0 "http://blog.fens.me/nodejs-socketio-chat/" "Mozilla/5.0 (Windows NT 5.1; rv:23.0) Gecko/20100101 Firefox/23.0"

1、访客ip地址: 58.215.204.118
2、访客用户信息: - -
3、请求时间:[18/Sep/2013:06:51:35 +0000]
4、请求方式:GET
5、请求的url:/wp-includes/js/jquery/jquery.js?ver=1.10.2
6、请求所用协议:HTTP/1.1
7、响应码:304
8、返回的数据流量:0
9、访客的来源url:http://blog.fens.me/nodejs-socketio-chat/
10、访客所用浏览器:Mozilla/5.0 (Windows NT 5.1; rv:23.0) Gecko/20100101 Firefox/23.0

脚本:

1、nginx_log.sh

#!/bin/bash    
#设置日志文件存放目录  
logs_path="/usr/local/nginx/logs/"  
#设置pid文件  
pid_path="/usr/local/nginx/nginx-1.7.3/logs/nginx.pid"  
#日志文件    
filepath=${logs_path}"access.log"  
# Source function library.    
#重命名日志文件  
mv ${logs_path}access.log ${logs_path}access_$(date -d '-1 day' '+%Y-%m-%d').log  
#向nginx主进程发信号重新打开日志  
kill -USR1 `cat ${pid_path}`  

2、定时执行

0 0 * * *  sh /usr/local/nginx/nginx_log.sh

Flume配置

针对nginx日志生成场景,如果通过flume(1.6)收集,无论是Spooling Directory Source和Exec Source均不能满足动态实时收集的需求,
flume1.7稳定版本中,提供了一个非常好用的TaildirSource,使用这个source,可以监控一个目录,并且使用正则表达式匹配该目录中的文件名进行实时收集。

核心配置:

agent1.sources = source1
agent1.sinks = sink1
agent1.channels = channel1agent1.sources.source1.type = TAILDIR 
agent1.sources.source1.positionFile = /var/log/flume/taildir_position.json
agent1.sources.source1.filegroups = f1
agent1.sources.source1.filegroups.f1 = /usr/local/nginx/logs/access_*.logagent1.sources.source1.interceptors = i1
agent1.sources.source1.interceptors.i1.type = host
agent1.sources.source1.interceptors.i1.hostHeader = hostname#配置sink组件为hdfs
agent1.sinks.sink1.type = hdfs
agent1.sinks.sink1.hdfs.path=hdfs://node-21:9000/weblog/flume-collection/%y-%m-%d/%H-%M_%hostname
#指定文件名前缀
agent1.sinks.sink1.hdfs.filePrefix = access_log
#指定每批下沉数据的记录条数
agent1.sinks.sink1.hdfs.batchSize= 100
agent1.sinks.sink1.hdfs.fileType = DataStream
agent1.sinks.sink1.hdfs.writeFormat =Text
#指定下沉文件按1G大小滚动
agent1.sinks.sink1.hdfs.rollSize = 1024*1024*1024
#指定下沉文件按1000000条数滚动
agent1.sinks.sink1.hdfs.rollCount = 1000000
#指定下沉文件按30分钟滚动
agent1.sinks.sink1.hdfs.rollInterval = 30
#agent1.sinks.sink1.hdfs.round = true
#agent1.sinks.sink1.hdfs.roundValue = 10
#agent1.sinks.sink1.hdfs.roundUnit = minute
agent1.sinks.sink1.hdfs.useLocalTimeStamp = true#使用memory类型channel
agent1.channels.channel1.type = memory
agent1.channels.channel1.capacity = 500000
agent1.channels.channel1.transactionCapacity = 600# Bind the source and sink to the channel
agent1.sources.source1.channels = channel1
agent1.sinks.sink1.channel = channel1

filegroups:指定filegroups,可以有多个,以空格分隔;(TailSource可以同时监控tail多个目录中的文件)。
positionFile:配置检查点文件的路径,检查点文件会以json格式保存已经tail文件的位置,解决了断点不能续传的缺陷。
filegroups.:配置每个filegroup的文件绝对路径,文件名可以用正则表达式匹配。
通过以上配置,就可以监控文件内容的增加和文件的增加。产生和所配置的文件名正则表达式不匹配的文件,则不会被tail。

脚本:

#!/bin/bash#
# ===========================================================================
# 程序名称:     
# 功能描述:     移动文件到预处理工作目录
# 输入参数:     运行日期
# 目标路径:     /data/weblog/preprocess/input
# 数据源  :     flume采集数据所存放的路径: /weblog/flume-collection
# 代码审核:     
# 修改人名:
# 修改日期:
# 修改原因:
# 修改列表: 
# ===========================================================================#set java env
export JAVA_HOME=/home/hadoop/apps/jdk1.7.0_51
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH#set hadoop env
export HADOOP_HOME=/home/hadoop/apps/hadoop-2.6.1
export PATH=${HADOOP_HOME}/bin:${HADOOP_HOME}/sbin:$PATH#flume采集生成的日志文件存放的目录
log_flume_dir=/weblog/flume-collection#预处理程序的工作目录
log_pre_input=/data/weblog/preprocess/input#获取时间信息
day_01=`date -d'-1 day' +%Y-%m-%d`
syear=`date --date=$day_01 +%Y`
smonth=`date --date=$day_01 +%m`
sday=`date --date=$day_01 +%d`#读取日志文件的目录,判断是否有需要上传的文件
files=`hadoop fs -ls $log_flume_dir | grep $day_01 | wc -l`
if [ $files -gt 0 ]; then
hadoop fs -mv ${log_flume_dir}/${day_01} ${log_pre_input}
echo "success moved ${log_flume_dir}/${day_01} to ${log_pre_input} ....."
fi

数据预处理

目的

过滤“不合规”数据,清洗无意义的数据。
格式转换和规整。
根据后续的统计需求,过滤分离出各种不同主题(不同栏目path)的基础数据。
在这里插入图片描述

初步处理

首先进行对数据的初步分析 , 将有用的url筛选出来 , 因此重写了Writable的初始化方法即setup方法 , 用于对日志文件进行过滤。

其次 , 我们在对数据进行遍历操作时 , 我们编写了一个WebLogParser类用于处理日志数据 , 里面对日期的格式进行了转换 , 并且对js/图片/css等静态资源进行了过滤。

最终实现文件的读取转换 , 即获得了初步数据处理。

开发mr程序

1、WebLogBean用于存储javabean类型的数据

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparable;/*** 对接外部数据的层,表结构定义最好跟外部数据源保持一致* 术语: 贴源表* @author**/
public class WebLogBean implements Writable {private boolean valid = true;// 判断数据是否合法private String remote_addr;// 记录客户端的ip地址private String remote_user;// 记录客户端用户名称,忽略属性"-"private String time_local;// 记录访问时间与时区private String request;// 记录请求的url与http协议private String status;// 记录请求状态;成功是200private String body_bytes_sent;// 记录发送给客户端文件主体内容大小private String http_referer;// 用来记录从那个页面链接访问过来的private String http_user_agent;// 记录客户浏览器的相关信息public void set(boolean valid,String remote_addr, String remote_user, String time_local, String request, String status, String body_bytes_sent, String http_referer, String http_user_agent) {this.valid = valid;this.remote_addr = remote_addr;this.remote_user = remote_user;this.time_local = time_local;this.request = request;this.status = status;this.body_bytes_sent = body_bytes_sent;this.http_referer = http_referer;this.http_user_agent = http_user_agent;}public String getRemote_addr() {return remote_addr;}public void setRemote_addr(String remote_addr) {this.remote_addr = remote_addr;}public String getRemote_user() {return remote_user;}public void setRemote_user(String remote_user) {this.remote_user = remote_user;}public String getTime_local() {return this.time_local;}public void setTime_local(String time_local) {this.time_local = time_local;}public String getRequest() {return request;}public void setRequest(String request) {this.request = request;}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}public String getBody_bytes_sent() {return body_bytes_sent;}public void setBody_bytes_sent(String body_bytes_sent) {this.body_bytes_sent = body_bytes_sent;}public String getHttp_referer() {return http_referer;}public void setHttp_referer(String http_referer) {this.http_referer = http_referer;}public String getHttp_user_agent() {return http_user_agent;}public void setHttp_user_agent(String http_user_agent) {this.http_user_agent = http_user_agent;}public boolean isValid() {return valid;}public void setValid(boolean valid) {this.valid = valid;}@Overridepublic String toString() {StringBuilder sb = new StringBuilder();sb.append(this.valid);sb.append("\001").append(this.getRemote_addr());sb.append("\001").append(this.getRemote_user());sb.append("\001").append(this.getTime_local());sb.append("\001").append(this.getRequest());sb.append("\001").append(this.getStatus());sb.append("\001").append(this.getBody_bytes_sent());sb.append("\001").append(this.getHttp_referer());sb.append("\001").append(this.getHttp_user_agent());return sb.toString();}@Overridepublic void readFields(DataInput in) throws IOException {this.valid = in.readBoolean();this.remote_addr = in.readUTF();this.remote_user = in.readUTF();this.time_local = in.readUTF();this.request = in.readUTF();this.status = in.readUTF();this.body_bytes_sent = in.readUTF();this.http_referer = in.readUTF();this.http_user_agent = in.readUTF();}@Overridepublic void write(DataOutput out) throws IOException {out.writeBoolean(this.valid);out.writeUTF(null==remote_addr?"":remote_addr);out.writeUTF(null==remote_user?"":remote_user);out.writeUTF(null==time_local?"":time_local);out.writeUTF(null==request?"":request);out.writeUTF(null==status?"":status);out.writeUTF(null==body_bytes_sent?"":body_bytes_sent);out.writeUTF(null==http_referer?"":http_referer);out.writeUTF(null==http_user_agent?"":http_user_agent);}}

2、处理

 /*** 处理日志数据 , 里面对日期的格式进行了转换 , 并且对js/图片/css等静态资源进行了过滤* */
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.Set;public class WebLogParser {public static SimpleDateFormat df1 = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss", Locale.US);public static SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);public static WebLogBean parser(String line) {WebLogBean webLogBean = new WebLogBean();String[] arr = line.split(" ");if (arr.length > 11) {webLogBean.setRemote_addr(arr[0]);webLogBean.setRemote_user(arr[1]);String time_local = formatDate(arr[3].substring(1));if(null==time_local || "".equals(time_local)) time_local="-invalid_time-";webLogBean.setTime_local(time_local);webLogBean.setRequest(arr[6]);webLogBean.setStatus(arr[8]);webLogBean.setBody_bytes_sent(arr[9]);webLogBean.setHttp_referer(arr[10]);//如果useragent元素较多,拼接useragentif (arr.length > 12) {StringBuilder sb = new StringBuilder();for(int i=11;i<arr.length;i++){sb.append(arr[i]);}webLogBean.setHttp_user_agent(sb.toString());} else {webLogBean.setHttp_user_agent(arr[11]);}if (Integer.parseInt(webLogBean.getStatus()) >= 400) {// 大于400,HTTP错误webLogBean.setValid(false);}if("-invalid_time-".equals(webLogBean.getTime_local())){webLogBean.setValid(false);}} else {webLogBean=null;}return webLogBean;}public static void filtStaticResource(WebLogBean bean, Set<String> pages) {if (!pages.contains(bean.getRequest())) {bean.setValid(false);}}//格式化时间方法public static String formatDate(String time_local) {try {return df2.format(df1.parse(time_local));} catch (ParseException e) {return null;}}
}import java.io.IOException;
import java.util.HashSet;
import java.util.Set;import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;/*** 处理原始日志,过滤出真实pv请求 转换时间格式 对缺失字段填充默认值 对记录标记valid和invalid* */
public class WeblogPreProcess {static class WeblogPreProcessMapper extends Mapper<LongWritable, Text, Text, NullWritable> {// 用来存储网站url分类数据Set<String> pages = new HashSet<String>();Text k = new Text();NullWritable v = NullWritable.get();/*** 从外部配置文件中加载网站的有用url分类数据 存储到maptask的内存中,用来对日志数据进行过滤*/@Overrideprotected void setup(Context context) throws IOException, InterruptedException {pages.add("/about");pages.add("/black-ip-list/");pages.add("/cassandra-clustor/");pages.add("/finance-rhive-repurchase/");pages.add("/hadoop-family-roadmap/");pages.add("/hadoop-hive-intro/");pages.add("/hadoop-zookeeper-intro/");pages.add("/hadoop-mahout-roadmap/");}@Overrideprotected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {String line = value.toString();WebLogBean webLogBean = WebLogParser.parser(line);if (webLogBean != null) {// 过滤js/图片/css等静态资源WebLogParser.filtStaticResource(webLogBean, pages);/* if (!webLogBean.isValid()) return; */k.set(webLogBean.toString());context.write(k, v);}}}public static void main(String[] args) throws Exception {Configuration conf = new Configuration();Job job = Job.getInstance(conf);job.setJarByClass(WeblogPreProcess.class);job.setMapperClass(WeblogPreProcessMapper.class);job.setOutputKeyClass(Text.class);job.setOutputValueClass(NullWritable.class);//		 FileInputFormat.setInputPaths(job, new Path(args[0]));
//		 FileOutputFormat.setOutputPath(job, new Path(args[1]));FileInputFormat.setInputPaths(job, new Path("d:/weblog/input"));FileOutputFormat.setOutputPath(job, new Path("d:/weblog/output"));job.setNumReduceTasks(0);boolean res = job.waitForCompletion(true);System.exit(res?0:1);}}

脚本

#!/bin/bash#
# ===========================================================================
# 程序名称:     
# 功能描述:     预处理原始日志
# 输入参数:     运行日期
# 数据源  :     /data/weblog/preprocess/input/2016-03-07  
# 目标路径:     /data/weblog/preprocess/output/2016-03-07
# 创建日期:     2016-12-21
# 版本说明:     v1.0
# 代码审核:     
# 修改人名:
# 修改日期:
# 修改原因:
# 修改列表: 
# ===========================================================================#set java env
export JAVA_HOME=/home/hadoop/apps/jdk1.7.0_51
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH#set hadoop env
export HADOOP_HOME=/home/hadoop/apps/hadoop
export PATH=${HADOOP_HOME}/bin:${HADOOP_HOME}/sbin:$PATH#清洗程序类名
preprocess_class="cn.xxx.bigdata.weblog.pre.WeblogPreProcess"#待处理日志存放的目录
log_pre_input=/data/weblog/preprocess/input#预处理输出结果(valid)目录
log_pre_valid_output=/data/weblog/preprocess/valid_output
log_pre_output=/data/weblog/preprocess/output#获取时间信息
day_01=`date -d'-1 day' +%Y-%m-%d`
syear=`date --date=$day_01 +%Y`
smonth=`date --date=$day_01 +%m`
sday=`date --date=$day_01 +%d`#读取日志文件的目录,判断是否有当日待处理的目录(如:2016-03-18)
files=`hadoop fs -ls $log_pre_input | grep $day_01 | wc -l`
if [ $files -gt 0 ]; then
#提交mr任务job运行
echo "running..    hadoop jar weblog.jar $preprocess_class $log_pre_input/$day_01 /$log_pre_output/$day_01"
hadoop jar weblog.jar $preprocess_class $log_pre_input/$day_01 $log_pre_output/$day_01
fi#如果失败
#发送邮件或短信,人为来干预

效果

在这里插入图片描述

点击流模型数据梳理/visit信息表

首先对清洗后的原始数据进行maptask操作 , 输出结果格式为<ip , weblogbean>类型 ;

reducetask对其进行处理 , 对对象集合根据某一属性进行排序 , 此时运用到Collection.sort(数据集合) , 重写排序规则 , 将获取的参数用pageViewBean封装处理 , 获取到对应的session , ip , 地址 , 时间 , 访问页面 , url , 停留时间 , 第几步等参数信息遍历数据集合 , 判断两次时间间隔是否大于30分钟 , 若大于 , 则属于另一个会话 , 若小于 , 则属于同一个会话。

我们将reducetask输出的数据类型定义为<nullWritable,pageViewBean>

通过pageViews的数据 , 经过map程序后到达visit的reducetask , 同样的将输出的数据封装成VisitBean输出。

VisitBean数据格式(session , remote_addr , inTime , outTime , inPage , outPage , referal , pageVisits)

开发mr程序

1、PageViewBean

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;import org.apache.hadoop.io.Writable;public class PageViewsBean implements Writable {private String session;private String remote_addr;private String timestr;private String request;private int step;private String staylong;private String referal;private String useragent;private String bytes_send;private String status;public void set(String session, String remote_addr, String useragent, String timestr, String request, int step, String staylong, String referal, String bytes_send, String status) {this.session = session;this.remote_addr = remote_addr;this.useragent = useragent;this.timestr = timestr;this.request = request;this.step = step;this.staylong = staylong;this.referal = referal;this.bytes_send = bytes_send;this.status = status;}public String getSession() {return session;}public void setSession(String session) {this.session = session;}public String getRemote_addr() {return remote_addr;}public void setRemote_addr(String remote_addr) {this.remote_addr = remote_addr;}public String getTimestr() {return timestr;}public void setTimestr(String timestr) {this.timestr = timestr;}public String getRequest() {return request;}public void setRequest(String request) {this.request = request;}public int getStep() {return step;}public void setStep(int step) {this.step = step;}public String getStaylong() {return staylong;}public void setStaylong(String staylong) {this.staylong = staylong;}public String getReferal() {return referal;}public void setReferal(String referal) {this.referal = referal;}public String getUseragent() {return useragent;}public void setUseragent(String useragent) {this.useragent = useragent;}public String getBytes_send() {return bytes_send;}public void setBytes_send(String bytes_send) {this.bytes_send = bytes_send;}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}@Overridepublic void readFields(DataInput in) throws IOException {this.session = in.readUTF();this.remote_addr = in.readUTF();this.timestr = in.readUTF();this.request = in.readUTF();this.step = in.readInt();this.staylong = in.readUTF();this.referal = in.readUTF();this.useragent = in.readUTF();this.bytes_send = in.readUTF();this.status = in.readUTF();}@Overridepublic void write(DataOutput out) throws IOException {out.writeUTF(session);out.writeUTF(remote_addr);out.writeUTF(timestr);out.writeUTF(request);out.writeInt(step);out.writeUTF(staylong);out.writeUTF(referal);out.writeUTF(useragent);out.writeUTF(bytes_send);out.writeUTF(status);}
}

2、ClickStreamPageView

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;import org.apache.commons.beanutils.BeanUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;/*** * 将清洗之后的日志梳理出点击流pageviews模型数据* * 输入数据是清洗过后的结果数据* * 区分出每一次会话,给每一次visit(session)增加了session-id(随机uuid)* 梳理出每一次会话中所访问的每个页面(请求时间,url,停留时长,以及该页面在这次session中的序号)* 保留referral_url,body_bytes_send,useragent* * * @author* */
public class ClickStreamPageView {static class ClickStreamMapper extends Mapper<LongWritable, Text, Text, WebLogBean> {Text k = new Text();WebLogBean v = new WebLogBean();@Overrideprotected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {String line = value.toString();String[] fields = line.split("\001");if (fields.length < 9) return;//将切分出来的各字段set到weblogbean中v.set("true".equals(fields[0]) ? true : false, fields[1], fields[2], fields[3], fields[4], fields[5], fields[6], fields[7], fields[8]);//只有有效记录才进入后续处理if (v.isValid()) {//此处用ip地址来标识用户k.set(v.getRemote_addr());context.write(k, v);}}}static class ClickStreamReducer extends Reducer<Text, WebLogBean, NullWritable, Text> {Text v = new Text();@Overrideprotected void reduce(Text key, Iterable<WebLogBean> values, Context context) throws IOException, InterruptedException {ArrayList<WebLogBean> beans = new ArrayList<WebLogBean>();// 先将一个用户的所有访问记录中的时间拿出来排序try {for (WebLogBean bean : values) {WebLogBean webLogBean = new WebLogBean();try {BeanUtils.copyProperties(webLogBean, bean);} catch(Exception e) {e.printStackTrace();}beans.add(webLogBean);}//将bean按时间先后顺序排序Collections.sort(beans, new Comparator<WebLogBean>() {@Overridepublic int compare(WebLogBean o1, WebLogBean o2) {try {Date d1 = toDate(o1.getTime_local());Date d2 = toDate(o2.getTime_local());if (d1 == null || d2 == null)return 0;return d1.compareTo(d2);} catch (Exception e) {e.printStackTrace();return 0;}}});/*** 以下逻辑为:从有序bean中分辨出各次visit,并对一次visit中所访问的page按顺序标号step* 核心思想:* 就是比较相邻两条记录中的时间差,如果时间差<30分钟,则该两条记录属于同一个session* 否则,就属于不同的session* */int step = 1;String session = UUID.randomUUID().toString();for (int i = 0; i < beans.size(); i++) {WebLogBean bean = beans.get(i);// 如果仅有1条数据,则直接输出if (1 == beans.size()) {// 设置默认停留时长为60sv.set(session+"\001"+key.toString()+"\001"+bean.getRemote_user() + "\001" + bean.getTime_local() + "\001" + bean.getRequest() + "\001" + step + "\001" + (60) + "\001" + bean.getHttp_referer() + "\001" + bean.getHttp_user_agent() + "\001" + bean.getBody_bytes_sent() + "\001"+ bean.getStatus());context.write(NullWritable.get(), v);session = UUID.randomUUID().toString();break;}// 如果不止1条数据,则将第一条跳过不输出,遍历第二条时再输出if (i == 0) {continue;}// 求近两次时间差long timeDiff = timeDiff(toDate(bean.getTime_local()), toDate(beans.get(i - 1).getTime_local()));// 如果本次-上次时间差<30分钟,则输出前一次的页面访问信息if (timeDiff < 30 * 60 * 1000) {v.set(session+"\001"+key.toString()+"\001"+beans.get(i - 1).getRemote_user() + "\001" + beans.get(i - 1).getTime_local() + "\001" + beans.get(i - 1).getRequest() + "\001" + step + "\001" + (timeDiff / 1000) + "\001" + beans.get(i - 1).getHttp_referer() + "\001"+ beans.get(i - 1).getHttp_user_agent() + "\001" + beans.get(i - 1).getBody_bytes_sent() + "\001" + beans.get(i - 1).getStatus());context.write(NullWritable.get(), v);step++;} else {// 如果本次-上次时间差>30分钟,则输出前一次的页面访问信息且将step重置,以分隔为新的visitv.set(session+"\001"+key.toString()+"\001"+beans.get(i - 1).getRemote_user() + "\001" + beans.get(i - 1).getTime_local() + "\001" + beans.get(i - 1).getRequest() + "\001" + (step) + "\001" + (60) + "\001" + beans.get(i - 1).getHttp_referer() + "\001"+ beans.get(i - 1).getHttp_user_agent() + "\001" + beans.get(i - 1).getBody_bytes_sent() + "\001" + beans.get(i - 1).getStatus());context.write(NullWritable.get(), v);// 输出完上一条之后,重置step编号step = 1;session = UUID.randomUUID().toString();}// 如果此次遍历的是最后一条,则将本条直接输出if (i == beans.size() - 1) {// 设置默认停留市场为60sv.set(session+"\001"+key.toString()+"\001"+bean.getRemote_user() + "\001" + bean.getTime_local() + "\001" + bean.getRequest() + "\001" + step + "\001" + (60) + "\001" + bean.getHttp_referer() + "\001" + bean.getHttp_user_agent() + "\001" + bean.getBody_bytes_sent() + "\001" + bean.getStatus());context.write(NullWritable.get(), v);}}} catch (ParseException e) {e.printStackTrace();}}private String toStr(Date date) {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);return df.format(date);}private Date toDate(String timeStr) throws ParseException {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);return df.parse(timeStr);}private long timeDiff(String time1, String time2) throws ParseException {Date d1 = toDate(time1);Date d2 = toDate(time2);return d1.getTime() - d2.getTime();}private long timeDiff(Date time1, Date time2) throws ParseException {return time1.getTime() - time2.getTime();}}public static void main(String[] args) throws Exception {Configuration conf = new Configuration();Job job = Job.getInstance(conf);job.setJarByClass(ClickStreamPageView.class);job.setMapperClass(ClickStreamMapper.class);job.setReducerClass(ClickStreamReducer.class);job.setMapOutputKeyClass(Text.class);job.setMapOutputValueClass(WebLogBean.class);job.setOutputKeyClass(Text.class);job.setOutputValueClass(Text.class);//		FileInputFormat.setInputPaths(job, new Path(args[0]));
//		FileOutputFormat.setOutputPath(job, new Path(args[1]));FileInputFormat.setInputPaths(job, new Path("d:/weblog/output"));FileOutputFormat.setOutputPath(job, new Path("d:/weblog/pageviews"));job.waitForCompletion(true);}}

3、VisitBean

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;import org.apache.hadoop.io.Writable;public class VisitBean implements Writable {private String session;private String remote_addr;private String inTime;private String outTime;private String inPage;private String outPage;private String referal;private int pageVisits;public void set(String session, String remote_addr, String inTime, String outTime, String inPage, String outPage, String referal, int pageVisits) {this.session = session;this.remote_addr = remote_addr;this.inTime = inTime;this.outTime = outTime;this.inPage = inPage;this.outPage = outPage;this.referal = referal;this.pageVisits = pageVisits;}public String getSession() {return session;}public void setSession(String session) {this.session = session;}public String getRemote_addr() {return remote_addr;}public void setRemote_addr(String remote_addr) {this.remote_addr = remote_addr;}public String getInTime() {return inTime;}public void setInTime(String inTime) {this.inTime = inTime;}public String getOutTime() {return outTime;}public void setOutTime(String outTime) {this.outTime = outTime;}public String getInPage() {return inPage;}public void setInPage(String inPage) {this.inPage = inPage;}public String getOutPage() {return outPage;}public void setOutPage(String outPage) {this.outPage = outPage;}public String getReferal() {return referal;}public void setReferal(String referal) {this.referal = referal;}public int getPageVisits() {return pageVisits;}public void setPageVisits(int pageVisits) {this.pageVisits = pageVisits;}@Overridepublic void readFields(DataInput in) throws IOException {this.session = in.readUTF();this.remote_addr = in.readUTF();this.inTime = in.readUTF();this.outTime = in.readUTF();this.inPage = in.readUTF();this.outPage = in.readUTF();this.referal = in.readUTF();this.pageVisits = in.readInt();}@Overridepublic void write(DataOutput out) throws IOException {out.writeUTF(session);out.writeUTF(remote_addr);out.writeUTF(inTime);out.writeUTF(outTime);out.writeUTF(inPage);out.writeUTF(outPage);out.writeUTF(referal);out.writeInt(pageVisits);}@Overridepublic String toString() {return session + "\001" + remote_addr + "\001" + inTime + "\001" + outTime + "\001" + inPage + "\001" + outPage + "\001" + referal + "\001" + pageVisits;}
}

4、ClickStreamVisit

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;import org.apache.commons.beanutils.BeanUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;/*** 输入数据:pageviews模型结果数据* 从pageviews模型结果数据中进一步梳理出visit模型* sessionid  start-time   out-time   start-page   out-page   pagecounts  ......* * @author**/
public class ClickStreamVisit {// 以session作为key,发送数据到reducerstatic class ClickStreamVisitMapper extends Mapper<LongWritable, Text, Text, PageViewsBean> {PageViewsBean pvBean = new PageViewsBean();Text k = new Text();@Overrideprotected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {String line = value.toString();String[] fields = line.split("\001");int step = Integer.parseInt(fields[5]);//(String session, String remote_addr, String timestr, String request, int step, String staylong, String referal, String useragent, String bytes_send, String status)//299d6b78-9571-4fa9-bcc2-f2567c46df3472.46.128.140-2013-09-18 07:58:50/hadoop-zookeeper-intro/160"https://www.google.com/""Mozilla/5.0"14722200pvBean.set(fields[0], fields[1], fields[2], fields[3],fields[4], step, fields[6], fields[7], fields[8], fields[9]);k.set(pvBean.getSession());context.write(k, pvBean);}}static class ClickStreamVisitReducer extends Reducer<Text, PageViewsBean, NullWritable, VisitBean> {@Overrideprotected void reduce(Text session, Iterable<PageViewsBean> pvBeans, Context context) throws IOException, InterruptedException {// 将pvBeans按照step排序ArrayList<PageViewsBean> pvBeansList = new ArrayList<PageViewsBean>();for (PageViewsBean pvBean : pvBeans) {PageViewsBean bean = new PageViewsBean();try {BeanUtils.copyProperties(bean, pvBean);pvBeansList.add(bean);} catch (Exception e) {e.printStackTrace();}}Collections.sort(pvBeansList, new Comparator<PageViewsBean>() {@Overridepublic int compare(PageViewsBean o1, PageViewsBean o2) {return o1.getStep() > o2.getStep() ? 1 : -1;}});// 取这次visit的首尾pageview记录,将数据放入VisitBean中VisitBean visitBean = new VisitBean();// 取visit的首记录visitBean.setInPage(pvBeansList.get(0).getRequest());visitBean.setInTime(pvBeansList.get(0).getTimestr());// 取visit的尾记录visitBean.setOutPage(pvBeansList.get(pvBeansList.size() - 1).getRequest());visitBean.setOutTime(pvBeansList.get(pvBeansList.size() - 1).getTimestr());// visit访问的页面数visitBean.setPageVisits(pvBeansList.size());// 来访者的ipvisitBean.setRemote_addr(pvBeansList.get(0).getRemote_addr());// 本次visit的referalvisitBean.setReferal(pvBeansList.get(0).getReferal());visitBean.setSession(session.toString());context.write(NullWritable.get(), visitBean);}}public static void main(String[] args) throws Exception {Configuration conf = new Configuration();Job job = Job.getInstance(conf);job.setJarByClass(ClickStreamVisit.class);job.setMapperClass(ClickStreamVisitMapper.class);job.setReducerClass(ClickStreamVisitReducer.class);job.setMapOutputKeyClass(Text.class);job.setMapOutputValueClass(PageViewsBean.class);job.setOutputKeyClass(NullWritable.class);job.setOutputValueClass(VisitBean.class);//		FileInputFormat.setInputPaths(job, new Path(args[0]));
//		FileOutputFormat.setOutputPath(job, new Path(args[1]));FileInputFormat.setInputPaths(job, new Path("d:/weblog/pageviews"));FileOutputFormat.setOutputPath(job, new Path("d:/weblog/visitout"));boolean res = job.waitForCompletion(true);System.exit(res?0:1);}}

脚本

#!/bin/bash#
# ===========================================================================
# 程序名称:     
# 功能描述:     点击流模型数据预处理
# 输入参数:     运行日期
# 目标路径:     /data/weblog/preprocess/input
# 数据源  :     /data/weblog/preprocess/output
# 创建日期:     2016-12-21
# 版本说明:     v1.0
# 代码审核:     
# 修改人名:
# 修改日期:
# 修改原因:
# 修改列表: 
# ===========================================================================#set java env
export JAVA_HOME=/home/hadoop/apps/jdk1.7.0_51
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH#set hadoop env
export HADOOP_HOME=/home/hadoop/apps/hadoop-2.6.1
export PATH=${HADOOP_HOME}/bin:${HADOOP_HOME}/sbin:$PATH#点击流pagevies模型预处理程序类名
click_pv_class="cn.xxx.bigdata.weblog.clickstream.ClickStreamPageView"
#点击流pagevies模型程序输入目录,即预处理输出结果目录
log_pre_output=/data/weblog/preprocess/output
#点击流pagevies模型预处理程序输出目录
click_pvout=/data/weblog/preprocess/click_pv_out#点击流visit模型预处理程序类名
click_visit_class="cn.xxx.bigdata.weblog.clickstream.ClickStreamVisit"#点击流visit模型预处理程序输入目录,即pagevies模型预处理程序输出目录  $click_pvout#点击流visit模型预处理程序输出目录
click_vstout=/data/weblog/preprocess/click_visit_out#获取时间信息
day_01=`date -d'-1 day' +%Y-%m-%d`
syear=`date --date=$day_01 +%Y`
smonth=`date --date=$day_01 +%m`
sday=`date --date=$day_01 +%d`#读取日志文件的目录,判断是否有当日待处理的目录(如:2016-03-18)
files=`hadoop fs -ls $log_pre_output | grep $day_01 | wc -l`
if [ $files -gt 0 ]; then
#提交mr任务job运行
echo "running..    hadoop jar weblog.jar $click_pv_class $log_pre_output/$day_01 $click_pvout/$day_01"
hadoop jar weblog.jar $click_pv_class $log_pre_output/$day_01 $click_pvout/$day_01
fiecho "pv处理运行结果: $?"
if [ $? -eq 0 ];then
#提交mr任务job运行
echo "running..    hadoop jar weblog.jar $click_visit_class $click_pvout $day_01 $click_vstout/$day_01"
hadoop jar weblog.jar $click_visit_class $click_pvout/$day_01 $click_vstout/$day_01
fi

效果

在这里插入图片描述

在这里插入图片描述

数据仓库设计

简述

维度建模基本概念:
1、事实表的设计是以能够正确记录历史信息为准则;
2、维度表的设计是以能够以合适的角度来聚合主题内容为准则;

维度建模三种模式:
1、星型模式是以事实表为中心,所有的维度表直接连接在事实表上,像星星一样;
2、雪花模式(Snowflake Schema)是对星形模式的扩展。雪花模式的维度表可以拥有其他维度表的,性能方面需要关联多层维表,性能也比星型模型要低。所以一般不是很常用;
3、星座模式是星型模式延伸而来,星型模式是基于一张事实表的,而星座模式是基于多张事实表的,而且共享维度信息。在业务发展后期,绝大部分维度建模都采用的是星座模式;

表结构

本项目中数据仓库的设计采用星型模型。
1、事实表

原始数据表: ods_weblog_origin =>对应mr清洗完之后的数据
valid	string	是否有效
remote_addr	string	访客ip
remote_user	string	访客用户信息
time_local	string	请求时间
request	string	请求url
status	string	响应码
body_bytes_sent	string	响应字节数
http_referer	string	来源url
http_user_agent	string	访客终端信息访问日志明细宽表:dw_weblog_detail
valid	string	是否有效
remote_addr	string	访客ip
remote_user	string	访客用户信息
time_local	string	请求完整时间
daystr	string	访问日期
timestr	string	访问时间
month	string	访问月
day	string	访问日
hour	string	访问时
request	string	请求url整串
status	string	响应码
body_bytes_sent	string	响应字节数
http_referer	string	来源url
ref_host	string	来源的host
ref_path	string	来源的路径
ref_query	string	来源参数query
ref_query_id	string	来源参数query值
http_user_agent	string	客户终端标识

2、维度表

时间维度 t_dim_time
date_Key
year
month
day
hour访客地域维度t_dim_area
area_ID
北京
上海
广州
深圳终端类型维度t_dim_termination
uc
firefox
chrome
safari
ios
android网站栏目维度 t_dim_section
跳蚤市场
房租信息
休闲娱乐
建材装修
本地服务
人才市场

维度表的数据一般要结合业务情况自己写脚本按照规则生成,也可以使用工具生成,方便后续的关联分析。

实现

创建ODS层数据表(ods_weblog_origin,ods_click_stream_visit,ods_click_stream_visit)

1、原始日志数据表
drop table if exists ods_weblog_origin;
create table ods_weblog_origin(
valid string,
remote_addr string,
remote_user string,
time_local string,
request string,
status string,
body_bytes_sent string,
http_referer string,
http_user_agent string)
partitioned by (datestr string)
row format delimited
fields terminated by '\001';2、点击流模型pageviews表
drop table if exists ods_click_pageviews;
create table ods_click_pageviews(
session string,
remote_addr string,
remote_user string,
time_local string,
request string,
visit_step string,
page_staylong string,
http_referer string,
http_user_agent string,
body_bytes_sent string,
status string)
partitioned by (datestr string)
row format delimited
fields terminated by '\001';3、点击流visit模型表
drop table if exist ods_click_stream_visit;
create table ods_click_stream_visit(
session     string,
remote_addr string,
inTime      string,
outTime     string,
inPage      string,
outPage     string,
referal     string,
pageVisits  int)
partitioned by (datestr string)
row format delimited
fields terminated by '\001';

导入数据脚本(ods_weblog_origin,ods_click_stream_visit,ods_click_stream_visit)

点击流模型的两张表数据导入操作如下:

注:生产环境中应该将数据load命令,写在脚本中,然后配置在azkaban中定时运行,注意运行的时间点,应该在预处理数据完成之后。

#!/bin/bash#
# ===========================================================================
# 程序名称:     
# 功能描述:     加载数据到ODS
# 输入参数:     运行日期
# 数据路径:     /data/weblog/preprocess/output
# 目标hive:     ods_weblog_orgin
# 创建日期:     2016-12-21
# 版本说明:     v1.0
# 代码审核:     
# 修改人名:
# 修改日期:
# 修改原因:
# 修改列表: 
# ===========================================================================#set java env
export JAVA_HOME=/home/hadoop/apps/jdk1.7.0_51
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH#set hadoop env
export HADOOP_HOME=/home/hadoop/apps/hadoop-2.6.1
export PATH=${HADOOP_HOME}/bin:${HADOOP_HOME}/sbin:$PATH#获取时间信息
day_01=`date -d'-1 day' +%Y-%m-%d`
syear=`date --date=$day_01 +%Y`
smonth=`date --date=$day_01 +%m`
sday=`date --date=$day_01 +%d`#预处理输出结果(raw)目录
log_pre_output=/data/weblog/preprocess/output
#点击流pagevies模型预处理程序输出目录
click_pvout="/data/weblog/preprocess/click_pv_out"
#点击流visit模型预处理程序输出目录
click_vstout="/data/weblog/preprocess/click_visit_out"#目标hive表
ods_weblog_origin="shizhan.ods_weblog_origin"
ods_click_pageviews="shizhan.ods_click_pageviews"
ods_click_visit="shizhan.ods_click_visit"#导入raw数据到zs.ods_weblog_origin
HQL_origin="load data inpath '$log_pre_output/$day_01' into table $ods_weblog_origin partition(datestr='$day_01')"
echo $HQL_origin 
/home/hadoop/apps/hive/bin/hive -e "$HQL_origin"#导入点击流模型pageviews数据到
HQL_pvs="load data inpath '$click_pvout/$day_01' into table $ods_click_pageviews partition(datestr='$day_01')"
echo $HQL_pvs 
/home/hadoop/apps/hive/bin/hive -e "$HQL_pvs"#导入点击流模型visit数据到
HQL_vst="load data inpath '$click_vstout/$day_01' into table $ods_click_visit partition(datestr='$day_01')"
echo $HQL_vst 
/home/hadoop/apps/hive/bin/hive -e "$HQL_vst"

流量统计分析

通过上面三张表的数据查询来统计需要的数据

分析示例

1、按时间分析

--计算该处理批次(一天)中的各小时pvs
drop table dw_pvs_everyhour_oneday;
create table dw_pvs_everyhour_oneday(month string,day string,hour string,pvs bigint) partitioned by(datestr string);insert into table dw_pvs_everyhour_oneday partition(datestr='20130918')
select a.month as month,a.day as day,a.hour as hour,count(*) as pvs from ods_weblog_detail a
where  a.datestr='20130918' group by a.month,a.day,a.hour;--计算每天的pvs
drop table dw_pvs_everyday;
create table dw_pvs_everyday(pvs bigint,month string,day string);insert into table dw_pvs_everyday
select count(*) as pvs,a.month as month,a.day as day from ods_weblog_detail a
group by a.month,a.day;

2、按终端分析

数据中能够反映出用户终端信息的字段是http_user_agent。
User Agent也简称UA。它是一个特殊字符串头,是一种向访问网站提供所使用的浏览器类型及版本、操作系统及版本、浏览器内核、等信息的标识。

select distinct(http_user_agent) from ods_weblog_detail where http_user_agent like '%Chrome%' limit 200;

3、按栏目分析
网站栏目可以理解为网站中内容相关的主题集中。体现在域名上来看就是不同的栏目会有不同的二级目录。比如某网站网址为www.xxxx.cn,旗下栏目可以通过如下方式访问。

那么根据用户请求url就可以解析出访问栏目,然后按照栏目进行统计分析。

实现示例

创建ODS层明细宽表(ods_weblog_detail)

整个数据分析的过程是按照数据仓库的层次分层进行的,总体来说,是从ODS原始数据中整理出一些中间表,然后再在中间表的基础之上统计出各种指标数据。

明细表ods_weblog_detail:
drop table ods_weblog_detail;
create table ods_weblog_detail(
valid           string, --有效标识
remote_addr     string, --来源IP
remote_user     string, --用户标识
time_local      string, --访问完整时间
daystr          string, --访问日期
timestr         string, --访问时间
month           string, --访问月
day             string, --访问日
hour            string, --访问时
request         string, --请求的url
status          string, --响应码
body_bytes_sent string, --传输字节数
http_referer    string, --来源url
ref_host        string, --来源的host
ref_path        string, --来源的路径
ref_query       string, --来源参数query
ref_query_id    string, --来源参数query的值
http_user_agent string --客户终端标识
)
partitioned by(datestr string);

导入数据脚本(ods_weblog_detail)

#!/bin/bash
# . /home/anjianbing/soft/functions/wait4FlagFile.sh
# ===========================================================================
# 程序名称:     
# 功能描述:     抽取明细宽表
# 输入参数:     运行日期
# 目标表名:     shizhan.ods_weblog_detail
# 数据源表:     shizhan.ods_weblog_origin
# 创建日期:     2016-12-21
# 版本说明:     v1.0
# 代码审核:     
# 修改人名:
# 修改日期:
# 修改原因:
# 修改列表: 
# ===========================================================================
### 1.参数加载
exe_hive="/home/hadoop/apps/hive/bin/hive"
if [ $# -eq 1 ]
thenday_01=`date --date="${1}" +%Y-%m-%d`
elseday_01=`date -d'-1 day' +%Y-%m-%d`
fi
syear=`date --date=$day_01 +%Y`
smonth=`date --date=$day_01 +%m`
sday=`date --date=$day_01 +%d`TARGET_DB=shizhan
TARGET_TABLE=ods_weblog_detail### 2.定义执行HQL
HQL="
insert into table shizhan.ods_weblog_detail partition(datestr='$day_01')
select c.valid,c.remote_addr,c.remote_user,c.time_local,
substring(c.time_local,0,10) as daystr,
substring(c.time_local,12) as tmstr,
substring(c.time_local,6,2) as month,
substring(c.time_local,9,2) as day,
substring(c.time_local,11,3) as hour,
c.request,
c.status,
c.body_bytes_sent,
c.http_referer,
c.ref_host,
c.ref_path,
c.ref_query,
c.ref_query_id,
c.http_user_agent
from
(SELECT 
a.valid,
a.remote_addr,
a.remote_user,a.time_local,
a.request,a.status,a.body_bytes_sent,a.http_referer,a.http_user_agent,b.ref_host,b.ref_path,b.ref_query,b.ref_query_id 
FROM zs.ods_weblog_origin a 
LATERAL VIEW 
parse_url_tuple(regexp_replace(http_referer, \"\\\"\", \"\"), 'HOST', 'PATH','QUERY', 'QUERY:id') b 
as ref_host, ref_path, ref_query, ref_query_id) c
"#执行hql
$exe_hive -e "$HQL"#异常处理
#如果失败,发送邮件、短信

统计数据导出

为了将我们计算出来的数据通过报表的形式展现到前台页面上去,我们可以通过sqoop将我们计算后的数据导出到关系型数据库mysql当中去(通常计算之后的数据量一般都不会太大,可以考虑使用关系型数据库的方式来做我们的报表展现,如果统计之后的数据量仍然很大,那么就应该考虑使用大数据的技术来实现我们数据的展现)。
这里选择几张hive表进行导出,其他的所有的导出基本上都是一样。

Mysql创建表

CREATE DATABASE /*!32312 IF NOT EXISTS*/`weblog` /*!40100 DEFAULT CHARACTER SET utf8 */;USE `weblog`;/*Table structure for table `dw_pvs_everyday` */DROP TABLE IF EXISTS `dw_pvs_everyday`;CREATE TABLE `dw_pvs_everyday` (`pvs` varchar(32) DEFAULT NULL,`month` varchar(16) DEFAULT NULL,`day` varchar(16) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;/*Table structure for table `dw_pvs_everyhour_oneday` */DROP TABLE IF EXISTS `dw_pvs_everyhour_oneday`;CREATE TABLE `dw_pvs_everyhour_oneday` (`month` varchar(32) DEFAULT NULL,`day` varchar(32) DEFAULT NULL,`hour` varchar(32) DEFAULT NULL,`pvs` varchar(32) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;/*Table structure for table `dw_pvs_referer_everyhour` */DROP TABLE IF EXISTS `dw_pvs_referer_everyhour`;CREATE TABLE `dw_pvs_referer_everyhour` (`refer_url` varchar(2048) DEFAULT NULL,`referer_host` varchar(64) DEFAULT NULL,`month` varchar(32) DEFAULT NULL,`day` varchar(32) DEFAULT NULL,`hour` varchar(32) DEFAULT NULL,`pv_referer_cnt` varchar(32) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Sqoop同步数据

/export/servers/sqoop-1.4.6-cdh5.14.0/bin/sqoop export 
--connect jdbc:mysql://192.168.1.106:3306/weblog 
--username root 
--password admin 
--m 1  
--export-dir /user/hive/warehouse/weblog.db/dw_pvs_everyday  
--table dw_pvs_everyday  
--input-fields-terminated-by '\001'/export/servers/sqoop-1.4.6-cdh5.14.0/bin/sqoop export 
--connect jdbc:mysql://192.168.1.106:3306/weblog 
--username root 
--password admin 
--m 1  
--export-dir /user/hive/warehouse/weblog.db/dw_pvs_everyhour_oneday/datestr=20130918  
--table dw_pvs_everyhour_oneday  
--input-fields-terminated-by '\001' /export/servers/sqoop-1.4.6-cdh5.14.0/bin/sqoop export 
--connect jdbc:mysql://192.168.1.106:3306/weblog 
--username root 
--password admin 
--m 1  
--export-dir /user/hive/warehouse/weblog.db/dw_pvs_referer_everyhour/datestr=20130918  
--table dw_pvs_referer_everyhour  
--input-fields-terminated-by '\001' 

Azkaban工作流调度

整个项目的数据按照处理过程,从数据采集到数据分析,再到结果数据的导出,一系列的任务可以分割成若干个azkaban的job单元,然后由工作流调度器调度执行。

调度脚本的编写难点在于shell脚本。但是一般都是有固定编写模式。

开发工具类

public class DateUtil  {/*** 获取昨日的日期* @return*/public static String getYestDate(){Calendar instance = Calendar.getInstance();instance.add(Calendar.DATE,-1);Date time = instance.getTime();String format = new SimpleDateFormat("yyyy-MM-dd").format(time);return format;}
}

定义数据每日上传的目录

hdfs dfs -mkdir -p /weblog/20180205/input
hdfs dfs -put access.log.fensi  /weblog/20180205/input

根据上面目录改造执行编写的MR程序

1、WebLogProcessor
在这里插入图片描述
2、ClickStreamPageView
在这里插入图片描述
3、ClickStreamVisit
在这里插入图片描述

程序打成jar包

开发azkaban调度脚本

初始化脚本

create database if not exist weblog;
use weblog;# 原始日志数据表
drop table if exists ods_weblog_origin;
create table ods_weblog_origin(
valid string,
remote_addr string,
remote_user string,
time_local string,
request string,
status string,
body_bytes_sent string,
http_referer string,
http_user_agent string)
partitioned by (datestr string)
row format delimited
fields terminated by '\001';# 点击流模型pageviews表
drop table if exists ods_click_pageviews;
create table ods_click_pageviews(
session string,
remote_addr string,
remote_user string,
time_local string,
request string,
visit_step string,
page_staylong string,
http_referer string,
http_user_agent string,
body_bytes_sent string,
status string)
partitioned by (datestr string)
row format delimited
fields terminated by '\001';# 点击流visit模型表
drop table if exist ods_click_stream_visit;
create table ods_click_stream_visit(
session     string,
remote_addr string,
inTime      string,
outTime     string,
inPage      string,
outPage     string,
referal     string,
pageVisits  int)
partitioned by (datestr string)
row format delimited
fields terminated by '\001';# 明细宽表
drop table ods_weblog_detail;
create table ods_weblog_detail(
valid           string, --有效标识
remote_addr     string, --来源IP
remote_user     string, --用户标识
time_local      string, --访问完整时间
daystr          string, --访问日期
timestr         string, --访问时间
month           string, --访问月
day             string, --访问日
hour            string, --访问时
request         string, --请求的url
status          string, --响应码
body_bytes_sent string, --传输字节数
http_referer    string, --来源url
ref_host        string, --来源的host
ref_path        string, --来源的路径
ref_query       string, --来源参数query
ref_query_id    string, --来源参数query的值
http_user_agent string --客户终端标识
)
partitioned by(datestr string);

第一个MR程序执行(1.job)

type=command
command=/export/servers/hadoop-2.6.0-cdh5.14.0/bin/hadoop jar weblogparser.jar cn.xxx.bigdata.weblog.pre.WeblogPreProcess

第二个MR程序执行(2.job)

type=command
command=/export/servers/hadoop-2.6.0-cdh5.14.0/bin/hadoop jar weblogparser.jar cn.xxx.bigdata.weblog.clickstream.ClickStreamPageView
dependencies=1

第二个MR程序执行(3.job)

type=command
command=/export/servers/hadoop-2.6.0-cdh5.14.0/bin/hadoop jar weblogparser.jar cn.xxx.bigdata.weblog.clickstream.ClickStreamVisit
dependencies=2

hive表数据加载

1、4.sh

#!/bin/bash#set java env
export JAVA_HOME=/export/servers/jdk1.8.0_141
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH#set hadoop env
export HADOOP_HOME=/export/servers/hadoop-2.6.0-cdh5.14.0
export PATH=${HADOOP_HOME}/bin:${HADOOP_HOME}/sbin:$PATH#获取时间信息
day_01=`date -d'-1 day' +%Y%m%d`
syear=`date --date=$day_01 +%Y`
smonth=`date --date=$day_01 +%m`
sday=`date --date=$day_01 +%d`#预处理输出结果(raw)目录
log_pre_output="/weblog/'$day_01'/weblogPreOut"
#点击流clickStream模型预处理程序输出目录
pageView="/weblog/'$day_01'/pageViewOut"
#点击流visit模型预处理程序输出目录
visit="/weblog/'$day_01'/clickStreamVisit"#目标hive表 ,需要提前创建好着三张表,参考
ods_weblog_origin="weblog.ods_weblog_origin"
ods_click_pageviews="weblog.ods_click_pageviews"
ods_click_stream_visit="weblog.ods_click_stream_visit"#导入数据到weblog.ods_weblog_origin
HQL_origin="load data inpath '$log_pre_output' overwrite into table $ods_weblog_origin partition(datestr='$day_01')"
echo $HQL_origin 
/export/servers/hive-1.1.0-cdh5.14.0/bin/hive -e "$HQL_origin"#导入点击流模型pageviews数据到
HQL_pvs="load data inpath '$pageView' overwrite into table ods_click_pageviews partition(datestr='$day_01')"
echo $HQL_pvs 
/export/servers/hive-1.1.0-cdh5.14.0/bin/hive -e "$HQL_pvs"#导入点击流模型visit数据到
HQL_vst="load data inpath '$visit' overwrite into table $ods_click_stream_visit partition(datestr='$day_01')"
echo $HQL_vst 
/export/servers/hive-1.1.0-cdh5.14.0/bin/hive -e "$HQL_vst"

2、4.job

type=command
command=sh 4.sh
dependencies=3

hive表数据分析

1、5.sql

create database if not exist weblog;
use weblog;-- 1.1.1 计算该处理批次(一天)中的各小时pvs
drop table if exists dw_pvs_everyhour_oneday;
create table if not exists dw_pvs_everyhour_oneday(month string,day string,hour string,pvs bigint) partitioned by(datestr string);insert  into table dw_pvs_everyhour_oneday partition(datestr='20130918')
select a.month as month,a.day as day,a.hour as hour,count(*) as pvs from ods_weblog_detail a
where  a.datestr='20130918' group by a.month,a.day,a.hour;-- 计算每天的pvs
drop table if exists dw_pvs_everyday;
create table if not  exists dw_pvs_everyday(pvs bigint,month string,day string);insert into table dw_pvs_everyday select count(*) as pvs,a.month as month,a.day as day from ods_weblog_detail a group by a.month,a.day;-- 统计每小时各来访url产生的pv量,查询结果存入:( "dw_pvs_referer_everyhour" )drop table if exists dw_pvs_referer_everyhour;
create table if not exists dw_pvs_referer_everyhour (referer_url string,referer_host string,month string,day string,hour string,pv_referer_cnt bigint) partitioned by(datestr string);insert into table dw_pvs_referer_everyhour partition(datestr='20130918') select http_referer,ref_host,month,day,hour,count(1) as pv_referer_cnt from ods_weblog_detail  group by http_referer,ref_host,month,day,hour  having ref_host is not null order by hour asc,day asc,month asc,pv_referer_cnt desc;-- 统计每小时各来访host的产生的pv数并排序
drop table if exists dw_pvs_refererhost_everyhour;
create table if not exists dw_pvs_refererhost_everyhour(ref_host string,month string,day string,hour string,ref_host_cnts bigint) partitioned by(datestr string);insert into table dw_pvs_refererhost_everyhour partition(datestr='20130918') select ref_host,month,day,hour,count(1) as ref_host_cnts from ods_weblog_detail  group by ref_host,month,day,hour  having ref_host is not null order by hour asc,day asc,month asc,ref_host_cnts desc;

2、5.job

type=command
command=/export/servers/hive-1.1.0-cdh5.14.0/bin/hive -f "5.sql"
dependencies=4

分析结果通过sqoop导出

1、6.sh

#!/bin/bash#数据导出
/export/servers/sqoop-1.4.6-cdh5.14.0/bin/sqoop export --connect jdbc:mysql://192.168.25.25:3306/weblog --username root --password admin --m 1  --export-dir /user/hive/warehouse/weblog.db/dw_pvs_everyday  --table dw_pvs_everyday  --input-fields-terminated-by '\001'#数据导出
/export/servers/sqoop-1.4.6-cdh5.14.0/bin/sqoop export --connect jdbc:mysql://192.168.25.25:3306/weblog --username root --password admin --m 1  --export-dir /user/hive/warehouse/weblog.db/dw_pvs_everyhour_oneday/datestr=20130918  --table dw_pvs_everyhour_oneday  --input-fields-terminated-by '\001' #数据导出
/export/servers/sqoop-1.4.6-cdh5.14.0/bin/sqoop export --connect jdbc:mysql://192.168.25.25:3306/weblog --username root --password admin --m 1  --export-dir /user/hive/warehouse/weblog.db/dw_pvs_referer_everyhour/datestr=20130918  --table dw_pvs_referer_everyhour  --input-fields-terminated-by '\001' 

2、6.job

type=command
command=sh 6.sh
dependencies=5	

所有文件打包

在这里插入图片描述
weblogparser.jar源码参考:大数据_MR开发示例

定时执行

定于每天晚上两点钟定时开始执行任务

0 2 ? * *

数据可视化实现

ECharts是一款由百度前端技术部开发的,基于Javascript的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。

提供大量常用的数据可视化图表,底层基于ZRender(一个全新的轻量级canvas类库),创建了坐标系,图例,提示,工具箱等基础组件,并在此上构建出折线图(区域图)、柱状图(条状图)、散点图(气泡图)、饼图(环形图)、K线图、地图、力导向布局图以及和弦图,同时支持任意维度的堆积和多图表混合展现。

可单独构建一个项目。

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

相关文章

  1. 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

    方法1: 采用递归的思想 (1)先确定合成的新链表的头节点 (2) 再合并剩下的节点ListNode* Merge(ListNode* pHead1, ListNode* pHead2){if(pHead1==nullptr)return pHead2;if(pHead2==nullptr)return pHead1;//判断节点大小,取小节点(要删除的节点)ListNode* cur=pHead1-…...

    2024/4/26 6:28:26
  2. 了解线段树(C++)

    一.定义: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。 二.功能: 快速的查找某一个节点在若干条线段中出现的次数。(单点、区间的修改、查询) 三.时间复杂度: O(logN) 四.注意: 实际应用时一般要开4N的数组…...

    2024/4/10 12:57:22
  3. 智算之道初赛第三场---水杯

    水杯 小小 D 有一个能显示温度的杯子. 其原理是杯盖上的一个传感器. 只有在杯子内的水的体积大于等于某个数 L 的时候传感器才能显示水温,并且如果水温不在 [A,B]内传感器也无法显示水温. 注意,这里温度对水的体积没有影响 初始水杯为空,有 n 次操作,操作分为三种: 1 x 表示…...

    2024/4/10 12:57:21
  4. 数学三角形(简单dp)

    大家请先看题 题目描述 观察下面的数字金字塔。 写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 输入格式 第一个行一个正整数 rr ,表示行的数目。 后面每行为这个数…...

    2024/5/6 2:27:02
  5. 设置eMMC和DDR的工作频率

    1、MTK 平台查看eMMC和DDR的工作频率eMMC:adb shell cat /sys/kernel/debug/mmc0/clockDDR:adb shell cat /sys/bus/platform/drivers/emi_clk_test/read_dram_data_rate补充说明:cat read_dram_data_rate节点显示的是实时的dramc频率,由于相关平台可能默认开启了DVFS功能,…...

    2024/4/23 11:25:43
  6. Java采用的字符集编码——Unicode(标准码)

    Ⅰ 什么是字符集? 字符:字符(Char)是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。 字符集:字符集(Charset)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同。 Ⅱ 有哪些常用的字符集? 1. ASCII 7位(bits)表示一个字符,共128字…...

    2024/4/19 6:32:07
  7. 全网Star最多(近20k)的Spring Boot开源教程 2019 年要继续更新了!

    从2016年1月开始写博客,默默地更新《Spring Boot系列教程》,从无人问津到千万访问,作为一个独立站点(http://blog.didispace.com),相信只有那些跟我一样,坚持维护自己独立博客的童鞋才能体会这有多么不容易。 由于没有CSDN、博客园这样的权重优势,各种发布于这些平台上…...

    2024/4/12 23:31:24
  8. Linux安装Docker 阿里云CentOs7

    在阿里云ecs上安装docker docker官方提供的安装指导 手动安装 https://docs.docker.com/engine/install/centos/ 卸载旧版本 $ sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \dock…...

    2024/5/5 4:54:10
  9. SpringBoot实现过滤器、拦截器与切片

    正文 Q:使用过滤器、拦截器与切片实现每个请求耗时的统计,并比较三者的区别与联系过滤器Filter过滤器概念Filter是J2E中来的,可以看做是Servlet的一种“加强版”,它主要用于对用户请求进行预处理和后处理,拥有一个典型的处理链。Filter也可以对用户请求生成响应,这一点与…...

    2024/4/10 12:28:05
  10. 深度解析商业债务催收,与个人债务催收有哪些不同

    当您听到“收债”这个词时,您可能会感到有那么一点不安,因为它会让人想起那些不厌其烦的催收电话,以及各种不愉快的沟通。您第一时间想到的可能都是消费者的债务催收。其实,对个人的债务催收和商业债务催收之间有很大的不同:消费者债务催收是指与消费者打交道的企业,如消费…...

    2024/4/18 12:29:06
  11. 动词5【流】

    复合动词流れ込む复合名词気象庁によりますと、梅雨前線が西日本から東北に停滞し、暖かく湿った空気が流れ込んでいるため、広い範囲で大気の状態が不安定になっていて、発達した雨雲がかかっているところがあります。 据气象厅消息,由于梅雨锋从西日本停在东北,温暖潮湿的空…...

    2024/4/17 6:37:05
  12. 标题基于django框架的模型能力封装:将模型部署在docker中提供WebAPI的服务

    标题基于django框架的模型能力封装:将模型部署在docker中提供WebAPI的服务 近刚刚入职,公司要我将做好的能力部署在docker中,之前从来没有接触过,在网上也没有教程,在这里我分享并记录一下我的学习成果,希望对有需求的人有所帮助。 展示API的使用方法 首先,我们需要知道…...

    2024/4/28 16:08:33
  13. rocketmq消峰之流量控制详解---实战 三

    rocketMq消费端消费分数以上三个步骤: 第一: 消费端从rocketMq服务端pull消息,到本地。 第二: 消费端消费pull到的消息。 第三: 消费消费结束后,回复Ack到rocketMq,偏移消费位置。 代码:/*** 测试mq 并发 接受*/@Component@RocketMQMessageListener(topic = ConstantTo…...

    2024/4/27 2:35:30
  14. 1. mysql安装

    1. 单实例安装 安装环境为CentOS 7、mysql-5.7.9-linux-glibc2.5-x86_64.tar。 mysql安装包从官网下载,或者见网盘分享:链接: https://pan.baidu.com/s/174C_hIQ5tjAgMQJ5oB2uzw 提取码: g7f2 拷贝安装包到/usr/local目录。 groupadd mysql useradd -r -g mysql mysql cd /us…...

    2024/4/17 21:32:52
  15. 查看github.io上的html文件

    参考:如何查看github中html页面 ~.github.io上写了许多学者的个人简历,如何查看呢?在网址前加上“http://htmlpreview.github.com/?” 如: https://github.com/vito-o/author/blob/master/index.html 加上后是: http://htmlpreview.github.com/?https://github.com/vito…...

    2024/4/23 5:53:20
  16. 0-1背包问题

    0-1背包问题 有 N件物品和一个容量是 V背包。每件物品只能使用一次。 第 i 件物品的体积是 vi,价值是 wi。 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。 输入格式 第一行两个整数,***N,V***用空格隔开,分别表示物品数量和…...

    2024/4/20 1:41:39
  17. 20200724-异常

    课堂笔记: Throwable:所以能导致程序无法运行的根类 Error:错误:无法恢复的 Exception:异常:处理之后能正常运行的 确定会发生异常的情况: Checked异常:确定的异常,提前处理 不确定: Runtime异常:运行期异常,RuntimeException,遇到了再解决 常见的异常类型: 算术异常…...

    2024/5/3 0:42:06
  18. 使用watch监听导航切换来控制导航栏的有无

    1、新建底部导航页面,bottomBar.vue 2、在App.vue中引入底部导航,通过watch去监听路由跳转 <template><div id="app"><router-view></router-view><bottom-bar v-if="show"></bottom-bar></div> </templat…...

    2024/4/27 8:46:35
  19. 扫描/渗透测试工具:Nmap从入门到精通

    文章目录扫描/渗透测试工具:Nmap从入门到精通下面是一些Nmap基本的命令和它们的用法的例子:1、扫描单一的一个主机,命令如下:2、扫描整个子网,命令如下:3、扫描多个目标,命令如下:4、扫描一个范围内的目标,如下:5、如果你有一个ip地址列表,将这个保存为一个txt文件,…...

    2024/4/23 16:02:32
  20. LeetCode 面试题 05.07. Exchange LCCI

    原题目:https://leetcode-cn.com/problems/exchange-lcci/思路:分别得到奇数位(num&0x55555555)和偶数位(num&0xaaaaaaaa)。然后奇数位右移,偶数位左移,最后加和即可。代码:class Solution { public:int exchangeBits(int num) {// 分别得到奇数位,和偶数位。…...

    2024/4/28 21:14:58

最新文章

  1. Android 10.0 Launcher3 app页面调整workspace边距app行距变小功能实现

    1.前言 在10.0的系统rom定制化开发中,在launcher3的一些开发定制功能中,在对于大分辨率比如1600*2560的设备进行开发的时候, 会在竖屏的时候,在默认7*4的布局的时候,显得行距有点宽,这样就需要调整整个CellLayout的上下左右边距,然后就 会显得行距会小一点,接下来具体…...

    2024/5/6 7:26:01
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. C++ 【原型模式】

    简单介绍 原型模式是一种创建型设计模式 | 它使你能够复制已有对象&#xff0c;客户端不需要知道要复制的对象是哪个类的实例&#xff0c;只需通过原型工厂获取该对象的副本。 以后需要更改具体的类或添加新的原型类&#xff0c;客户端代码无需改变&#xff0c;只需修改原型工…...

    2024/5/5 8:37:55
  4. Oracle 正则表达式

    一、Oracle 正则表达式相关函数 (1) regexp_like &#xff1a;同 like 功能相似&#xff08;模糊 匹配&#xff09; (2) regexp_instr &#xff1a;同 instr 功能相似&#xff08;返回字符所在 下标&#xff09; (3) regexp_substr &#xff1a; 同 substr 功能相似&…...

    2024/5/5 8:52:36
  5. 416. 分割等和子集问题(动态规划)

    题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义&#xff1a;dp[i][j]表示当背包容量为j&#xff0c;用前i个物品是否正好可以将背包填满&#xff…...

    2024/5/5 18:19:03
  6. 【Java】ExcelWriter自适应宽度工具类(支持中文)

    工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...

    2024/5/5 12:22:20
  7. Spring cloud负载均衡@LoadBalanced LoadBalancerClient

    LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon&#xff0c;直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件&#xff0c;我们讨论Spring负载均衡以Spring Cloud2020之后版本为主&#xff0c;学习Spring Cloud LoadBalance&#xff0c;暂不讨论Ribbon…...

    2024/5/5 19:59:54
  8. TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案

    一、背景需求分析 在工业产业园、化工园或生产制造园区中&#xff0c;周界防范意义重大&#xff0c;对园区的安全起到重要的作用。常规的安防方式是采用人员巡查&#xff0c;人力投入成本大而且效率低。周界一旦被破坏或入侵&#xff0c;会影响园区人员和资产安全&#xff0c;…...

    2024/5/6 7:24:07
  9. VB.net WebBrowser网页元素抓取分析方法

    在用WebBrowser编程实现网页操作自动化时&#xff0c;常要分析网页Html&#xff0c;例如网页在加载数据时&#xff0c;常会显示“系统处理中&#xff0c;请稍候..”&#xff0c;我们需要在数据加载完成后才能继续下一步操作&#xff0c;如何抓取这个信息的网页html元素变化&…...

    2024/5/5 15:25:47
  10. 【Objective-C】Objective-C汇总

    方法定义 参考&#xff1a;https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...

    2024/5/6 6:01:13
  11. 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】

    &#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】&#x1f30f;题目描述&#x1f30f;输入格…...

    2024/5/6 7:24:06
  12. 【ES6.0】- 扩展运算符(...)

    【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数&#xff0…...

    2024/5/6 1:08:53
  13. 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?

    文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕&#xff0c;各大品牌纷纷晒出优异的成绩单&#xff0c;摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称&#xff0c;在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁&#xff0c;多个平台数据都表现出极度异常…...

    2024/5/5 18:50:00
  14. Go语言常用命令详解(二)

    文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令&#xff0c;这些命令可以帮助您在Go开发中进行编译、测试、运行和…...

    2024/5/6 0:27:44
  15. 用欧拉路径判断图同构推出reverse合法性:1116T4

    http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b&#xff0c;我们在 a i a_i ai​ 和 a i 1 a_{i1} ai1​ 之间连边&#xff0c; b b b 同理&#xff0c;则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然&#xff0…...

    2024/5/6 7:24:04
  16. 【NGINX--1】基础知识

    1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息&#xff0c;并安装一些有助于配置官方 NGINX 软件包仓库的软件包&#xff1a; apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...

    2024/5/6 7:24:04
  17. Hive默认分割符、存储格式与数据压缩

    目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限&#xff08;ROW FORMAT&#xff09;配置标准HQL为&#xff1a; ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...

    2024/5/5 13:14:22
  18. 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法

    文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中&#xff0c;传感器和控制器产生大量周…...

    2024/5/6 7:24:03
  19. --max-old-space-size=8192报错

    vue项目运行时&#xff0c;如果经常运行慢&#xff0c;崩溃停止服务&#xff0c;报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中&#xff0c;通过JavaScript使用内存时只能使用部分内存&#xff08;64位系统&…...

    2024/5/5 17:03:52
  20. 基于深度学习的恶意软件检测

    恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞&#xff0c;例如可以被劫持的合法软件&#xff08;例如浏览器或 Web 应用程序插件&#xff09;中的错误。 恶意软件渗透可能会造成灾难性的后果&#xff0c;包括数据被盗、勒索或网…...

    2024/5/5 21:10:50
  21. JS原型对象prototype

    让我简单的为大家介绍一下原型对象prototype吧&#xff01; 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定&#xff0c;每一个构造函数都有一个 prototype 属性&#xff0c;指向另一个对象&#xff0c;所以我们也称为原型对象…...

    2024/5/6 7:24:02
  22. C++中只能有一个实例的单例类

    C中只能有一个实例的单例类 前面讨论的 President 类很不错&#xff0c;但存在一个缺陷&#xff1a;无法禁止通过实例化多个对象来创建多名总统&#xff1a; President One, Two, Three; 由于复制构造函数是私有的&#xff0c;其中每个对象都是不可复制的&#xff0c;但您的目…...

    2024/5/6 7:24:01
  23. python django 小程序图书借阅源码

    开发工具&#xff1a; PyCharm&#xff0c;mysql5.7&#xff0c;微信开发者工具 技术说明&#xff1a; python django html 小程序 功能介绍&#xff1a; 用户端&#xff1a; 登录注册&#xff08;含授权登录&#xff09; 首页显示搜索图书&#xff0c;轮播图&#xff0…...

    2024/5/5 17:03:21
  24. 电子学会C/C++编程等级考试2022年03月(一级)真题解析

    C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...

    2024/5/5 15:25:31
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57