Spark wordcount 代码分析

本文详细分析了Spark 2.1.0版本的WordCount程序源码,涉及SparkSession的getOrCreate()方法、输入文件处理、RDD操作等关键步骤,帮助理解Spark内部工作原理及Scala编程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spark WordCount 源码解析

学习Spark有一段时间了,现在只处于会用的阶段,对内部代码和原理不怎么熟悉,乘着这几天有时间,解读一下源码,同时也熟悉一下scala语言

环境:

Spark 2.1.0
IntelliJ IDEA 15.0.2
Scala 2.10.4

废话不说,先上完整代码:

这里写图片描述

然后我们开始逐条代码分析:

if (args.length < 1) {

    System.err.println("Usage: JavaWordCount <file>");
    System.exit(1);
 }

因为我们的程序是计算单词的个数,所以需要有个单词源,即输入文件。所以在程序的开始先判断一下是否有输入目录,如果没有文件目录输入,程序打印错误日志,直接退出。如果输入正常,程序向下执行。

SparkSession spark = SparkSession
    .builder()
    .appName("JavaWordCount")
    .getOrCreate();

在spark2.0之后,引入了SparkSession这个类,SparkSession中包含SparkConf、SparkContext、SQLContext,再使用Spark的API时只需要创建一个SparkSession就可以了。

进入SparkSession中的getOrCreate()方法:

首先先获取一个SparkSession,这个SparkSession可以为null

var session = activeThreadSession.get()

我们先看一下activeThreadSession的声明

private val activeThreadSession = new InheritableThreadLocal[SparkSession]

InheritableThreadLocal 是 ThreadLocal的子类,是用来实现变量的线程安全的

获取SparkSession之后,

      if ((session ne null) && !session.sparkContext.isStopped) {
        options.foreach { case (k, v) => session.sessionState.conf.setConfString(k, v) }
        if (options.nonEmpty) {
          logWarning("Using an existing SparkSession; some configuration may not take effect.")
        }
        return session
      }

如果SparkSession不为null,把option中的配置赋值给SparkSession中的conf,然后返回。

如果当前线程没有SparkSession,直接从全局session中获取,看如下分析

session = defaultSession.get()

我们先看一下defaultSession的是什么

private val defaultSession = new AtomicReference[SparkSession]

这里的AutoicReference是jdk中的方法,主要用来的对对象进行原子操作,保证SparkSession对象的原子性(不会被调度机制打断),简单来说,同一时间,只有一个线程或进程对SparkSession进行操作,

        if ((session ne null) && !session.sparkContext.isStopped) {
          options.foreach { case (k, v) => session.sessionState.conf.setConfString(k, v) }
          if (options.nonEmpty) {
            logWarning("Using an existing SparkSession; some configuration may not take effect.")
          }
          return session
        }

从option(HashMap)中读取配置信息给SparkSession中的conf设置,然后返回。

最后如果全局session也没有话,那么只能创建一个新的session。

        val sparkContext = userSuppliedContext.getOrElse {
          // set app name if not given
          val randomAppName = java.util.UUID.randomUUID().toString
          val sparkConf = new SparkConf()
          options.foreach { case (k, v) => sparkConf.set(k, v) }
          if (!sparkConf.contains("spark.app.name")) {
            sparkConf.setAppName(randomAppName)
          }
          val sc = SparkContext.getOrCreate(sparkConf)
          // maybe this is an existing SparkContext, update its SparkConf which maybe used
          // by SparkSession
          options.foreach { case (k, v) => sc.conf.set(k, v) }
          if (!sc.conf.contains("spark.app.name")) {
            sc.conf.setAppName(randomAppName)
          }
          sc
        }
        session = new SparkSession(sparkContext)
        options.foreach { case (k, v) => session.sessionState.conf.setConfString(k, v) }
        defaultSession.set(session)

创建session过程中,如果没有设置一些配置,比如 executor_memory ,程序会设置默认值,比如

这里写图片描述

创建完session之后返回到主函数继续执行。

JavaRDD<String> lines = spark.read().textFile(args[0]).javaRDD();

读取文件内容并转化RDD,默认文件类型parquet

    JavaRDD<String> words = lines.flatMap(new FlatMapFunction<String, String>() {
      @Override
      public Iterator<String> call(String s) {
        return Arrays.asList(SPACE.split(s)).iterator();
      }
    });

读取每一行数据进行切分,返回一个新的RDD

    JavaPairRDD<String, Integer> ones = words.mapToPair(
      new PairFunction<String, String, Integer>() {
        @Override
        public Tuple2<String, Integer> call(String s) {
          return new Tuple2<>(s, 1);
        }
      });

创建一个PairRDD,为了之后的计算的单词出现的总次数

    JavaPairRDD<String, Integer> counts = ones.reduceByKey(
      new Function2<Integer, Integer, Integer>() {
        @Override
        public Integer call(Integer i1, Integer i2) {
          return i1 + i2;
        }
      });

对相同key的单词进行求和计算

    List<Tuple2<String, Integer>> output = counts.collect();
    for (Tuple2<?,?> tuple : output) {
      System.out.println(tuple._1() + ": " + tuple._2());
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值