i-Spark-2

一. Spark 核心 API

[SparkContext]

  • 连接到spark集群,入口点.

[HadoopRDD]

  • 读取hadoop上的数据,

[MapPartitionsRDD]

  • 针对父RDD的每个分区提供了函数构成的新类型RDD.

[PairRDDFunctions]

  • 对偶RDD函数类。
  • 可用于KV类型RDD的附加函数。可以通过隐式转化得到.

[ShuffleRDD]

  • 从Shuffle中计算结果的RDD.

[RDD]

  • 是分区的集合, 弹性分布式数据集, 不可变的数据分区集合.
  • 基本操作(map filter , persist)
  • 特点:
    • 分区列表 //数据
    • 应用给每个切片的计算函数 //行为
    • 到其他RDD的依赖列表 //依赖关系
    • (可选)针对kv类型RDD的分区类
    • (可选)首选位置列表

[DAGScheduler]

  • 高级调度器层面,实现按照阶段(stage) 进行 shuffle
  • 对每个JOB的各阶段(stage)计算有向无环图(DAG),并且跟踪RDD和每个阶段的输出。
  • 找出最小调度运行作业, 将Stage对象以TaskSet方式提交给底层的调度器。
  • 底层调度器实现TaskScheduler, 进而在cluster上运行job.
  • TaskSet已经包含了全部的单独的task,这些Task都能够基于cluster的数据进行正确运行。
  • Stage通过在需要shuffle的边界处将RDD打碎来创建Stage对象。
  • 具有窄依赖的RDD操作(比如map /filter)被管道化至一个taskset中.
  • 而具有shuffle依赖的操作则包含多个Stage(一个进行输出,另一个进行输入)
  • 最后,每个stage都有一个针对其他stage的shuffle依赖,可以计算多个操作。
  • DAG调度器检测首选位置来运行task,通过基于当前的缓存状态,并传递给底层的task调度器来实现。根据shuffle的输出是否丢失处理故障问题。
  • 不是因为随机文件丢失造成的故障会由任务调度程序处理,它在取消整个stage前花一小段时间重试每个任务
  • 为了容错,同一stage可能会运行多次,称之为”attempts”,如果task调度器报告了一个故障(该故障是由于上一个stage丢失输出文件而导致的)DAG调度就会重新提交丢失的stage。这个通过具有 FetchFailed的CompletionEvent对象或者ExecutorLost进行检测的。
  • DAG调度器会等待一段时间看其他节点或task是否失败,然后对丢失的stage重新提交taskset,计算丢失的task。


术语介绍

[ job ]

  • 提交给调度的顶层的工作项目,由 ActiveJob 表示。
  • 是Stage集合。

[Stage]

  • 是task的集合,计算job中的中间结果。同一RDD的每个分区都会应用相同的计算函数。
  • 在shuffle的边界处进行隔离(因此引入了隔断,需要上一个stage完成后,才能得到output结果)
  • Stage有两个子类:
    • 1) ResultStage,用于执行action动作的最终stage。
    • 2) ShuffleMapStage, 对shuffle进行输出文件的写操作的。如果job重用了同一个rdd的话,stage通常可以跨越多个job实现共享。
  • 并行任务的集合,都会计算同一函数。所有task有着同样的shuffle依赖,调度器运行的task DAG 在shuffle边界处划分成不同阶段。调度器以拓扑顺序执行.
  • 每个stage可以shuffleMapStage,该阶段下输出是下一个stage的输入,也可以是resultStage,该阶段 task直接执行spark action。对于shuffleMapStage,需要跟踪每个输出分区所在的节点。
  • 每个stage都有FirstJobId,区分于首次提交的id

[ShuffleMapStage]

  • 产生输出数据,在每次shuffle之前发生。内部含有shuffleDep字段,有相关字段记录产生多少输出以及多少输出可用
  • DAGScheduler.submitMapStage()方法可以单独提交submitMapStage().

[ResultStage]

  • 该阶段在RDD的一些分区中应用函数来计算Action的结果。有些stage并不会在所有分区上执行。例如first(),lookup();

[Task]

  • 单独的工作单元,每个发送给一台主机。

[Cache tracking]

  • Dag调度器找出哪些RDD被缓存,避免不必要的重复计算,同时,也会记住哪些shuffleMap已经输出了结果,避免map端shuffle的重复处理。

[Preferred locations]

  • dag调度器根据rdd的中首选位置属性计算task在哪里运行。

[Cleanup]

  • 运行的job如果完成就会清楚数据结构避免内存泄漏,主要是针对耗时应用。

[ActiveJob]

  • 在Dag调度器中运行job。作业分为两种类型,
    • 1) result job,计算ResultStage来执行action.
    • 2 )map-state job,为shuffleMapState结算计算输出结果以供下游stage使用。主要使用finalStage字段进行类型划分。
  • job只跟踪客户端提交的”leaf” stage,通过调用Dag调度器的submitjob或者- submitMapStage()方法实现.
  • job类型引发之前stage的执行,而且多个job可以共享之前的stage。这些依赖关系由DAG调度器内部管理。

