Spark集群 + Akka + Kafka + Scala 开发(3) : 开发一个Akka + Spark的应用
[comment]: # Spark集群 + Akka + Kafka + Scala 开发(3) : 开发一个Akka + Spark的应用
前言
在Spark集群 + Akka + Kafka + Scala 开发(1) : 配置开发环境中,我们已经部署好了一个Spark的开发环境。
在Spark集群 + Akka + Kafka + Scala 开发(2) : 开发一个Spark应用中,我们已经写好了一个Spark的应用。
本文的目标是写一个基于akka的scala工程,在一个spark standalone的集群环境中运行。
akka是什么?
akka的作用
akka的名字是action kernel的回文。根据官方定义:akka用于resilient elastic distributed real-time transaction processing。
个人理解是:
resilient:是指对需求和安全性等方面(来自于外部的)的一种适应力(弹性)。
elastic:是指对资源利用方面的弹性。
因此,akka是一个满足需求弹性、资源分配弹性的分布式实时事务处理系统。
akka只是一个类库,一个工具,并没有提供一个平台。
akka的运行模式和用例
- akka有两种运行模式:
- As a library: 一个使用于web应用,把akka作为一个普通的jar包放到classpath或者
WEB-INF/lib。 - As an application: 也称为micro system。
 
 - As a library: 一个使用于web应用,把akka作为一个普通的jar包放到classpath或者
 - akka的用例
akka的用例很多,可以参照Examples of use-cases for Akka. 
本文中的用例
在本文中,一个Spark + akka的环境里,akka被用于as an application模式下。
我们会创建一个akka工程,含有两个应用:
- akka host application
建立一个actor system, 定义了所有的任务(actors)。等待客户端的请求。
部分actor使用了spark的云计算功能。
这是一个spark的应用。 - akka client application
调用host application上特定的actor。 
我们看出,这里我们把akka作为一个任务处理器,并通过spark来完成任务。
项目结构和文件说明
说明
这个工程包含了两个应用。
一个Consumer应用:CusomerApp:实现了通过Spark的Stream+Kafka的技术来实现处理消息的功能。
一个Producer应用:ProducerApp:实现了向Kafka集群发消息的功能。
文件结构
AkkaSampleApp    # 项目目录
|-- build.bat    # build文件
|-- src
    |-- main
        |-- resources
            |-- application.conf   # Akka Server应用的配置文件
            |-- client.conf        # Akka Client应用的配置文件
        |-- scala
            |-- ClientActor.scala       # Akka Client的Actor:提供了一种调用Server Actor的方式。
            |-- ClientApp.scala         # Akka Client应用
            |-- ProductionReaper.scala  # Akka Shutdown pattern的实现者
            |-- Reaper.scala            # Akka Shutdown pattern的Reaper抽象类
            |-- ServerActor.scala       # Akka Server的Actor,提供一个求1到n的MapReduce计算。使用了Spark。
            |-- ServerApp.scala         # Akka Server应用
