开发者学堂课程【2020版大数据实战项目之 DMP 广告系统(第五阶段):报表统计_执行框架_设计】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/680/detail/11816
报表统计_执行框架_设计
一、抽取公共类
1、目标和步骤
(1)上节课对整个统计数据在地区上分布的报表数据进行了一些处理,并且落地到 Kudo 当中,第一个报表已经做完了。下面要对刚才的代码进行一次再审视,看一看这份代码里面有没有可抽取出来的、可变化的点,可以抽取出来的一套框架,接下来就是抽取框架。
(2)①这一步要做的事情目的非常简单,所有报表统计的基本思想都是一致的,把数据从 ODS 中取出,经过处理,再落地到另外一张表中,这些功能都是公用的,流程都一样,可以把流程抽出来。第二点,找工作的时候面向对象是一个非常重要的技能,虽然找工作的时候不一定会直接询问相关的面向对象的内容,因为不好考察,但是在写代码的时候,如果想晋升一步,比如说架构师、开源社区的管理者,想再往前走一步,面向对象技能就非常重要。
②所以这一小节的内容比较难,不太好理解。在我的视角上来看,并不是很难,但是站在大家的视角上来看,会稍微有一点难度。大家在一开始写代码的时候会强调功能的实现,而会忽略一些结构上的面向对象上的东西,这些确实需要一些经验才能积累出来。面向对象是一个非常重要的技能,它所带来的增益不仅是面试的时候,面试官可能会问设计模式听过吗?软件开发的原则听过吗?想要理解这些设计模式、开发原则等还是需要积累经验的。第二点增益是,程序员梦想就是开发数据库,开发操作系统,开发编译器,这三件事没有一件是容易的。在做这种级别、这种规模的系统的时候的面向对象技能一定要好,才行。一般情况下写业务代码是比较枯燥的,但是能把业务代码写好,写的可扩展,有一本书里面讲了程序员不应该面向需求进行开发,而应该面向需求的变更进行开发,好的程序员会预先进行一些需求变更的考虑。如果想要做到比较好的程度,就需要面向对象的技巧。通过这个小节的学习,会抽取一个简单的框架,这对于以后查看代码、查看源码、自己抽取一些框架或自己编写一些扩展性和维护性比较好的代码的时候会有帮助,这是目的,也是大家能学到的东西。
③步骤总共有三步,第一步分析现有的代码逻辑,第二步进行框架设计,第三步修改现有的代码,让它适应这个框架。建议大家在做任何事情之前,先抽时间思考这个东西是什么样的,不需要把每一步都想清楚,但是需要有一个大概的逻辑,要知道抽取这个框架是干什么的,它要实现什么功能,外部怎么调用它。这个时候再提笔去做,就会减少很多失误的情况。
2、分析现有代码逻辑
(1)①下面进行第一步分析现有的代码逻辑。一般情况下在进行代码抽取或设计的时候,大致的步骤就是四个步骤,首先要详细的观察代码,从这份代码里面找到流程,找到重复的东西。比如说现在有十个类,他们共同遵循某一个框架,那这十个类它们共有的东西是什么?共同点是什么?共同点是可以抽取出来的点,这就是第一步,要先观察找到重复性的东西。要在流程性的,重复性的东西当中找到每一个类的个性点,就是可能会变化的点,找到这些点以后框架就有了。第三步,把这些点抽取出来,第四步就是把整个抽取出来的东西套进流程中,整体上的功能就实现了。步骤就是这样,但真的想实施还是会有一些障碍的。下面是刚才写的代码
object RegionReportProcessor {
def main(args:Array[String]):Unit={
import com. itheima. dmp. utils.SparkConfigHelper.
import com. itheima. dmp. utils.KuduHelper.
import org. apache. spark. sql. functions.
val spark=SparkSession. builder()
. master("local[6]")
.appName("pmt_etl")
.loadConfig( )
.get0rCreate()
import spark. implicits...
val origin=spark.readKuduTable(ORIGIN_TABLE_NAME)
if(origin.isEmpty) return
val result=origin. get.groupBy($"region",$"city")
. agg(count($"*") as "count")
. select($"region",$"city",$"count")
spark.createKuduTable(TARGET_TABLE_NAME, schema, keys)
result.saveToKudu(TARGET_TABLE_NAME)
}
private val ORIGIN_TABLE_NAME=PmtETLRunner.ODS_TABLE_ NAME
private val TARGET_TARELE_NAME="ANALYSIS_REGION_ "+KuduHelper.formattedDate()
import scala. collection.JavaConverters._
private val schema=new Schema(List(
new ColumnSchemaBuilder("region",Type.STRING). nullable
(false). key(true). build,
new ColumnSchemaBuilder(“city”, Type.STRING). nullable
(true). key(true). build(),
new ColumnSchemaBuilder("count",Type.STRING). nullable
(true). key(false). build()
) .asJava
)
private val keys=Seq("region","city")
}
② main 方法当中引入了一些东西,接下来创建 spark,创建 spark 以后进行一个 implicits,再进行一个 origin,就是读一个 Kudo 表,读完以后判断为空的话 return,不为空的话进行分组,分组完以后 select,进行 count,再进行一个 select,保存到某一个表当中。把步骤总结一下,第一步,创建环境,创建环境指的是 SparkSession,导入一些隐式转换,这些都是环境上的东西,对应的如图
第二步,读取数据,判断数据是否读取到了。
第三步,进行流程的计算,数据处理。
第四步,数据的落地。
③整个的内容大致就是这四步,这四步有哪些步是可以重用的?哪些步没有个性化,就是所有人都要有的。这个时候要再强调一遍,抽取代码的意义是因为有很多报表要统计,要对很多报表进行统计,但是这些报表有很多共同点,这些不同的报表统计之间的共同点,大家觉得可能是这四步当中的哪几步?写任何一个 spark 程序,都要导入 sparksession,导入一些隐式转换。第二步,读取数据,在进行报表统计的时候都要读取 ods 层的数据,第二步也是公共点。第三步,按照数据集统计在地域的分布和按照广告在地域的分步,他的数据处理是不一样的,所以数据处理上会有区别,数据处理不是一个通用的步骤,就是个性化的步骤,每一个报表的统计都不一样。所有报表都要进行一次数据落地,这样就知道了第一步,第二步都可以直接抽取出来。在第二步抽取的时候每一个报表统计的类需要提供什么东西?有没有一些个性化的点?不同的报表,读取的表可能不一样,所以表名是一个个性化的点。第三步,数据处理,整体就是个性化的点。第四步,数据落地,不同的报表中落地的表名可能是不同的,表的 schema 也不同,keys 肯定也不同。如果为每一个报表提供一个公共父类的话,那么这个公共父类或者每一个报表的工具应该提供什么东西?第一个要提供一个要读取表的表名,数据处理的过程,数据落地的时候要提供数据落地的表名,提供落地的表 schema,提供落地的表 keys,这些都是个性化的点。这个报表的类应该怎样设计呢?
3、公共流程
(1)先来看一看公共的流程,公共流程第一步,创建 sparksession 对象。
第二步,读取表,虽然名字不同,但是读取的动作所有的报表统计中都要有。
第三步,处理数据,虽然数据处理后生成的表数据不同,但是数据都要经过处理。
第四步,创建落地表,虽然落地表的结构、表名、分区 key 不同,但是都要创建这样一张表。
第五步,落地数据,虽然要落地的表明不同,但是都要落地。
这五步是公共的流程,为这些报表提供个性化的点该怎么提供,这些报表都有什么个性化的点?
4、框架设计
(1)可变点 Processor
首先,报表的类暂定为 processor,processor 要对整个流程提供处理过程,提供源表名称,提供目标表的名称,提供目标表的结构,提供目标表的分区 key,这是需要提供的,所以 processor 这样设计,如下图
(2)公共流程 DailyaReportRunner
创建 sparksession,读取源表,处理数据,创建目标表,保存数据到目标表中,就是这样一个流程。
首先,创建 sparksession,
其次,下图是不同报表的数据
processor 是一个 List,processor 当中是所有的报表处理的 processor,因为这个 processor 是一个 List,所以要针对每一个 processor 进行处理,所以在 for 循环当中,拿到 processor 获取到 sourcetablename,然后进行公共的数据处理,再进行源表名称的获取,源表的 scheme 获取,源表的分区 key 获取,然后把数据 save 到源表中,大致的流程就是这样。