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分支查看

相关推荐
蚂蚁数据AntData15 小时前
流批一体向量化计算引擎 Flex 在蚂蚁的探索和实践
大数据·数据仓库·spark·数据库架构
lucky_syq2 天前
Spark和MapReduce之间的区别?
大数据·spark·mapreduce
lucky_syq2 天前
Hive SQL和Spark SQL的区别?
hive·sql·spark
lucky_syq2 天前
Spark和Hadoop之间的区别
大数据·hadoop·spark
NiNg_1_2343 天前
Spark常用的转化操作和动作操作详解
大数据·ajax·spark
岑梓铭3 天前
(CentOs系统虚拟机)Standalone模式下安装部署“基于Python编写”的Spark框架
linux·python·spark·centos
Data跳动3 天前
Spark内存都消耗在哪里了?
大数据·分布式·spark
lucky_syq3 天前
流式处理,为什么Flink比Spark Streaming好?
大数据·flink·spark
goTsHgo3 天前
在 Spark 上实现 Graph Embedding
大数据·spark·embedding
程序猿小柒3 天前
【Spark】Spark SQL执行计划-精简版
大数据·sql·spark