深圳幻海软件技术有限公司 欢迎您!

初学Tips - 为啥Flink的Java模块需要Scala的版本后缀

2023-02-28

“ From开发者:“flink关于java和scala开发,总是搞不清maven依赖什么情况,能不能讲下?知道flink是在java上面包裹了一层scala,但是添加maven依赖的时候,总是很迷惑...””01 Java 开发者暂时抛开网友的问题,我们先黑盒子方式启

 From 开发者:“ flink关于java和scala开发,总是搞不清maven依赖什么情况,能不能讲下?知道flink是在java上面包裹了一层scala,但是添加maven依赖的时候,总是很迷惑... ”

01 Java 开发者

暂时抛开网友的问题,我们先黑盒子方式启程一下Java开发者构建一个简单示例。初学者我们还是模版走...

我们以mvn命令方式切入,如下命令:

mvn archetype:generate \
    -DarchetypeGroupId=org.apache.flink \
    -DarchetypeArtifactId=flink-walkthrough-datastream-java \
    -DarchetypeVersion=1.13.0 \
    -DgroupId=khkw \
    -DartifactId=helloJava \
    -Dversion=0.1 \
    -Dpackage=khkw \
    -DinteractiveMode=false
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

上面命令最终看到如下成功信息:

接下来,我们看看生成的pom依赖和示例的import情况,我使用Idea查看,所以到hellojava目录下执行 mvn idea:idea 之后用IDE打开。

在properties里面也声明了scala的版本2.11,也及时在dependency依赖里面也使用了scala的版本信息,这一点是网友很困惑的点,我稍后解释。我们在看看Java开发DataStream最核心的依赖是什么?

我们发现最核心的依赖是 flink-streaming-java_2.11,这里虽然是java开发,但是也的确携带了scala的版本后缀。为啥会这样呢?后面说flink整体scala/java设计部分解释,这里只是声明一个依赖是一次性工作,如果引入依赖之后后面的开发没有import的误区那么可以暂时忍受一下。我们重点看到开发过程是否有让人困惑的地方?我们打开FraudDetectionJob作业主类,如下:

和业务相关的import是开发者自己非常清楚的,不必多说,和flink框架相关的引入只有DataStream & StreamExecutionEnvironment ,那么我们在自动import时候是否有import错误的可能呢?这个是Flink java开发者的省心的地方,如下:

这里我们发现,Java用户没有机会犯错,都只有唯一的一个。但对于Scala用户来说就没有那么容易了,我们继续往下看...

02 Scala 开发者

我们同样也是以mvan命令的方式来建立示例工程,如下:

 mvn archetype:generate \
    -DarchetypeGroupId=org.apache.flink \
    -DarchetypeArtifactId=flink-walkthrough-datastream-scala \
    -DarchetypeVersion=1.13.0 \
    -DgroupId=khkw \
    -DartifactId=helloScala \
    -Dversion=0.1 \
    -Dpackage=khkw \
    -DinteractiveMode=false
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

成功创建之后,我们同样打开pom文件查看依赖关系,如下:

这个对于scala用户来说是非常正常的依赖了,不论是artifacId名字还是携带scala版本都是很合理的设计。我们再来看看开发过程类的import情况是否一样明了?