构建工程目录
可以运行:
mkdir AkkaSampleApp
mkdir -p /AkkaSampleApp/src/main/resources
mkdir -p /AkkaSampleApp/src/main/scala
代码
build.sbt
name := "akka-sample-app"
version := "1.0"
scalaVersion := "2.11.8"
scalacOptions += "-feature"
scalacOptions += "-deprecation"
scalacOptions += "-language:postfixOps"
libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor" % "2.4.10",
  "com.typesafe.akka" %% "akka-remote" % "2.4.10",
  "org.apache.spark" %% "spark-core" % "2.0.0"
)
resolvers += "Akka Snapshots" at "http://repo.akka.io/snapshots/"
application.conf
akka {
  #loglevel = "DEBUG"
  actor {
    provider = "akka.remote.RemoteActorRefProvider"
  }
  remote {
    enabled-transports = ["akka.remote.netty.tcp"]
    netty.tcp {
      hostname = "127.0.0.1"
      port = 2552
    }
    #log-sent-messages = on
    #log-received-messages = on
  }
}
cient.conf
akka {
  #loglevel = "DEBUG"
  actor {
    provider = "akka.remote.RemoteActorRefProvider"
  }
  remote {
    enabled-transports = ["akka.remote.netty.tcp"]
    netty.tcp {
      hostname = "127.0.0.1"
      port = 0
    }
    #log-sent-messages = on
    #log-received-messages = on
  }
}
注:
port = 0表示这个端口号会自动生成一个。
ClientActor.scala
import akka.actor._
import akka.event.Logging
class ClientActor(serverPath: String) extends Actor {
  val log = Logging(context.system, this)
  val serverActor = context.actorSelection(serverPath)
  def receive = {
    case msg: String =>
        log.info(s"ClientActor received message '$msg'")
        serverActor ! 10000L
  }
}
ClientApp.scala
import com.typesafe.config.ConfigFactory
import akka.actor._
import akka.remote.RemoteScope
import akka.util._
import java.util.concurrent.TimeUnit
import scala.concurrent._
import scala.concurrent.duration._
object ClientApp {
  def main(args: Array[String]): Unit = {
    val system = ActorSystem("LocalSystem", ConfigFactory.load("client"))
    // get the remote actor via the server actor system's address
    val serverAddress = AddressFromURIString("akka.tcp://ServerActorSystem@127.0.0.1:2552")
    val actor = system.actorOf(Props[ServerActor].withDeploy(Deploy(scope = RemoteScope(serverAddress))))
    // invoke the remote actor via a client actor.
    // val remotePath = "akka.tcp://ServerActorSystem@127.0.0.1:2552/user/serverActor"
    // val actor = system.actorOf(Props(classOf[ClientActor], remotePath), "clientActor")
    buildReaper(system, actor)
    // tell
    actor ! 10000L
    waitShutdown(system, actor)
  }
  private def buildReaper(system: ActorSystem, actor: ActorRef): Unit = {
    import Reaper._
    val reaper = system.actorOf(Props(classOf[ProductionReaper]))
    // Watch the action
    reaper ! WatchMe(actor)
  }
  private def waitShutdown(system: ActorSystem, actor: ActorRef): Unit = {
    // trigger the shutdown operation in ProductionReaper
    system.stop(actor)
    // wait to shutdown
    Await.result(system.whenTerminated, 60.seconds)
  }
}
ProductionReaper.scala
当所有的Actor停止后,终止Actor System。
class ProductionReaper extends Reaper {
  // Shutdown
  def allSoulsReaped(): Unit = {
    context.system.terminate()
  }
}
Reaper.scala
import akka.actor.{Actor, ActorRef, Terminated}
import scala.collection.mutable.ArrayBuffer
object Reaper {
  // Used by others to register an Actor for watching
  case class WatchMe(ref: ActorRef)
}
abstract class Reaper extends Actor {
  import Reaper._
  // Keep track of what we're watching
  val watched = ArrayBuffer.empty[ActorRef]
  // Derivations need to implement this method.  It's the
  // hook that's called when everything's dead
  def allSoulsReaped(): Unit
  // Watch and check for termination
  final def receive = {
    case WatchMe(ref) =>
      context.watch(ref)
      watched += ref
    case Terminated(ref) =>
      watched -= ref
      if (watched.isEmpty) allSoulsReaped()
  }
}
ServerActor.scala
提供一个求1到n平方和的MapReduce计算。
import akka.actor.Actor
import akka.actor.Props
import akka.event.Logging
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf
class ServerActor extends Actor {
  val log = Logging(context.system, this)
  def receive = {
    case n: Long =>
        squareSum(n)
  }
  private def squareSum(n: Long): Long = {
    val conf = new SparkConf().setAppName("Simple Application")
    val sc = new SparkContext(conf)
    val squareSum = sc.parallelize(1L until n).map { i =>
      i * i
    }.reduce(_ + _)
    log.info(s"============== The square sum of $n is $squareSum. ==============")
    squareSum
  }
}
ServerApp.scala
import scala.concurrent.duration._
import com.typesafe.config.ConfigFactory
import akka.actor.ActorSystem
import akka.actor.Props
object ServerApp {
  def main(args: Array[String]): Unit = {
    val system = ActorSystem("ServerActorSystem")
    val actor = system.actorOf(Props[ServerActor], name = "serverActor")
  }
}
构建工程
进入目录AkkaSampleApp。运行:
sbt package
第一次运行时间会比较长。
测试应用
启动Spark服务
- 启动spark集群master server
 
