spark工程化项目-Java

spark_demo

spark工程,依据Java项目常见三层架构模式,分为:

  • controller:控制层
  • service:服务层
  • dao:持久层
  • application:应用程序层
  • common:通用类抽象方法
  • util:工具类
  • bean:实体类

在此基础上做了一定程度的封装以及常见案例的demo。

主要目录说明

common

通用类抽象方法。但是此处除了controller、service、application的底层接口及抽象类之外,还有dao层的数据读取接口,由于dao层的底层数据读取做了抽象工厂的方法,在命名上与其他层的抽象方法有所区别,在此处进行说明。

application的抽象接口及抽象类比较简单,只是定义了程序入口的抽象方法:

java 复制代码
// 抽象接口
public interface TApplication {

    void start();
}
// 抽象类,继承TApplication接口
public abstract class Application implements TApplication, Serializable {
    @Override
    public void start() {

    }

    public abstract void start(String master, String appName);
}

controller层与上面一致

java 复制代码
public interface TController {

    void dispatch();

}
public abstract class Controller implements TController{
    @Override
    public abstract void dispatch();
}

dao层原本也是与上面的定义方式一致,但是无法满足我最终的需求,所以就采用了抽象工厂的方式。 我的需求是该项目实现spark-core、spark-sql、spark-streaming的通用工厂项目,可以根据实际需求选择对应的spark实现方式,故dao层采用了抽象工厂的方式实现了这三种数据读取方式。

rdd方面是定义了contextProduct的接口,通过sparkContextProduct继承该接口实现具体的读取方法:

java 复制代码
public interface contextProduct {

    JavaRDD makeRDD(String path);
}

public class sparkContextProduct implements contextProduct{
    sparkContextProduct(){}
    @Override
    public JavaRDD makeRDD(String path) {
        JavaSparkContext sparkContext = projectEnv.getSparkRunEnv().getSparkContext();
        // 此处只读取了textfile的方法,可以继承该接口实现其他的方式读取数据
        return sparkContext.textFile(path);
    }

}

dataset方面也是一样的实现方式,只是实现了JDBC的读取数据方式:

java 复制代码
public interface sessionProduct {
    Dataset makeDataSet();
}
public class sparkSessionProduct implements sessionProduct{
    private HashMap<String, String> options = new HashMap<>();

    sparkSessionProduct(String url, String driver, String user, String passwd, String dbtable){
        this.options.put("url", url);
        this.options.put("driver", driver);
        this.options.put("user", user);
        this.options.put("password", passwd);
        this.options.put("dbtable", dbtable);
    }

    @Override
    public Dataset<Row> makeDataSet() {
        sparkEnv sparkRunEnv = projectEnv.getSparkRunEnv();
        SparkSession sparkSession = sparkRunEnv.getSparkSession();
        Dataset<Row> dataFrame = sparkSession.read().format("jdbc")
                .option("url", this.options.get("url"))
                .option("driver", this.options.get("driver"))
                .option("user", this.options.get("user"))
                .option("password", this.options.get("password"))
                .option("dbtable", this.options.get("dbtable"))
                .load();
        return dataFrame;
    }
}

streaming也是一致,此处实现了socket以及kafka的读取方式

java 复制代码
public interface streamingProduct {
    JavaInputDStream makeDStream();
}
public class sparkStreamingProduct implements streamingProduct, Serializable {
    private String sourceName;
    private HashMap<String, String> options = new HashMap<>();
    private HashMap<String, Object> kafkaConfig = new HashMap<String, Object>();