看到上面情况,初学者应该会有点蒙圈了:( ,我需要的类有这么多,怎么选择?

对于Scala开发者看到和类同名的Object其实很容易理解,那么Scala里面也有一个同名的Java实现,对于初学者可能就会稍微犹豫一下,但是既然我们用Scala开发,java和scala并存的时候优先选择Scala实现。

那么为啥有2个同名的实现,我们看看依赖树就感觉是那么回事了,如下:

Flink Runtime是Java开发的,Scala API是建立在Java API基础之上的,所以对于Scala用户来说理论上永远有2个选择,虽然选择是很明确的。

03 为什么Java模块需要Scala版本后缀?

我们还是网友所问的作为切入点:“像java-datastream_2.11这种,都写着java了。后面又分2.11”,问题很好,为什么 flink-streaming-java 模块的artifactId要携带2.11,这个 flink-streaming-java_2.11 让人费解,我们先看现状,依赖关系,如下:


[INFO] org.apache.flink:flink-streaming-java_2.11:jar:1.14-SNAPSHOT
[INFO] +- org.apache.flink:flink-core:jar:1.14-SNAPSHOT:compile
[INFO] |  +- org.apache.flink:flink-annotations:jar:1.14-SNAPSHOT:compile
[INFO] |  +- org.apache.flink:flink-metrics-core:jar:1.14-SNAPSHOT:compile
[INFO] |  +- org.apache.flink:flink-shaded-asm-7:jar:7.1-13.0:compile
[INFO] |  +- org.apache.commons:commons-lang3:jar:3.3.2:compile
[INFO] |  +- com.esotericsoftware.kryo:kryo:jar:2.24.0:compile
[INFO] |  |  \- com.esotericsoftware.minlog:minlog:jar:1.2:compile
[INFO] |  +- commons-collections:commons-collections:jar:3.2.2:compile
[INFO] |  \- org.apache.commons:commons-compress:jar:1.20:compile
[INFO] +- org.apache.flink:flink-file-sink-common:jar:1.14-SNAPSHOT:compile
[INFO] +- org.apache.flink:flink-runtime_2.11:jar:1.14-SNAPSHOT:compile
[INFO] |  +- org.apache.flink:flink-queryable-state-client-java:jar:1.14-SNAPSHOT:compile
[INFO] |  +- org.apache.flink:flink-hadoop-fs:jar:1.14-SNAPSHOT:compile
[INFO] |  +- commons-io:commons-io:jar:2.8.0:compile
[INFO] |  +- org.apache.flink:flink-shaded-netty:jar:4.1.49.Final-13.0:compile
[INFO] |  +- org.apache.flink:flink-shaded-jackson:jar:2.12.1-13.0:compile
[INFO] |  +- org.apache.flink:flink-shaded-zookeeper-3:jar:3.4.14-13.0:compile
[INFO] |  +- commons-cli:commons-cli:jar:1.3.1:compile
[INFO] |  +- org.javassist:javassist:jar:3.24.0-GA:compile
[INFO] |  +- org.scala-lang:scala-library:jar:2.11.12:compile
[INFO] |  +- com.typesafe.akka:akka-actor_2.11:jar:2.5.21:compile
[INFO] |  |  +- com.typesafe:config:jar:1.3.0:compile
[INFO] |  |  \- org.scala-lang.modules:scala-java8-compat_2.11:jar:0.7.0:compile
[INFO] |  +- com.typesafe.akka:akka-stream_2.11:jar:2.5.21:compile
[INFO] |  |  +- org.reactivestreams:reactive-streams:jar:1.0.2:compile
[INFO] |  |  \- com.typesafe:ssl-config-core_2.11:jar:0.3.7:compile
[INFO] |  |     \- org.scala-lang.modules:scala-parser-combinators_2.11:jar:1.1.1:compile
[INFO] |  +- com.typesafe.akka:akka-protobuf_2.11:jar:2.5.21:compile
[INFO] |  +- com.typesafe.akka:akka-slf4j_2.11:jar:2.5.21:compile
[INFO] |  +- org.clapper:grizzled-slf4j_2.11:jar:1.3.2:compile
[INFO] |  +- com.github.scopt:scopt_2.11:jar:3.5.0:compile
[INFO] |  +- org.xerial.snappy:snappy-java:jar:1.1.8.3:compile
[INFO] |  \- org.lz4:lz4-java:jar:1.6.0:compile
[INFO] +- org.apache.flink:flink-java:jar:1.14-SNAPSHOT:compile
[INFO] +- org.apache.flink:flink-shaded-guava:jar:18.0-13.0:compile
[INFO] +- org.apache.commons:commons-math3:jar:3.5:compile
[INFO] +- org.apache.flink:flink-core:test-jar:tests:1.14-SNAPSHOT:test
[INFO] +- org.apache.flink:flink-test-utils-junit:jar:1.14-SNAPSHOT:test
[INFO] +- org.apache.flink:flink-runtime_2.11:test-jar:tests:1.14-SNAPSHOT:test
[INFO] +- org.apache.flink:flink-shaded-force-shading:jar:13.0:compile
[INFO] +- org.slf4j:slf4j-api:jar:1.7.15:compile
[INFO] +- com.google.code.findbugs:jsr305:jar:1.3.9:compile
[INFO] +- junit:junit:jar:4.13.2:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] +- org.mockito:mockito-core:jar:2.21.0:test
[INFO] |  +- net.bytebuddy:byte-buddy:jar:1.8.15:test
[INFO] |  +- net.bytebuddy:byte-buddy-agent:jar:1.8.15:test
[INFO] |  \- org.objenesis:objenesis:jar:2.1:compile
[INFO] +- org.powermock:powermock-module-junit4:jar:2.0.4:test
[INFO] |  \- org.powermock:powermock-module-junit4-common:jar:2.0.4:test
[INFO] |     +- org.powermock:powermock-reflect:jar:2.0.4:test
[INFO] |     \- org.powermock:powermock-core:jar:2.0.4:test
[INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.4:test
[INFO] |  \- org.powermock:powermock-api-support:jar:2.0.4:test
[INFO] +- org.hamcrest:hamcrest-all:jar:1.3:test
[INFO] +- org.apache.logging.log4j:log4j-slf4j-impl:jar:2.14.1:test
[INFO] +- org.apache.logging.log4j:log4j-api:jar:2.14.1:test
[INFO] +- org.apache.logging.log4j:log4j-core:jar:2.14.1:test
[INFO] \- org.apache.logging.log4j:log4j-1.2-api:jar:2.14.1:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  42.595 s
[INFO] Finished at: 2021-06-26T08:57:22+08:00
[INFO] ------------------------------------------------------------------------
  • 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.

我们发现flink-streaming-java,依赖了flink-runtime_2.11也是有scala版本后缀的,一样的困惑,为啥纯java写的flink runtime要一携带scala的版本号,继续追,发现flink-runtime也是依赖了akka-actor/stream/protobuf_2.11,也就是flink依赖的三方库虽然是纯java的但是同样携带了scala版本。按照这种钻取方式我们就要去挖akka-stream为啥携带scala版本后缀了, 那这个问题就到了,akka是50+的代码都是用scala写的,查看 https://github.com/akka/akka 。 

04 未来会怎样?

用户的眼光都是雪亮的,问题都是一针见血的,这问题也的的确确是Flink社区需要解决的,也发起了去掉Scala依赖的讨论。

详情:

https://lists.apache.org/thread.html/rd7bf0dabe2d75adb9f97a1879638711d04cfce0774d31b033acae0b8%40%3Cdev.flink.apache.org%3E

05 Flink从那一层主动使用Scala?

那么上面Java模块需要scala后缀是被动的,那么Flink从哪个层面是主动引入Scala的呢?这个主要是从Flink开发者受众角度思考的,Flink虽然核心是Java写的,但是更加期望更多的非Java的开发者可以在不切换开发语言的前提下便捷的使用Flink。

对于Scala用户天然能用Java的API但是Scala相对于Java来说从语言API上面有很多的优势,所以Flink社区为了不改变Scala用户的开发习惯,为Scala用户提供了Scala的API,所以Flink主动引入Scala是从API层面切入的。Flink社区致力于Scala API包括所有的Java API功能,同时前面版本也在Scala API封装了一些易用的、符合Scala编程习惯的API封装。

从Flink所有的Module看,只有 flink-scala/flink-stream-scala/flink-table模块涉及到了scala的依赖,flink-scala是基础封装,包括 type,codegen,metric等基础部分,而flink-stream-scala是纯面向Scala用户的DataStream API部分,flink-talbe是里面有scala部分的SQL层面封装。多说一句,不仅仅是Java/Scala的用户受众,Flink更期望对多语言有很好的支持,PyFlink就迈出了Flink 多语言支持的第一步~~ :)

06 勿以善小而不为

行善之人,如春园小草,不见其长,日有所增,作恶之人,若磨刀砺石,不见其损,日有所亏, 可谓,人为善,福虽未至,祸已远离;人为恶,祸虽未至,福已远离!

作者介绍

孙金城,51CTO社区编辑,Apache Flink PMC 成员,Apache Beam Committer,Apache IoTDB PMC 成员,ALC Beijing 成员,Apache ShenYu 导师,Apache 软件基金会成员。关注技术领域流计算和时序数据存储。