$SPARK_HOME/sbin/start-master.sh
master服务,默认会使用
7077这个端口。可以通过其日志文件查看实际的端口号。
- 启动spark集群slave server
 
$SPARK_HOME/sbin/start-slave.sh spark://$(hostname):7077
启动Akka Server应用
运行:
$SPARK_HOME/bin/spark-submit --master spark://$(hostname):7077 --class ServerApp target/scala-2.11/akka-sample-app_2.11-1.0.jar
如果出现java.lang.NoClassDefFoundError错误,
请参照Spark集群 + Akka + Kafka + Scala 开发(1) : 配置开发环境,
确保akka的包在Spark中设置好了。
注:可以使用Ctrl+C来中断这个Server应用。
启动Akka Client应用
新启动一个终端,运行:
java -classpath ./target/scala-2.11/akka-sample-app_2.11-1.0.jar:$AKKA_HOME/lib/akka/*:$SCALA_HOME/lib/* ClientApp
# or
# $SPARK_HOME/bin/spark-submit --master spark://$(hostname):7077 --class ClientApp target/scala-2.11/akka-sample-app_2.11-1.0.jar
然后:看看Server应用是否开始处理了。
总结
Server应用需要Spark的技术,因此,是在Spark环境中运行。
Clinet应用,可以是一个普通的Java应用。
下面请看
至此,我们已经写好了一个spark集群+akka+scala的应用。下一步请看:
Spark集群 + Akka + Kafka + Scala 开发(4) : 开发一个Kafka + Spark的应用
参照
- akka document
 - Elasticity (cloud computing)
 - Resilient control systems
 - akka 2.4.10 code samples
 - akka office samples
 - A simple Akka (actors) remote example
 - Shutdown Patterns in AKKA 2
 
Spark集群 + Akka + Kafka + Scala 开发(3) : 开发一个Akka + Spark的应用的更多相关文章
- Spark入门:第2节 Spark集群安装:1 - 3;第3节 Spark HA高可用部署:1 - 2
		
三. Spark集群安装 3.1 下载spark安装包 下载地址spark官网:http://spark.apache.org/downloads.html 这里我们使用 spark-2.1.3-bi ...
 - 大数据技术之_19_Spark学习_01_Spark 基础解析 + Spark 概述 + Spark 集群安装 + 执行 Spark 程序
		
第1章 Spark 概述1.1 什么是 Spark1.2 Spark 特点1.3 Spark 的用户和用途第2章 Spark 集群安装2.1 集群角色2.2 机器准备2.3 下载 Spark 安装包2 ...
 - hadoop+spark集群搭建入门
		
忽略元数据末尾 回到原数据开始处 Hadoop+spark集群搭建 说明: 本文档主要讲述hadoop+spark的集群搭建,linux环境是centos,本文档集群搭建使用两个节点作为集群环境:一个 ...
 - CentOS7 安装spark集群
		
Spark版本 1.6.0 Scala版本 2.11.7 Zookeeper版本 3.4.7 配置虚拟机 3台虚拟机,sm,sd1,sd2 1. 关闭防火墙 systemctl stop firewa ...
 - spark集群搭建
		
文中的所有操作都是在之前的文章scala的安装及使用文章基础上建立的,重复操作已经简写: 配置中使用了master01.slave01.slave02.slave03: 一.虚拟机中操作(启动网卡)s ...
 - Spark学习笔记5:Spark集群架构
		
Spark的一大好处就是可以通过增加机器数量并使用集群模式运行,来扩展计算能力.Spark可以在各种各样的集群管理器(Hadoop YARN , Apache Mesos , 还有Spark自带的独立 ...
 - Spark集群新增节点方法
		
Spark集群处理能力不足需要扩容,如何在现有spark集群中新增新节点?本文以一个实例介绍如何给Spark集群新增一个节点. 1. 集群环境 现有Spark集群包括3台机器,用户名都是cdahdp, ...
 - Spark集群安装与配置
		
一.Scala安装 1.https://www.scala-lang.org/download/2.11.12.html下载并复制到/home/jun下解压 [jun@master ~]$ cd sc ...
 - Spark 个人实战系列(1)--Spark 集群安装
		
前言: CDH4不带yarn和spark, 因此需要自己搭建spark集群. 这边简单描述spark集群的安装过程, 并讲述spark的standalone模式, 以及对相关的脚本进行简单的分析. s ...
 - Spark集群术语
		
Spark集群术语解析 1. Application Application是用户在Spark上构建(编写)的程序,包含driver program 和executors(分布在集群中多个节点上运行的 ...
 
随机推荐
- Linux初学 - Elasticsearch环境安装
			
下载 https://www.elastic.co/downloads/elasticsearch 安装 rpm -ivh 也可以双击rpm包安装 修改elastaticsearch host配置 修 ...
 - rabbitmq消息队列——"工作队列"
			
二."工作队列" 在第一节中我们发送接收消息直接从队列中进行.这节中我们会创建一个工作队列来分发处理多个工作者中的耗时性任务. 工作队列主要是为了避免进行一些必须同步等待的资源密集 ...
 - java向mysql数据库插入数据显示乱码的问题
			
在做一个java web工程时,有时会碰到在向数据库添加数据库时,结果出现乱码”???“的问题.针对该问题的主要解决办法就是: 一.确保是否添加了字符集过滤器: 在java web工程中的web.xm ...
 - Cocos2d-x 3.2 学习笔记(一)环境搭建
			
目前项目无事,时间比较充裕,因此来学习下cocos2dx,当然本人也是新手一个, 写此笔记做备忘和脚步. 最近3.2版本更新出來了!官方说这是自2.x分支以来修复了超过450个bug,3.2版本是目前 ...
 - Linux的学习--系统目录
			
因为利用Linux来开发产品或distributions的社群/公司与个人实在太多了, 如果每个人都用自己的想法来配置文件放置的目录,那么将可能造成很多管理上的困扰. 你能想象,你进入一个企业之后,所 ...
 - Key Components and Internals of Spring Boot Framework--转
			
原文地址:http://www.journaldev.com/7989/key-components-and-internals-of-spring-boot-framework In my prev ...
 - URL格式
			
URL由三部分组成:资源类型.存放资源的主机域名.资源文件名. URL的一般语法格式为: (带方括号[]的为可选项): protocol :// hostname[:port] / path / [; ...
 - Android学习笔记之短信验证码的获取和读取
			
PS:最近很多事情都拖拖拉拉的..都什么办事效率啊!!! 还得吐槽一下移动运营商,验证码超过五次的时候,直接把我的手机号封闭.真是受够了. 学习笔记: 1.Android之如何获取短信验证码. 2.如 ...
 - mybatis错误之配置文件属性配置问题
			
在mybatis的配置文件SqlMapConfig.xml中,可以在开始的地方先加载一个properties节点,用来定义属性变量. <!-- 加载属性文件 --> <propert ...
 - 解决The file “FWLifeApp” couldn’t be opened because you don’t have permission to view it.问题
			
The file “FWLifeApp” couldn’t be opened because you don’t have permission to view it问题是因为项目文件中的Bundl ...