[LiveListenerBus]

  • 异步传输spark监听事件到监听器事件集合中。

[EventLoop]

  • 从caller接受事件,在单独的事件线程中处理所有事件,该类的唯一子类是DAGSchedulerEventProcessLoop。

[LiveListenerBus]

  • 监听器总线,存放Spark监听器事件的队列。用于监控。

[OutputCommitCoordinator]

  • 输出提交协调器.决定提交的输出是否进入hdfs。

[TaskScheduler]

  • 底层的调度器,唯一实现TaskSchedulerImpl。可插拔,同Dag调度器接受task,发送给cluster,运行任务,失败重试,返回事件给DAG调度器。

[TaskSchedulerImpl]

  • TaskScheduler调度器的唯一实现,通过BackendScheduler(后台调度器)实现各种类型集群的任务调度。

[SchedulerBackend]

  • 可插拔的后台调度系统,本地调度,mesos调度,。。。

  • 在SchedulerBackend下方,实现有三种

    • LocalSchedulerBackend 本地后台调度器,启动task.
    • StandaloneSchedulerBackend 独立后台调度器

    • CoarseGrainedSchedulerBackend 粗粒度后台调度器

[Executor]

  • spark程序执行者,通过线程池执行任务。

[Dependency]:依赖

  • NarrowDependency: 子RDD的每个分区依赖于父RDD的少量分区, 也叫完全依赖

    ​ |

    ​ / \

    ​ —

    ​ |—- OneToOneDependency //父子RDD之间的分区存在一对一关系。

    ​ |—- RangeDependency //父RDD的一个分区范围和子RDD存在一对一关系。

    ​ |—- PruneDependency // 在PartitionPruningRDD和其父RDD之间的依赖, 子RDD包含了父RDD的分区子集。

  • ShuffleDependency: 在shuffle阶段输出时的一种依赖, 属于一种宽依赖, 也叫部分依赖

二. Spark 其它概念

1. 创建 Spark 上下文(Context)

[本地模式,通过线程模拟]

​ 本地后台调度器

​ spark local

​ spark local[3] //3线程,模拟cluster集群

​ spark local[*] //匹配cpu个数,

​ spark local[3,2] //3:3个线程,2为maxFailures。

  • // local[*, M] means the number of cores on the computer with M failures
  • // local[N, M] means exactly N threads with M failures
  • 0和1等价,只执行一次, 失败后不会重试

[相当于伪分布式]

​ StandaloneSchedulerBackend

​ spark local-cluster[N, cores, memory] //模拟spark集群。

[完全分布式]

​ StandaloneSchedulerBackend

​ spark spark://cs1:7077 //连接到spark集群上.

2. RDD 持久化

  • 跨操作进行RDD的内存式存储。

  • 持久化RDD时,节点上的每个分区都会保存操内存中,以备在其他操作中进行重用。

  • 缓存技术是迭代式计算和交互式查询的重要工具。

  • 使用persist()和cache()进行rdd的持久化。

  • cache()是persist()一种.

  • action第一次计算时会发生persist().

  • spark的cache是容错的,如果rdd的任何一个分区丢失了,都可以通过最初创建rdd的进行重新计算。

  • persist可以使用不同的存储级别进行持久化。

    1
    2
    3
    4
    5
    6
    7
    8
    MEMORY_ONLY            //只在内存
    MEMORY_AND_DISK
    MEMORY_ONLY_SER //内存存储(串行化)
    MEMORY_AND_DISK_SER
    DISK_ONLY //硬盘 // 默认的路径似乎在程序执行完后自己会删掉??
    MEMORY_ONLY_2 //带有副本
    MEMORY_AND_DISK_2 //快速容错。
    OFF_HEAP // 离堆内存
  • 删除持久化数据 rdd.unpersist();

3.数据传递

  • map(),filter()高级函数中访问的对象被串行化到各个节点。每个节点都有一份拷贝。
  • 变量值并不会回传到driver程序。

4.共享变量

spark通过广播变量和累加器实现共享变量。


[广播变量]

1
2
3
//创建广播变量
val bc1 = sc.broadcast(Array(1,2,3))
bc1.value

[累加器]

1
2
3
4
5
// 创建累加器
val ac1 = sc.longaccumulator("ac1")
ac1.value
sc.parallelize(1 to 10).map(_ * 2).map(e=>{ac1.add(1) ; e}).reduce(_+_)
ac1.value //10

5.通过 Spark 实现 PI 的分布式计算

:TODO 计算出来的结果不精准 ??

