大数据开发的日常工作中,开发人员经常需要使用 Spark、Flink 等计算引擎作为工具来实现一些 业务逻辑 的计算。
以 Spark 为例,开发人员会使用 SparkSQL、DataFrame、RDD 等不同形式的API来实现业务需求。
通常情况下,简单的需求都可以通过 SparkSQL、DataFrame 很方便的实现,其简洁的API也是其深受数据分析师青睐的原因之一。
但是正是因为 SparkSQL、DataFrame 的高层次封装,在 复杂度较高的计算需求 实现中,可能会出现 实现复杂或者API的功能性无法满足,或者千方百计实现需求之后 性能表现低下,代码段复杂而庞大 的情况。
尽管Spark允许开发人员通过UDF、UDAF等形式提供自定义的函数功能,但是此时很多人会选择使用较为底层的RDD接口进行开发:可控性好、开发与调试方便、性能强劲。
但是使用RDD接口来开发业务需求时,很多小的项目团队并没有一个统一的项目规范,需求开发完全由开发人员个人自己发挥。
各个业务项目的大致流程基本是相同的:
创建SparkSession用 spark.table or spark.textFile 等API读取数据源进行RDD的各种 Transformation 和 Action 操作得到数据结果之后再调用 saveAsTable or saveAsTextFile 写入外部数据源中
虽然看起来流程挺一致的,但是其中仍然存在以下问题:
业务代码混乱团队成员代码风格不一,有的喜欢一长串一长串的写,有的喜欢将过程封装即使将过程封装了,但是封装的边界没有明确定义,仍然会有人不小心“越界”读写数据源的API使用不统一各个计算引擎对各个数据源都有不同的读写API接口提供使用,一些比较繁杂的API可能会被人“错误”使用同时也会有人时常忘记对应接口如何使用,反复查阅资料重复的编码工作理论上所有业务项目,除了业务逻辑是变化的之外,其余应该都是一个不变的模板开发人员应该专注于变化的业务逻辑,而不是每次都要分一些精力出来处理其他“边边角角”的事情
没有规范任由团队成员发挥的话,尽管有些成员能写一手漂亮的代码,但是你并不能保证所有人都这么优秀。
时间一久项目中代码的 坏味道 会越来越多,最后混乱的程度可能会超出你的想象。
为了解决以上问题,我们建议:定义一个项目规范,所有业务项目都需要遵守这个规范。
俗话说,有规矩成方圆。
有了项目规范,所有人都遵守这个标准来开发。
有了这个标准,我们就可以在标准化的基础上做很多事情,比如 定义自动化工具来帮助开发人员解放双手。
本文讨论的项目规范可以作为一种参考,以供读者与相关开发人员翻阅。
和Java项目规范类似,以 模块化项目 的结构来定义项目规范可以为业务项目提供 结构化标准,其可以规整所有 混乱的业务项目结构。
项目结构标准化的重要性:
项目统一管理与生成方便快速搭框架所有开发人员遵守相同的编码规范易于交接与维护
以下模块划分和Java项目类似,略微有些细节差异。
业务计算逻辑模块,不应该出现任何 Spark等执行框架的API 以 保持模块独立性与可移植。
理论上该模块可以独立构成一个单机程序执行,这样可以将最重要的业务逻辑根据需要迁移到任意计算引擎中,如 Spark 到 Spark Streaming、Flink 甚至 Hive UDF 等。
对外只提供接口调用,不可直接在外部实例化具体类(工厂模式)所有service业务逻辑需要有对应的测试用例事务控制、所有异常捕获和处理依赖common
项目内通用的常量、枚举、POJO实体类、工具函数等,视情况分离,可集成到 context 中
不包含任何业务逻辑不依赖其他模块相关工具保持单例
Spark或者其他程序 执行入口,负责初始化各种计算引擎的环境变量。
系统 全局配置(conf)与脚本(bin) 集中管理依赖server、api、common程序关键点需要打印日志以便后续debug使用
整个项目中整合了业务逻辑调用、数据源读写等操作的模块,需求简单的情况下可以直接集成到 context 中。
该模块中根据不同的接口操作类,还划分了 dal、service与manager三个包。
1.4.1 dal包
主要是对数据进行操作,如读写常用的库:Hive、MySQL、HBase;以及读写文件系统:HDFS。
dal中的所有使用都由接口来定义,不同的接口实现使用不同的应用框架API,如Spark、Flink应该为两个独立的dal实现,在后续service使用过程中可以自由切换。
需要遵循以下原则:
所有bean对象,定义在dal不得在dal写各种业务逻辑、数据清洗逻辑一张表对应一个dal接口、一个bean,对应多个独立的dao实现不允许在1个dao中同时操作多个表
包结构如下:
basic: basic包下主要放一些基础对象,如BaseDao,所有dao都需要完善 TABLE_NAMEbean: 定义数据源表结构,不同的数据源可以定义在不同的包中,如hive、hbase、mysql等dao: 接口具体实现,用来操作数据表。如:增删改查
1.4.2 service包
和dao对接,一个service对应一个dao,service的使用都由接口来定义。
一个service下有两个实现包:
正常实现包:直接对接dao,简单处理一些判断:如参数不合法校验等。测试实现包:模拟数据,可以不通过dao获取,从本地文件生成或代码中生成。
不同的计算框架有不同的service实现,如spark、flink等(需要传入其环境变量)。
1.4.3 manager包
调用service包实现数据增删改查调用api模块进行业务逻辑组合提供函数接口给context模块调用执行
基于以上项目模块的划分,我们可以看到,api、common是 每次都会变化的业务逻辑和通用属性的抽取,而 context 是根据业务需要的计算引擎和运行环境设置的 执行入口。
以上三个模块都是 根据业务需求变化比较大的,而server模块则是负责对 其他各个模块的调用与整合,最后通过 manager 提供统一的函数接口给 context 入口调用执行。
所以 server 模块是这个项目规范中可以 自动化 起来的重点目标。
基于这个目标,我们开发了一个 大数据业务开发 基准项目的雏形,开发人员能够做到开箱即用,不必再花太多精力在研究计算引擎与各个数据源的接口和API如何调用,专注于业务逻辑的实现,提升开发效率。
项目地址:
使用介绍
org.aisql.bigdata.base.framework 包中提供了几种常见大数据项目需要用到的数据源。
framework 以 模块化项目 的结构提供了 各个数据源基础的Dao、Service接口与默认实现。
配合自动化的代码生成工具,可以一键生成 server 模块的代码文件直接使用。
现在我们来看一下规范+自动化的威力,例如现有 default.t_users 表需要读取。
开发人员仅需要生成代码文件并复制到项目中,写代码如下:
是不是 so easy?
其实所做的内容也就是在 server 模块中封装了 常用的不同计算引擎对不同数据源的读写操作API,并 自动化了 bean、dal、service 三个部分的代码生成。
使得开发人员可以直接使用 service 提供的数据操作接口 读出数据源 后 调用业务计算逻辑 处理完毕后 写入数据源 中。
通过对项目模块的标准化规范,我们可以以一个 比较统一和简单易懂的开发方式 来进行需求落地。
虽然刚开始使用规范的时候会有人觉得繁琐与不耐烦,如果是手动开发的话谁都会烦,都是一些重复性的苦力活儿,这就是框架规范的缺点:特别繁琐。
但是配套做一些自动化工具来使用的话,相信大部分开发人员都会觉得很酸爽,某种程度上标准化项目就是这么来提升开发效率的。