    sparkStreamingProduct(String source, HashMap<String, String> options){
        this.sourceName = source;
        if (source.equals("socket")){
            this.options.put("address", options.get("address"));
            this.options.put("port", options.get("port"));

        } else if (source.equals("kafka")) {
            this.kafkaConfig.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, options.get("server"));
            this.kafkaConfig.put(ConsumerConfig.GROUP_ID_CONFIG, options.get("groupid"));
            this.kafkaConfig.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
            this.kafkaConfig.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
            this.kafkaConfig.put(ConsumerConfig.AUTO_OFFSET_RESET_DOC, options.get("offset"));
            this.kafkaConfig.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);  // 默认非自动提交
            this.options.put("topic", options.get("topic"));
        }
    }

    @Override
    public JavaInputDStream makeDStream() {
        sparkEnv sparkRunEnv = projectEnv.getSparkRunEnv();
        JavaStreamingContext streamingContext = sparkRunEnv.getStreamingContext();
        if (this.sourceName.equals("socket")){
            return streamingContext.socketTextStream(this.options.get("address"), Integer.valueOf(this.options.get("port")));
        } else if (this.sourceName.equals("kafka")) {
            List<String> topic = Arrays.asList(this.options.get("topic"));  // topic列表
            JavaInputDStream<ConsumerRecord<String, String>> kafkaStream = KafkaUtils.createDirectStream(
                    streamingContext,  // spark streaming context实例
                    LocationStrategies.PreferConsistent(),
                    ConsumerStrategies.<String, String>Subscribe(
                            topic,  // 需要传入collection对象
                            this.kafkaConfig  // 需要传入map对象
                    )
            );
            return kafkaStream;

        }

        return null;
    }
}

除此之外,common目录中还有当前项目的环境对象,包含了spark-corespark-sqlspark-streaming的上下文环境对象的构建方法。

java 复制代码
public class sparkEnv implements Serializable {
    private SparkConf sparkConf;

    private JavaSparkContext sparkContext;
    private SparkSession sparkSession;
    private JavaStreamingContext sparkStreamingContext;

    public sparkEnv(String master, String appName){
        this.sparkConf = new SparkConf().setMaster(master).setAppName(appName);
        // 如果通过streaming读取Kafka数据的时候,不配置kryo序列化,则会抛出异常
        this.sparkConf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer");
    }

    public SparkConf getSparkConf(){return this.sparkConf;}

    public void setSparkContext(){
        this.sparkContext = new JavaSparkContext(this.sparkConf);
    }

    public JavaSparkContext getSparkContext(){
        return this.sparkContext;
    }

    public void setSparkSession(){
        this.sparkSession = SparkSession.builder().config(this.sparkConf).getOrCreate();
    }

    public SparkSession getSparkSession(){
        return this.sparkSession;
    }

    public void setStreamingContext(Duration durations){
        this.sparkStreamingContext = new JavaStreamingContext(this.sparkContext, durations);
    }

    public JavaStreamingContext getStreamingContext(){
        return this.sparkStreamingContext;
    }

    public void clear(){
        if (this.sparkSession!=null){
            this.sparkSession.stop();
        }
        if (this.sparkContext!=null){
            this.sparkContext.stop();
        }
        if (this.sparkStreamingContext!=null){
            this.sparkStreamingContext.stop();
        }

    }

}

在实例化的时候会创建SparkConf对象实例,然后根据需求再创建对应的上下文对象,如果在调用上下文对象的时候没有在application层配置,则会抛出空指针异常。 该对象实例会存放到projectEnv的thread local变量中供其他层调用。

dao

dao层主要是读取数据,具体通过3个子类去实现,

text 复制代码
sparkRDDAppDao
sparkDataSetAppDao
sparkStreamAppDao

sparkAppDao封装了3个方法,分别调用3个子类的具体方法去读取数据。

service

service层的基类sparkAppService提供了一个实现模板,其余子类分别对应了各种具体的实现

text 复制代码
sparkRDDAppService
sparkDSAppService
sparkStreamAppService
sparkStreamKafkaAppService

controller

controller层主要是与service层进行交互,dispatch方法调用具体的service层的dataAnalysis

java 复制代码
public class sparkAppService implements Serializable {
    private sparkAppDao appDao = new sparkAppDao();

    public sparkAppDao getAppDao(){
        return this.appDao;
    }

