Spark和Spring整合处理离线数据

如果你比较熟悉JavaWeb应用开发,那么对Spring框架一定不陌生,并且JavaWeb通常是基于SSM搭起的架构,主要用Java语言开发。但是开发Spark程序,Scala语言往往必不可少。

众所周知,Scala如同Java一样,都是运行在JVM上的,所以它具有很多Java语言的特性,同时作为函数式编程语言,又具有自己独特的特性,实际应用中除了要结合业务场景,还要对Scala语言的特性有深入了解。

如果想像使用Java语言一样,使用Scala来利用Spring框架特性、并结合Spark来处理离线数据,应该怎么做呢?

本篇文章,通过详细的示例代码,介绍上述场景的具体实现,大家如果有类似需求,可以根据实际情况做调整。

1.定义一个程序启动入口

复制代码
object Bootstrap {
  private val log = LoggerFactory.getLogger(Bootstrap.getClass)

  //指定配置文件如log4j的路径
  val ConfFileName = "conf"
  val ConfigurePath = new File("").getAbsolutePath.substring(0, if (new File("").getAbsolutePath.lastIndexOf("lib") == -1) 0
  else new File("").getAbsolutePath.lastIndexOf("lib")) + this.ConfFileName + File.separator

  //存放实现了StatsTask的离线程序处理的类
  private val TASK_MAP = Map("WordCount" -> classOf[WordCount])

  def main(args: Array[String]): Unit = {
    //传入一些参数,比如要运行的离线处理程序类名、处理哪些时间的数据
    if (args.length < 1) {
      log.warn("args 参数异常!!!" + args.toBuffer)
      System.exit(1)
    }
    init(args)
  }

  def init(args: Array[String]) {
    try {
      SpringUtils.init(Array[String]("applicationContext.xml"))
      initLog4j()

      val className = args(0)
      // 实例化离线处理类
      val task = SpringUtils.getBean(TASK_MAP(className))

      args.length match {
        case 3 =>
          // 处理一段时间的每天离线数据
          val dtStart = DateTimeFormat.forPattern("yyyy-MM-dd").parseDateTime(args(1))
          val dtEnd = DateTimeFormat.forPattern("yyyy-MM-dd").parseDateTime(args(2))
          val days = Days.daysBetween(dtStart, dtEnd).getDays + 1
          for (i <- 0 until days) {
            val etime = dtStart.plusDays(i).toString("yyyy-MM-dd")
            task.runTask(etime)

            log.info(s"JOB --> $className 已成功处理: $etime 的数据")
          }

        case 2 =>
          // 处理指定的某天离线数据
          val etime = DateTimeFormat.forPattern("yyyy-MM-dd").parseDateTime(args(1)).toString("yyyy-MM-dd")
          task.runTask(etime)
          log.info(s"JOB --> $className 已成功处理: $etime 的数据")

        case 1 =>
          // 处理前一天离线数据
          val etime = DateTime.now().minusDays(1).toString("yyyy-MM-dd")
          task.runTask(etime)
          log.info(s"JOB --> $className 已成功处理: $etime 的数据")

        case _ => println("执行失败 args参数:" + args.toBuffer)
      }
    } catch {
      case e: Exception =>
        println("执行失败 args参数:" + args.toBuffer)
        e.printStackTrace()
    }

    // 初始化log4j
    def initLog4j() {
      val fileName = ConfigurePath + "log4j.properties"
      if (new File(fileName).exists) {
        PropertyConfigurator.configure(fileName)
        log.info("日志log4j已经启动")
      }
    }
  }
}

2.加载Spring配置文件工具类

复制代码
object SpringUtils {
  private var context: ClassPathXmlApplicationContext = _

  def getBean(name: String): Any = context.getBean(name)

  def getBean[T](name: String, classObj: Class[T]): T = context.getBean(name, classObj)

  def getBean[T](_class: Class[T]): T = context.getBean(_class)

  def init(springXml: Array[String]): Unit = {
    if (springXml == null || springXml.isEmpty) {
      try
        throw new Exception("springXml 不可为空")
      catch {
        case e: Exception => e.printStackTrace()
      }
    }
    context = new ClassPathXmlApplicationContext(springXml(0))
    context.start()
  }

}

3.Spring配置文件applicationContext.xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
          http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!-- 配置包扫描 -->
    <context:component-scan base-package="com.bigdata.stats"/>

</beans>

4.定义一个trait,作为离线程序的公共"父类"

复制代码
trait StatsTask extends Serializable {
  //"子类"继承StatsTask重写该方法实现自己的业务处理逻辑 
  def runTask(etime: String)
}
5.继承StatsTask的离线处理类
//不要忘记添加 @Component ,否则无法利用Spring对WordCount进行实例化
@Component
class WordCount extends StatsTask {

  override def runTask(etime: String): Unit = {
    val sparkSession = SparkSession
      .builder()
      .appName("test")
      .master("local[*]")
      .getOrCreate()

    import sparkSession.implicits._

    val words = sparkSession.read.textFile("/Users/BigData/Documents/data/wordcount.txt").flatMap(_.split(" "))
      .toDF("word")

    words.createOrReplaceTempView("wordcount")

    val df = sparkSession.sql("select word, count(*) count from wordcount group by word")

    df.show()
  }
}

更多干货抢先看: 世界格局的演变:一场"热闹非凡"的历史大戏

相关推荐
TM1Club1 天前
AI驱动的预测:新的竞争优势
大数据·人工智能·经验分享·金融·数据分析·自动化
zhang133830890751 天前
CG-09H 超声波风速风向传感器 加热型 ABS材质 重量轻 没有机械部件
大数据·运维·网络·人工智能·自动化
电商API_180079052471 天前
第三方淘宝商品详情 API 全维度调用指南:从技术对接到生产落地
java·大数据·前端·数据库·人工智能·网络爬虫
龙山云仓1 天前
No140:AI世间故事-对话康德——先验哲学与AI理性:范畴、道德律与自主性
大数据·人工智能·深度学习·机器学习·全文检索·lucene
躺柒1 天前
读数字时代的网络风险管理:策略、计划与执行04风险指引体系
大数据·网络·信息安全·数字化·网络管理·网络风险管理
启山智软1 天前
【中大企业选择源码部署商城系统】
java·spring·商城开发
独自归家的兔1 天前
从 “局部凑活“ 到 “全局最优“:AI 规划能力的技术突破与产业落地实践
大数据·人工智能
海域云-罗鹏1 天前
国内公司与英国总部数据中心/ERP系统互连,SD-WAN专线实操指南
大数据·数据库·人工智能
策知道1 天前
依托政府工作报告准备省考【经验贴】
大数据·数据库·人工智能·搜索引擎·政务
Henry-SAP1 天前
SAP(ERP) 组织结构业务视角解析
大数据·人工智能·sap·erp·sap pp