1.1 框架设计原理
框架设计思想可以采取两种模式,一种是MVC,另外一种是三层架构,由于我们这里没有页面展示的需求,所以我们暂时采取三层架构的方式。
-
.三层架构的概念
- Controller:控制层,封装调度作用,数据的流转过程
- Service: 服务层,封装实际的计算逻辑
- DAO :Data Access Object,数据访问对象,专门用于和一些关系型数据互相访问,用来和数据源的连接
-
.调用的顺序
-
.架构中一些其他的内容
- bean : 用来封装一个bean类,对数据的一些封装,采用样例类,声明在包对象中
- helper:辅助类,如累加器类
- Apllication : 应用程序,主程序
- Util : 工具类
1.2 框架搭建
1.2.1 Util
EnvUtils
– 如何实现三层框架共享数据呢?
- 实现原理:在当前线程中创建一个内存,将共享数据存放在这个内存中,这样三层架构均可以使用。
- 实现方式:
a、在线程中一直就保留着可以共享数据的空间
b、JDK API 提供了一个工具类,可以直接访问这个空间
c、只要调用这个工具类(ThreadLocal)将数据存入到共享数据中,也可以从这个内存中调用共享内存中的数据 - 具体的步骤:
a、"创建"一个共享数据 : 案例如下:
“private val scLocal = new ThreadLocal[SparkContext] "
b、将共享数据"放进"共享空间中
“scLocal.set(sc)”
c、从当前线程的共享空间中"获取"共享数据
“scLocal.get()”
d、将共享数据从共享空间中"清除”
“scLocal.remove()”
package com.ifeng.summer.foramework.util
import org.apache.spark.{SparkConf, SparkContext}
object EnvUtils {
// 创建一个共享数据
private val scLocal = new ThreadLocal[SparkContext]
//获取环境对象
def getEnv() = {
//从当前线程的共享空间中获取环境对象
var sc: SparkContext = scLocal.get()
if (sc == null) {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkApplication")
sc = new SparkContext(sparkConf)
scLocal.set(sc)
}
sc
}
// 清除对象
def clean() = {
getEnv().stop()
// 将共享内存中的数据清除
scLocal.remove()
}
}
PropertiesUtil
–动态获取连接的资源,将需要连接的资源配置文件放置在配置文件中,从配置文件中读取连接需要的资源,这样,当我们需要更换连接的资源时,只要修改配置文件即可。
这种思想是非常重要的,类似hadoop的MR,将资源和计算分离开,做资源的调度,扩展起来就非常的方便。
package com.ifeng.summer.foramework.util
import java.util.ResourceBundle
object PropertiesUtil {
//绑定配置文件
val summer: ResourceBundle = ResourceBundle.getBundle("summers")
def getValue(key: String): String = {
//传入一个key,返回一个value
summer.getString(key)
}
}
1.2.2 core
TApplication
package com.ifeng.summer.foramework.core
import java.net.{InetAddress, ServerSocket, Socket}
import com.ifeng.summer.foramework.util.{EnvUtils, PropertiesUtil}
import org.apache.spark.{SparkConf, SparkContext}
trait TApplication {
var envdata: Any = null
//第一步:初始化环境
def start(t: String)(op: => Unit) = {
if (t == "Socket") {
envdata = new Socket(PropertiesUtil.getValue("serverhost")
, PropertiesUtil.getValue("serverport").toInt)
} else if (t == "ServerSocket") {
envdata = new ServerSocket(PropertiesUtil.getValue("serverport").toInt)
} else if (t == "Spark") {
envdata = EnvUtils.getEnv()
}
//业务逻辑
try {
op
} catch {
case ex: Exception => println("op执行失败,原因是:" + ex.getMessage)
}
//环境关闭
if (t == "ServerSocket") {
val ServerSocket = envdata.asInstanceOf[ServerSocket]
if (!ServerSocket.isClosed) {
ServerSocket.close()
}
} else if (t == "Socket") {
val socket = envdata.asInstanceOf[Socket]
if (!socket.isClosed) {
socket.close()
}
} else if (t == "Spark") {
EnvUtils.clean()
}
}
}
TController
package com.ifeng.summer.foramework.core
trait TController {
//执行控制
def execute() : Unit
}
TService
package com.ifeng.summer.foramework.core
trait TService {
//数据分析
def analysis() : Any
//数据分析
def analysis(data :Any) : Any
}
TDAO
package com.ifeng.summer.foramework.core
import com.ifeng.summer.foramework.util.EnvUtils
import org.apache.spark.rdd.RDD
trait TDAO {
def readFile(path :String) ={
val fileRDD: RDD[String] = EnvUtils.getEnv().textFile(path)
fileRDD
}
}
封装
1 更改trait
object WordCountApplication extends App { //extend App 不用main直接运行
由于extend了App
class Application 无法再次extend
改成 trait TApplication
object WordCountApplication extends App with TApplication
```java
trait TApplication
if (t == "Socket") {
envdata = new Socket(
PropertiesUtil.getValue("serverhost"),
PropertiesUtil.getValue("serverport").toInt)
} else if (t == "ServerSocket") {
envdata = new ServerSocket(PropertiesUtil.getValue("serverport").toInt)
// } else if (t == "Spark") {
// envdata = EnvUtils.getEnv()
} else if (t == "Spark") {
val sparkConf = new SparkConf().setAppName("SparkApplication").setMaster("local")
envdata = new SparkContext(sparkConf)
}
// TODO 3 环境关闭
if (t == "ServerSocket") {
val ServerSocket = envdata.asInstanceOf[ServerSocket]
if (!ServerSocket.isClosed) {
ServerSocket.close()
}
} else if (t == "Socket") {
val socket = envdata.asInstanceOf[Socket]
if (!socket.isClosed) {
socket.close()
}
} else if (t == "Spark") {
// EnvUtils.clean()
val sc = envdata.asInstanceOf[SparkContext]
sc.stop()
}
2 添加依赖
需要在WordCountApplication 中添加如下依赖
```java
<dependency>
<groupId>org.ifeng</groupId>
<artifactId>summer_framework</artifactId>
<version>1.0</version>
</dependency>
3 WordCountApplication
1 在start 中写逻辑业务
2 val sc = envdata.asInstanceOf[SparkContext] //Tapplication 中copy
object WordCountApplication extends App with TApplication{ //extend App 不用main直接运行
start("Spark"){
val sc = envdata.asInstanceOf[SparkContext] //Tapplication 中copy
val rdd1 = sc.parallelize(List("a","b","c","d","e","a"))
val newRDD: RDD[Map[String, Int]] = rdd1.map(word => Map[String, Int]((word, 1)))
val reduceWC: Map[String, Int] = newRDD.reduce(
(map1, map2) => {
map1.foldLeft(map2)(
(map, kv) => {
val word = kv._1
val count = kv._2
map.updated(word, map.getOrElse(word, 0) + count)
}
)
}
)
reduceWC.foreach(println)
}
三层架构
线程中的一块
在三层架构中,可以在线程中开辟一块空间,共享数据的内存空间,广播变量的思想
package com.ifeng.summer.foramework.util
import org.apache.spark.{SparkConf, SparkContext}
object EnvUtils {
// 创建一个共享数据
private val scLocal = new ThreadLocal[SparkContext]
//获取环境对象
def getEnv() = {
// TODO 从当前线程的共享空间中获取环境对象
var sc: SparkContext = scLocal.get()
if (sc == null) {
// TODO 如果获取不到环境变量,那么创建新的环境变量,保存到内存中
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkApplication")
sc = new SparkContext(sparkConf)
// 保存到共享内存中
scLocal.set(sc)
}
sc
}
// TODO 清除对象
def clean() = {
getEnv().stop()
// 将共享内存中的数据清除掉
scLocal.remove()
}
}
/*
* WordCont 控制器
* */
class WordCountController extends TController{
//Controller 中应该走向Service
private val WordCountService = new WordCountService
override def execute(): Unit = {
//调用WordCountService的逻辑
WordCountService.analysis()
}
}
/*
* 数据分析
* */
class WordCountService extends TService{
//Service 应该访问DAO
private val WordCountDAO = new WordCountDAO
override def analysis(): Any = {
null
}
override def analysis(data: Any): Any = ???
}