1
2
3
4
5
6
7
8
sc.parallelize(1 to 100000000).map(e=>{
val a = 1f / (2 * e - 1) ;
val b = if (e % 2 == 0) -1 else 1 ;
a * b * 4
}).reduce(_+_)

res25: Float = 3.1415968
==> 用了一个亿算出来这个...

三. SparkSQL

Hive //hadoop mr sql
pheonix //hbase之上构建sql交互过程
DataFrame //收据框.表.该模块能在spark运行sql语句。
SparkSQL //SQL | DataFrame API.

1.Spark SQL Shell 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//创建样例类
$scala>case class Customer(id:Int,name:String,age:Int)
//构造数据
$scala>val arr = Array("1,tom,12","2,tomas,13","3,tomasLee,14")
$scala>val rdd1 = parallelize(arr)
//创建对象rdd
$scala>val rdd2 = rdd1.map(e=>{e.split(",") ; Customer(arr(0).toInt,arr(1),arr(2).toInt)})
//通过rdd创建数据框
$scala>val df = spark.createDataFrame(rdd2);
//打印表结构
$scala>df.printSchema
$scala>df.show //插叙数据

//创建临时视图
$scala>df.createTempView("customers")
$scala>val df2 = spark.sql("select * from customers")
$scala>spark.sql("select * from customers").show //使用sql语句
$scala>val df1 = spark.sql("select * from cusotmers where id < 2");
$scala>val df2 = spark.sql("select * from cusotmers where id > 2");
$scala>df1.createTempView("c1")
$scala>df2.createTempView("c2")
$scala>spark.sql("select * from c1 union select * from c2").show
$scala>df1.union(df2);
$scala>spark.sql("select id,name from customers").show
$scala>df.selectExpr("id","name")
$scala> spark.sql("select * from cus where name like 't%' order by name desc").show
$scala>df.where("name like 't%'").show

//映射
$scala>df.map(_.getAs[Int]("age")).reduce(_+_) //聚合操作DataSet[Int]
$scala>df.agg(sum("age"),max("age"),min("age")) //聚合函数

2. IDEA 操作 SQL

具体查看我的 Github

1> 通过读写文件完成基本 SQL 操作

见我的 Github 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 源码 ==> //DataFrame 类似于table操作。
type DataFrame = Dataset[Row]

1> 通过RDD创建 DataFrame
df = sc.createDataFrame(rdd);

2> DataFrame 转回为 RDD
JavaRDD<Row> rdd = df1.toJavaRDD();

3> SparkSession 对象,通过读取 json 文件, 创建 DataSet
Dataset<Row> df = session.read().json("file:///tmp/test.json");

4> 保存spark的sql计算结果为 json 文件
//保存成json文件。 模式为添加模式
df.write().mode(SaveMode.Append).json("file://...")

2> 通过 JDBC 操作 MySQL

  1. 添加 pom 依赖 mysql-connector-java

    1
    2
    3
    4
    5
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.17</version>
    </dependency>
  2. 编写代码, 见我的 Github

3.整合 Hive

1.配置 & Spark-shell 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1> .hive的类库需要在spark worker节点。
----------------------------------------------------------------------------------

2> .复制hive-site.xml(hive) 到 spark/conf下。
----------------------------------------------------------------------------------
(core-site.xml(hdfs) + hdfs-site.xml(hdfs) 就不用复制了, 在 conf/spark-env.sh 中, 配置了 HADOOP_CONF_DIR 就可以了)

3> .复制mysql驱动程序到/soft/spark/jars下 (Hive 的 metadata 存在 mysql)
----------------------------------------------------------------------------------

4> .启动spark-shell,指定启动模式
----------------------------------------------------------------------------------
spark-shell --master local[4]
# 创建 Hive 表
$scala>create table tt(id int,name string , age int)
row format delimited fields terminated by ','
lines terminated by '\n'
stored as textfile

//加载数据到hive表
$scala>spark.sql("load data local inpath 'file:///home/centos/data.txt' into table mydb.tt");

2. Java版SparkSQL 操作 Hive 表

所有的 pom 文件配置

1
2
3
4
5
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-jdbc</artifactId>
<version>2.1.0</version>
</dependency>

Java 代码

详细代码见github

1
2
3
4
5
6
7
8
9
10
SparkConf conf = new SparkConf();
conf.setMaster("local") ;
conf.setAppName("SQLJava");
SparkSession sess = SparkSession.builder()
.appName("HiveSQLJava")
.config("spark.master","local")
.getOrCreate();

Dataset<Row> df = sess.sql("create table mytt(id int)");
df.show();

3. Spark下 分布式访问 Hive (Spark-Shell) —– 开启thriftserver, 通过 beeline 访问 hive

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1> 在默认库下创建hive的数据表。
$>hive -e "create table tt(id int,name string , age int) row format delimited fields terminated by ',' lines terminated by '\n' stored as textfile"