    public Object dataAnalysis(){
        return null;
    }

}

util

util目录下主要有两个关键的类:projectEnvprojectSettings projectEnv主要是共享spark的上下文环境,projectSettings主要是共享项目内全局的配置,供读取数据时调用。 这两者都要在`application层进行设置(主要也是为了减少配置的地方以及数量,而增加的类)。

application

application是具体的项目主程序入口,基础的demo

java 复制代码
public class sparkApplication extends Application implements Serializable {

    @Override
    public void start(String master, String appName) {

    }

    public static void main(String[] args) {
    }
}

如果需要实现,建议以此demo为基础。 application目录内提供了常见的spark具体案例的实现,包括spark-core、spark-sql、spark-streaming的案例:

text 复制代码
wordCountRDDApplication是基于rdd的word count案例
areaCountDSApplication是基于spark-sql的dataset的统计案例,通过jdbc连接MySQL数据读取数据库统计数据
sparkStreamSocketApplication是基于socket的streaming的word count案例
sparkStreamingKafkaApplication是基于Kafka数据源的streaming的word count案例

可以基于上述的demo实现具体的案例。

环境配置

text 复制代码
java 1.8+
spark 3.0+
mysql 8.0
kafka 3.4.0

maven环境

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>src.main</groupId>
    <artifactId>spark_demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spark.version>3.3.2</spark.version>

    </properties>

    <dependencies>
        <dependency> <!-- Spark dependency -->
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.12</artifactId>
            <version>3.3.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-streaming_2.12</artifactId>
            <version>3.3.2</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.spark/spark-sql -->

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-streaming-kafka-0-10_2.12</artifactId>
            <version>3.3.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_2.12</artifactId>
            <version>3.3.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.12.7</version>
            <scope>provided</scope>
        </dependency>
        <!-- http://repo1.maven.org/maven2/com/alibaba/druid/ -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
        </dependency>
        <!--        <dependency>-->
        <!--            <groupId>log4j</groupId>-->
        <!--            <artifactId>log4j</artifactId>-->
        <!--            <version>1.2.17</version>-->
        <!--        </dependency>-->
        <!--        <dependency>-->
        <!--            <groupId>org.slf4j</groupId>-->
        <!--            <artifactId>slf4j-api</artifactId>-->
        <!--            <version>1.7.30</version>-->
        <!--        </dependency>-->
        <!--        <dependency>-->
        <!--            <groupId>org.slf4j</groupId>-->
        <!--            <artifactId>slf4j-log4j12</artifactId>-->
        <!--            <version>1.7.7</version>-->
        <!--        </dependency>-->
    </dependencies>

</project>

运行

直接运行application层的main入口函数即可,单个application代码即main入口。

代码仓库

github.com/saberbin/sp... 请切换至master分支查看

相关推荐
快手技术18 小时前
Blaze RangePartitioning 算子Native实现全解析
spark·naive
知初~2 天前
出行项目案例
hive·hadoop·redis·sql·mysql·spark·database
努力的小T2 天前
使用 Docker 部署 Apache Spark 集群教程
linux·运维·服务器·docker·容器·spark·云计算
Java资深爱好者2 天前
在Spark中,如何使用DataFrame进行高效的数据处理
大数据·分布式·spark
阿里云大数据AI技术2 天前
美的楼宇科技基于阿里云 EMR Serverless Spark 构建 LakeHouse 湖仓数据平台
大数据·阿里云·spark·serverless·emr
python资深爱好者2 天前
什么容错性以及Spark Streaming如何保证容错性
大数据·分布式·spark
猪猪果泡酒2 天前
spark
spark
weixin_307779133 天前
PySpark实现MERGE INTO的数据合并功能
大数据·python·spark
lucky_syq4 天前
Spark算子:大数据处理的魔法棒
大数据·分布式·spark
D愿你归来仍是少年4 天前
解决Python升级导致PySpark任务异常方案
大数据·开发语言·python·spark