package com.spark.study.sparksql; import org.apache.spark.SparkConf; import org.apache.spark.api.java.JavaSparkContext; import org.apache.spark.sql.DataFrame; import org.apache.spark.sql.hive.HiveContext; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; /** * 新闻网站关键指标离线统计Spark作业 * * 案例需求分析 * * 每天每个页面的PV:PV是Page View,是指一个页面被所有用户访问次数的总和,页面被访问一次就被记录1次PV * 每天每个页面的UV:UV是User View,是指一个页面被多少个用户访问了,一个用户访问一次是1次UV,一个用户访问多次还是1次UV * 新用户注册比率:当天注册用户数 / 当天未注册用户的访问数 * 用户跳出率:IP只浏览了一个页面就离开网站的次数/网站总访问数(PV) * 版块热度排行榜:根据每个版块每天被访问的次数,做出一个排行榜 * * 网站日志格式 * date timestamp userid pageid section action * * 日志字段说明 * date: 日期,yyyy-MM-dd格式 * timestamp: 时间戳 * userid: 用户id * pageid: 页面id * section: 新闻版块 * action: 用户行为,两类,点击页面和注册 */ public class NewsOfflineStatSpark { public static void main(String[] args) { /** * // 一般来说,在小公司中,可能就是将我们的spark作业使用linux的crontab进行调度 * // 将作业jar放在一台安装了spark客户端的机器上,并编写了对应的spark-submit shell脚本 * // 在crontab中可以配置,比如说每天凌晨3点执行一次spark-submit shell脚本,提交一次spark作业 * // 一般来说,离线的spark作业,每次运行,都是去计算昨天的数据 * // 大公司总,可能是使用较为复杂的开源大数据作业调度平台,比如常用的有azkaban、oozie等 * // 但是,最大的那几个互联网公司,比如说BAT、美团、京东,作业调度平台,都是自己开发的 * // 我们就会将开发好的Spark作业,以及对应的spark-submit shell脚本,配置在调度平台上,几点运行 * // 同理,每次运行,都是计算昨天的数据 * * // 一般来说,每次spark作业计算出来的结果,实际上,大部分情况下,都会写入mysql等存储 * // 这样的话,我们可以基于mysql,用java web技术开发一套系统平台,来使用图表的方式展示每次spark计算 * // 出来的关键指标 * // 比如用折线图,可以反映最近一周的每天的用户跳出率的变化 * * // 也可以通过页面,给用户提供一个查询表单,可以查询指定的页面的最近一周的pv变化 * // date pageid pv * // 插入mysql中,后面用户就可以查询指定日期段内的某个page对应的所有pv,然后用折线图来反映变化曲线 * */ // 拿到昨天的日期,去hive表中,针对昨天的数据执行SQL语句 String yesterday = getYesterday(); // 创建SparkConf以及Spark上下文 SparkConf conf = new SparkConf() .setAppName("NewsOfflineStatSpark"); // .setMaster("local"); JavaSparkContext sc = new JavaSparkContext(conf); HiveContext hiveContext = new HiveContext(sc.sc()); // 开发第一个关键指标:页面pv统计以及排序 calculateDailyPagePv(hiveContext, yesterday); // 开发第二个关键指标:页面uv统计以及排序 calculateDailyPageUv(hiveContext, yesterday); // 开发第三个关键指标:新用户注册比率统计 calculateDailyNewUserRegisterRate(hiveContext, yesterday); // 开发第四个关键指标:用户跳出率统计 calculateDailyUserJumpRate(hiveContext, yesterday); // 开发第五个关键指标:版块热度排行榜 calculateDailySectionPvSort(hiveContext, yesterday); // 关闭Spark上下文 sc.close(); } /** * 获取昨天的字符串类型的日期 * @return 日期 */ private static String getYesterday(){ Calendar cal = Calendar.getInstance(); cal.setTime(new Date()); cal.add(Calendar.DAY_OF_YEAR, -1); Date yesterday = cal.getTime(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); return sdf.format(yesterday); } /** * 计算每天每个页面的pv以及排序 */ private static void calculateDailyPagePv( HiveContext hiveContext, String date){ String sql = "SELECT " + "date," + "pageid," + "pv " +"FROM (" +"SELECT " + "date," + "pageid," + "count(*) pv " + "FROM news_access " + "WHERE action = view " + "AND date = '" + date + "' " + "GROUP BY date,pageid " +") t " +"ORDER BY pv DESC"; DataFrame df = hiveContext.sql(sql); df.show(); } /** * 计算每天每个页面的uv以及排序 * Spark SQL的count(distinct)语句,有bug,默认会产生严重的数据倾斜 * 只会用一个task,来做去重和汇总计数,性能很差 * @param hiveContext * @param date */ private static void calculateDailyPageUv( HiveContext hiveContext, String date){ String sql = "SELECT " + "date," + "pageid," + "uv " + "FROM (" +"SELECT " + "date," + "pageid," + "count(*) uv " + "FROM (" + "SELECT " + "date," + "pageid," + "userid " + "FROM news_access " + "WHERE action = view " + "AND date = '" + date + "' " + "GROUP BY date,pageid,userid " + ") t1 " + "GROUP BY date,pageid " + ") t2 " +"ORDER BY uv DESC"; DataFrame df = hiveContext.sql(sql); df.show(); } /** * 计算每天的新用户注册比例 * @param hiveContext * @param date */ private static void calculateDailyNewUserRegisterRate( HiveContext hiveContext,String date){ // 昨天所有访问行为中,userid为null,新用户的访问总数 String sql1 = "SELECT count(*) FROM news_access WHERE action='view' AND date='" + date + "' AND userid IS NULL"; // 昨天的总注册用户数 String sql2 = "SELECT count(*) FROM news_access WHERE action='register' AND date='" + date + "' "; Object result1 = hiveContext.sql(sql1).collect()[0].get(0); long number1 = 0L; if(result1 != null){ number1 = Long.valueOf(String.valueOf(result1)); } Object result2 = hiveContext.sql(sql2).collect()[0].get(0); long number2 = 0L; //Long,包装类0L\null都行 if(result2 != null) { number2 = Long.valueOf(String.valueOf(result2)); } // 计算结果 System.out.println("======================" + number1 + "======================"); System.out.println("======================" + number2 + "======================"); double rate = (double)number2 / (double)number1; //long先转double再计算 System.out.println("======================" + formatDouble(rate, 2) + "======================"); } /** * 计算每天的用户跳出率 * @param hiveContext * @param date */ private static void calculateDailyUserJumpRate( HiveContext hiveContext, String date) { // 计算已注册用户的昨天的总的访问pv String sql1 = "SELECT count(*) FROM news_access WHERE action='view' AND date='" + date + "' AND userid IS NOT NULL "; // 已注册用户的昨天跳出的总数 String sql2 = "SELECT count(*) FROM ( SELECT count(*) cnt FROM news_access WHERE action='view' AND date='" + date + "' AND userid IS NOT NULL GROUP BY userid HAVING cnt=1 ) t "; // 执行两条SQL,获取结果 Object result1 = hiveContext.sql(sql1).collect()[0].get(0); long number1 = 0L; if(result1 != null) { number1 = Long.valueOf(String.valueOf(result1)); } Object result2 = hiveContext.sql(sql2).collect()[0].get(0); long number2 = 0L; if(result2 != null) { number2 = Long.valueOf(String.valueOf(result2)); } // 计算结果 System.out.println("======================" + number1 + "======================"); System.out.println("======================" + number2 + "======================"); double rate = (double)number2 / (double)number1; System.out.println("======================" + formatDouble(rate, 2) + "======================"); } /** * 计算每天的版块热度排行榜 * @param hiveContext * @param date */ private static void calculateDailySectionPvSort( HiveContext hiveContext, String date) { String sql = "SELECT " + "date," + "section," + "pv " + "FROM ( " + "SELECT " + "date," + "section," + "count(*) pv " + "FROM news_access " + "WHERE action='view' " + "AND date='" + date + "' " + "GROUP BY date,section " + ") t " + "ORDER BY pv DESC "; DataFrame df = hiveContext.sql(sql); df.show(); } /** * 格式化小数 * @param scale 四舍五入的位数 * @return 格式化小数 */ private static double formatDouble(double num, int scale){ BigDecimal bd = new BigDecimal(num); return bd.setScale(scale,BigDecimal.ROUND_HALF_UP).doubleValue(); } }
新闻网站关键指标离线统计Spark作业
最新推荐文章于 2021-04-23 17:28:01 发布
该博客展示了如何使用Spark SQL对新闻网站的关键指标进行离线统计,包括每日页面PV、UV、新用户注册比率、用户跳出率和版块热度排行榜。通过编写Spark作业,从Hive表中读取数据并执行SQL查询来计算这些指标。
摘要由CSDN通过智能技术生成