Spark日志分析项目Demo(5)--自定义Accumulator
来源:互联网 发布:矩阵相似性度量 编辑:程序博客网 时间:2024/05/16 04:17
累加器(accumulator)
我们传递给Spark的函数,如map(),或者filter()的判断条件函数,能够利用定义在函数之外的变量,但是集群中的每一个task都会得到变量的一个副本,并且task在对变量进行的更新不会被返回给driver。而Spark的两种共享变量:累加器(accumulator)和广播变量(broadcast variable),在广播和结果聚合这两种常见类型的通信模式上放宽了这种限制。
使用累加器可以很简便地对各个worker返回给driver的值进行聚合。累加器最常见的用途之一就是对一个job执行期间发生的事件进行计数。例如,当我们统计输入文件信息时,有时需要统计空白行的数量。下面的scala程序描述了这个过程。
import org.apache.spark.{SparkContext, SparkConf}object AccumulatorTest{ def main(args: Array[String]) { val conf = new SparkConf() .setMaster("local") .setAppName("accumulatrorTest") .setJars(List("E:\\IdeaProjects\\SparkExercise\\out\\artifacts\\SparkExercise_jar\\SparkExercise.jar")) val sc = new SparkContext(conf) val file = sc.textFile("E:\\file.txt") val blankLines = sc.accumulator(0) // Create an Accumulator[Int] initialized to 0,task会得到blankLines的一个副本,且每个task对它的更新不会返回给driver val callSigns = file.flatMap(line => { if (line == "") { blankLines += 1 // Add to the accumulator } line.split(" ") }) callSigns.saveAsTextFile("E:\\output.txt") println("Blank lines: " + blankLines) sc.stop() }}
在上面的例子中,我们创建了一个名为blankLines的整型累加器(Accumulator[Int]),初始化为0,然后再每次读到一个空白行的时候blankLines加一。因此,累加器使我们可以用一种更简便的方式,在一个RDD的转换过程中对值进行聚合,而不用额外使用一个filter()或reduce()操作。
需要注意的是,由于Spark的lazy机制,只有在saveAsTestFile这个action算子执行后我们才能得到blankLines的正确结果。
由于对于worker节点来说,累加器的值是不可访问的,所有对于worker上的task,累加器是write-only的。这使得累加器可以被更高效的实现,而不需要在每次更新时都进行通信。
自定义累加器
上面的例子中使用的是Spark内建的Integer类型累加器。同时,Spark还支持Double,Long,和Float类型的累加器。除此之外,Spark还提供了自定义累加器类型和聚合操作(如查找最大值等加操作以外的操作)的API,但要保证定义的操作满足交换律和结合律。
下载介绍项目中用到的自定义累加器,重载了方法zero(),add()
public class SessionAggrStatAccumulator implements AccumulatorParam<String> { private static final long serialVersionUID = 6311074555136039130L; /** * zero方法,其实主要用于数据的初始化 * 那么,我们这里,就返回一个值,就是初始化中,所有范围区间的数量,都是0 * 各个范围区间的统计数量的拼接,还是采用一如既往的key=value|key=value的连接串的格式 */ public String zero(String initialValue) { return Constants.SESSION_COUNT + "=0|" + Constants.TIME_PERIOD_1s_3s + "=0|" + Constants.TIME_PERIOD_4s_6s + "=0|" + Constants.TIME_PERIOD_7s_9s + "=0|" + Constants.TIME_PERIOD_10s_30s + "=0|" + Constants.TIME_PERIOD_30s_60s + "=0|" + Constants.TIME_PERIOD_1m_3m + "=0|" + Constants.TIME_PERIOD_3m_10m + "=0|" + Constants.TIME_PERIOD_10m_30m + "=0|" + Constants.TIME_PERIOD_30m + "=0|" + Constants.STEP_PERIOD_1_3 + "=0|" + Constants.STEP_PERIOD_4_6 + "=0|" + Constants.STEP_PERIOD_7_9 + "=0|" + Constants.STEP_PERIOD_10_30 + "=0|" + Constants.STEP_PERIOD_30_60 + "=0|" + Constants.STEP_PERIOD_60 + "=0"; } /** * addInPlace和addAccumulator * 可以理解为是一样的 * * 这两个方法,其实主要就是实现,v1可能就是我们初始化的那个连接串 * v2,就是我们在遍历session的时候,判断出某个session对应的区间,然后会用Constants.TIME_PERIOD_1s_3s * 所以,我们,要做的事情就是 * 在v1中,找到v2对应的value,累加1,然后再更新回连接串里面去 * */ public String addAccumulator(String v1, String v2) { return add(v1,v2); } public String addInPlace(String v1, String v2) { return add(v1,v2); } /** * session统计计算逻辑 * @param v1 连接串 * @param v2 范围区间 * @return 更新以后的连接串 */ private String add(String v1, String v2){ // 校验:v1为空的话,直接返回v2 if(StringUtils.isEmpty(v1)) { return v2; } // 使用StringUtils工具类,从v1中,提取v2对应的值,并累加1 String oldValue = StringUtils.getFieldFromConcatString(v1, "\\|", v2); if(oldValue != null) { // 将范围区间原有的值,累加1 int newValue = Integer.valueOf(oldValue) + 1; // 使用StringUtils工具类,将v1中,v2对应的值,设置成新的累加后的值 return StringUtils.setFieldInConcatString(v1, "\\|", v2, String.valueOf(newValue)); } return v1; }}
/** * 字符串工具类 * @author Administrator * */public class StringUtils { /** * 判断字符串是否为空 * @param str 字符串 * @return 是否为空 */ public static boolean isEmpty(String str) { return str == null || "".equals(str); } /** * 判断字符串是否不为空 * @param str 字符串 * @return 是否不为空 */ public static boolean isNotEmpty(String str) { return str != null && !"".equals(str); } /** * 截断字符串两侧的逗号 * @param str 字符串 * @return 字符串 */ public static String trimComma(String str) { if(str.startsWith(",")) { str = str.substring(1); } if(str.endsWith(",")) { str = str.substring(0, str.length() - 1); } return str; } /** * 补全两位数字 * @param str * @return */ public static String fulfuill(String str) { if(str.length() == 2) { return str; } else { return "0" + str; } } /** * 从拼接的字符串中提取字段 * @param str 字符串 * @param delimiter 分隔符 * @param field 字段 * @return 字段值 */ public static String getFieldFromConcatString(String str, String delimiter, String field) { try { String[] fields = str.split(delimiter); for(String concatField : fields) { // searchKeywords=|clickCategoryIds=1,2,3 if(concatField.split("=").length == 2) { String fieldName = concatField.split("=")[0]; String fieldValue = concatField.split("=")[1]; if(fieldName.equals(field)) { return fieldValue; } } } } catch (Exception e) { e.printStackTrace(); } return null; } /** * 从拼接的字符串中给字段设置值 * @param str 字符串 * @param delimiter 分隔符 * @param field 字段名 * @param newFieldValue 新的field值 * @return 字段值 */ public static String setFieldInConcatString(String str, String delimiter, String field, String newFieldValue) { String[] fields = str.split(delimiter); for(int i = 0; i < fields.length; i++) { String fieldName = fields[i].split("=")[0]; if(fieldName.equals(field)) { String concatField = fieldName + "=" + newFieldValue; fields[i] = concatField; break; } } StringBuffer buffer = new StringBuffer(""); for(int i = 0; i < fields.length; i++) { buffer.append(fields[i]); if(i < fields.length - 1) { buffer.append("|"); } } return buffer.toString(); }}
public class AccumuletorTest { public static void main(String[] args) { // 构建Spark上下文 SparkConf conf = new SparkConf() .setAppName("AccumuletorTest") .setMaster("local"); JavaSparkContext sc = new JavaSparkContext(conf); final Accumulator<String> sessionAggrStatAccumulator = sc.accumulator( "", new SessionAggrStatAccumulator()); List<Long> seq= new ArrayList<Long>(); for (int i = 0; i < 1000; i++) { seq.add(Long.valueOf((int) (Math.random() * 1000))); } JavaRDD<Long> rdd=sc.parallelize(seq); //必须用RDD执行 rdd.foreach(new VoidFunction<Long>() { public void call(Long aLong) throws Exception { sessionAggrStatAccumulator.add(Constants.SESSION_COUNT); calculateVisitLength(aLong); calculateStepLength(aLong); } /** * 计算访问时长范围 * @param visitLength */ private void calculateVisitLength(long visitLength) { if (visitLength >= 1 && visitLength <= 3) { sessionAggrStatAccumulator.add(Constants.TIME_PERIOD_1s_3s); } else if (visitLength >= 4 && visitLength <= 6) { sessionAggrStatAccumulator.add(Constants.TIME_PERIOD_4s_6s); } else if (visitLength >= 7 && visitLength <= 9) { sessionAggrStatAccumulator.add(Constants.TIME_PERIOD_7s_9s); } else if (visitLength >= 10 && visitLength <= 30) { sessionAggrStatAccumulator.add(Constants.TIME_PERIOD_10s_30s); } else if (visitLength > 30 && visitLength <= 60) { sessionAggrStatAccumulator.add(Constants.TIME_PERIOD_30s_60s); } else if (visitLength > 60 && visitLength <= 180) { sessionAggrStatAccumulator.add(Constants.TIME_PERIOD_1m_3m); } else if (visitLength > 180 && visitLength <= 600) { sessionAggrStatAccumulator.add(Constants.TIME_PERIOD_3m_10m); } else if (visitLength > 600 && visitLength <= 1800) { sessionAggrStatAccumulator.add(Constants.TIME_PERIOD_10m_30m); } else if (visitLength > 1800) { sessionAggrStatAccumulator.add(Constants.TIME_PERIOD_30m); } } /** * 计算访问步长范围 * @param stepLength */ private void calculateStepLength(long stepLength) { if (stepLength >= 1 && stepLength <= 3) { sessionAggrStatAccumulator.add(Constants.STEP_PERIOD_1_3); } else if (stepLength >= 4 && stepLength <= 6) { sessionAggrStatAccumulator.add(Constants.STEP_PERIOD_4_6); } else if (stepLength >= 7 && stepLength <= 9) { sessionAggrStatAccumulator.add(Constants.STEP_PERIOD_7_9); } else if (stepLength >= 10 && stepLength <= 30) { sessionAggrStatAccumulator.add(Constants.STEP_PERIOD_10_30); } else if (stepLength > 30 && stepLength <= 60) { sessionAggrStatAccumulator.add(Constants.STEP_PERIOD_30_60); } else if (stepLength > 60) { sessionAggrStatAccumulator.add(Constants.STEP_PERIOD_60); } } }); System.out.println("accumuletor2:"+sessionAggrStatAccumulator.value()); }}
- Spark日志分析项目Demo(5)--自定义Accumulator
- Spark日志分析项目Demo(3)--Spark入口和DataFrame
- Spark日志分析项目Demo(1)--Flume-ng的安装
- Spark日志分析项目Demo(9)--常规性能调优
- Spark日志分析项目Demo(10) --JVM调优
- spark自定义Accumulator高级应用(JAVA)
- Spark日志分析项目Demo(6)--页面单跳转化率分析
- 日志采集分析项目Demo
- Spark日志分析项目Demo(4)--RDD使用,用户行为统计分析
- Spark日志分析项目Demo(7)--临时表查询,各区域top3热门商品统计
- Spark日志分析项目Demo(8)--SparkStream,广告点击流量实时统计
- spark-broadcast&accumulator使用
- 【Spark Java API】broadcast、accumulator
- 【Spark Java API】broadcast、accumulator
- Spark Streaming Accumulator 并发问题
- Spark(Accumulator)陷阱及解决办法
- Spark 基于自定义分区的方式 分析点击流日志
- Spark-项目中分析日志的核心代码
- html-css练习题(天天生鲜静态网页制作)
- 和为S的两个数字
- java并发编程实践之共享对象学习笔记
- CentOS安装配置ClamAV
- Spark 报错scala.reflect.api.JavaUniverse.runtimeMirror
- Spark日志分析项目Demo(5)--自定义Accumulator
- Android息屏与亮屏
- React Native 环境搭建 初始化项目 运行 调试
- Python3之利用requests和BeautifulSoup抓取部分信息
- 完美解决Linux sublime Text中文无法输入的问题
- python -- 装饰器的高级应用
- Mac上[ERROR] docker-cli docker ps failed
- NYOJ 733 万圣节派对
- 直接插入排序