2> 加载数据到hive表中.
$>hive -e "load data local inpath 'file:///home/ap/stu' into table tt"
$>hive -e "select * from tt"

3> 启动 thriftserver 服务器
# 启动 thriftServer
[ap@cs1]~/apps/spark/sbin%> start-thriftserver.sh --master spark://cs1:7077

# 查看
netstat -anop | grep 10000

4> 启动 beeline
# 连接beeline, 记得一定要加上用户名!!!! -n ap
# 下面的情况是, cs1是装了 hive 的
[ap@cs1]~/apps/spark%> bin/beeline -u jdbc:hive2://localhost:10000 -n ap -d org.apache.hive.jdbc.HiveDriver

5> 接下来, 跟直接在hive 中, 通过 beeline 访问 hive, 是一样的操作, 就是直接操作 hive

4. 使用 Java 通过 ThreadServer, 使用 JDBC 访问 Hive

详情看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Class.forName("org.apache.hive.jdbc.HiveDriver");

Connection conn = DriverManager.getConnection("jdbc:hive2://cs1:10000","ap","123");

Statement st = conn.createStatement();

ResultSet rs = st.executeQuery("select * from tt where age > 12 ORDER BY age DESC ");

while (rs.next()) {
int id = rs.getInt(1);
String name = rs.getString(2);
int age = rs.getInt(3);
System.out.println(id + "," + name + "," + age);
}
rs.close();

5. 直接使用 $SPARK_HOME/bin/spark-sql 脚本访问 hive

1.配置 hive-site.xml, 分发到各个节点

1
2
3
4
5
<!--加上一下配置, 指明访问 hive 的 metadata 服务-->
<property>
<name>hive.metastore.uris</name>
<value>thrift://cs2:9083</value>
</property>

2.在装有 hive & mysql 的节点上启动 hive 的 metastore 服务

1
nohup hive --service metastore 1>/home/ap/logs/hive_thriftserver.log 2>&1 &

3.在 cs1节点启动 spark-sql服务, 即可进入 spark-sql 命令行模式

1
$cs1> spark-sql

6.在 idea 上操作 hive

代码见我的github

1.遇到问题

spark-hive_2.1.1 pom 导入失败

解决:

  • 首先看报错时, 缺少哪些库, 然后直接选择 option+return, 有一个自动导入 maven 的选项, 不过最好是自己知道要导入哪些库
  • 如果pom.xml一直导入失败, 看下报错的提示, 有哪些库导入不成功, 可以 reimport, 还是不行的话, 找到报错的类, 找到自己的 maven 仓库, 删掉原来下载好的, 重新 导入, 重新点击 maven 刷新

idea 编码中, 关键点在.enableHiveSupport(), 和pom.xml 配置`

1
2
3
4
5
6
7
8
9
System.setProperty("HADOOP_USER_NAME","ap");
SparkSession sess = SparkSession.builder()
.appName("HiveSQLJava")
.config("spark.master","local")
.enableHiveSupport()
.getOrCreate();
sess.sql("use mydb_test");
Dataset<Row> df = sess.sql("select * from new_help_keyword limit 10");
df.show();
2.自己的 xml 备份
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<?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>com.rox</groupId>
<artifactId>SparkDemo-1</artifactId>
<version>1.0-SNAPSHOT</version>


<build>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<recompileMode>incremental</recompileMode>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.11.8</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-mllib_2.11</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.17</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>2.1.0</version>
</dependency>

<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-jdbc</artifactId>
<version>2.1.0</version>
</dependency>

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
</project>
3.比较全的 xml 备份 (Spark)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
<?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>com.chen</groupId>
<artifactId>spark01</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<project.build.sourceEncoding>UTF8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<encoding>UTF-8</encoding>
<scala.version>2.11.8</scala.version>
<spark.version>2.3.1</spark.version>
<hadoop.version>2.7.6</hadoop.version>
<scala.compat.version>2.11</scala.compat.version>
</properties>

<dependencies>

<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
</dependency>

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>${spark.version}</version>
</dependency>

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>${spark.version}</version>
</dependency>

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-graphx_2.11</artifactId>
<version>${spark.version}</version>
</dependency>

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-mllib_2.11</artifactId>
<version>${spark.version}</version>
</dependency>

<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>

<!-- SparkStreaming和kafka做整合 -->
<!--<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka_2.11</artifactId>
<version>1.6.3</version>
</dependency>-->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
<version>2.3.0</version>
</dependency>

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-flume_2.11</artifactId>
<version>${spark.version}</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.17</version>
</dependency>

<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.11</artifactId>
<version>${spark.version}</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka -->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.11</artifactId>
<version>1.1.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>


</dependencies>

<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>scala-test-compile</id>
<phase>process-test-resources</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
如果帮到你, 可以给我赞助杯咖啡☕️
0%