目录
以下笔记基于对尚硅谷Java版Flink(2020版)的学习,Flink版本1.10
Flink安装
Standalone模式
1.解压程序包
2.修改配置文件 conf/flink-conf.yaml
jobmanager.rpc.address 作业管理器访问地址
taskmanager.numberOfTaskSlots 每个任务管理器任务插槽数量,一般设置为CPU核心数
parallelism.default 默认并行数
3.修改配置文件 conf/slaves ,填入其他从动主机名
4.文件分发给其他从动主机
5.启动Flink集群
./start-cluster.sh
访问http://localhost:8081可以对flink集群和任务进行监控管理
6.提交任务
浏览器中提交
在Submit New Job页面上传jar包,填写下面的信息:
入口类
并行数
程序参数
命令行提交
./flink run -c com.aiccfly.wc.StreamWordCount –p 3 StreamWordCount.jar --host lcoalhost –port 7777
./flink list
./flink list -a
./flink cancel 任务ID
立即调用作业算子的 cancel() 方法,以尽快取消它们。如果算子在接到 cancel() 调用后没有停止,Flink 将开始定期中断算子线程的执行,直到所有算子停止为止。
./flink stop 任务ID
更优雅的停止正在运行流作业的方式。stop() 仅适用于 source 实现了StoppableFunction 接口的作业。当用户请求停止作业时,作业的所有 source 都将接收 stop() 方法调用。直到所有 source 正常关闭时,作业才会正常结束。这种方式,使作业正常处理完所有作业。
Yarn模式
Session-cluster 模式
Session-Cluster模式需要先启动集群,然后再提交作业,接着会向yarn申请一块空间后,资源永远保持不变。如果资源满了,下一个作业就无法提交,只能等到yarn中的其中一个作业执行完成后,释放了资源,下个作业才会正常提交。
所有作业共享Dispatcher和ResourceManager;共享资源;适合规模小执行时间短的作业。
在yarn中初始化一个flink集群,开辟指定的资源,以后提交任务都向这里提交。这个flink集群会常驻在yarn集群中,除非手工停止。
1) 启动hadoop集群
2) 启动yarn-session
./yarn-session.sh -n 2 -s 2 -jm 1024 -tm 1024 -nm test -d
其中:
-n(--container):TaskManager的数量。新版本不再使用,可以动态分配。
-s(--slots): 每个TaskManager的slot数量,默认一个slot一个core,默认每个taskmanager的slot的个数为1,有时可以多一些taskmanager,做冗余。
-jm:JobManager的内存(单位MB)。
-tm:每个taskmanager的内存(单位MB)。
-nm:yarn 的appName(现在yarn的ui上的名字)。
-d:后台执行。
3) 执行任务
./flink run -c com.aiccfly.wc.StreamWordCount –p 3 StreamWordCount.jar --host lcoalhost –port 7777
4) 去yarn控制台查看任务状态
5) 取消yarn-session
yarn application --kill application_1577588252906_0001
Per-Job-Cluster 模式
一个Job会对应一个集群,每提交一个作业会根据自身的情况,都会单独向yarn申请资源,直到作业执行完成,一个作业的失败与否并不会影响下一个作业的正常提交和运行。独享Dispatcher和ResourceManager,按需接受资源申请;适合规模大长时间运行的作业。
每次提交都会创建一个新的flink集群,任务之间互相独立,互不影响,方便管理。任务执行完成之后创建的集群也会消失。
1) 启动hadoop集群
2) 不启动yarn-session,直接执行job
./flink run –m yarn-cluster -c com.aiccfly.wc.StreamWordCount FlinkTutorial-1.0-SNAPSHOT-jar-with-dependencies.jar --host lcoalhost –port 7777
–m yarn-cluster为固定写法
Flink 流处理API
Environment -> Source -> Transform -> Sink
执行环境-Environment
getExecutionEnvironment 创建一个执行环境
// 批处理
ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
// 流处理
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
createLocalEnvironment 获取本地执行环境,需要在调用时指定默认的并行度
StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironment(1);
createRemoteEnvironment
获取集群执行环境,将Jar提交到远程服务器。需要在调用时指定JobManager的IP和端口号,并指定要在集群中运行的Jar包
StreamExecutionEnvironment env = StreamExecutionEnvironment.createRemoteEnvironment("jobmanage-hostname", 6123,"YOURPATH//wordcount.jar");
读取数据-Source
// 从集合读取数据
env.fromCollection(Collection<OUT> data)
env.fromElements(OUT... data)
// 从文件读取数据
env.readTextFile(String filePath)
env.readFile(FileInputFormat<OUT> inputFormat, String filePath)
// 从Socket读取数据
env.socketTextStream(String hostname, int port)
env.socketTextStream(String hostname, int port, String delimiter, long maxRetry)
// 从Kafka获取数据
Properties properties = new Properties();
properties.setProperty("bootstrap.servers", "hadoop102:9092");
properties.setProperty("group.id", "consumer-group");
properties.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
properties.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
properties.setProperty("auto.offset.reset", "latest");
DataStreamSource<String> stream = env.addSource(new FlinkKafkaConsumer<String>(
"sensor",
new SimpleStringSchema(),
properties
));
// 自定义Source
env.addSource(SourceFunction<OUT> function)
// 继承SoruceFunction,实现run和cancel方法,run方法里通过ctx.collect(T element)对外输出
数据转换-Transform
// 一进一出
dataStream.map(MapFunction<T,R> mapper)
// 实现MapFunction接口,在map方法里返回新的内容
// 一进多出
dataStream.flatMap(FlatMapFunction<T,R> flatMapper)
// 实现FlatMapFunction接口,在flatMap方法里通过out.collect(T record)添加输出
// 条件过滤
dataStream.filter(FilterFunction<T> filter)
// 实现FilterFunction接口,在filter方法里通过返回True/False表示是否输出
// 流操作的分组,返回KeyedStream<T,Tuple>,会造成数据重分区
dataStream.keyBy(int... fields)
// 流数据为元组的时候,可以使用0开始的位置
dataStream.keyBy(String... fields)
dataStream.keyBy(KeySelector<T,K> key)
// 滚动聚合算子(Rolling Aggregation)
keyedStream.sum(int positionToSum)
keyedStream.sum(String field)
keyedStream.max(int positionToMax)
keyedStream.max(String field)
keyedStream.min(int positionToMin)
keyedStream.min(String field)
// 指定字段以外的部分,取第一条记录的信息
keyedStream.maxBy(int positionToMax)
keyedStream.maxBy(String field)
keyedStream.minBy(int positionToMin)
keyedStream.minBy(String field)
// 指定字段以外的部分,取最大/最小记录的信息
// Reduce,有状态一进一出
keyedStream.reduce(ReduceFunction<T> reducer)
// 实现ReduceFunction接口,在reduce方法里参数1为状态行,参数2为新数据,返回值为输出
// Split 和 Select 分流操作,split为数据打标签,返回SplitStream,select选择数据,已经Deprecated
dataStream.split(OutputSelector<T> outputSelector)
// 实现OutputSelector接口,在select方法里参数为新数据,返回值为数据标签迭代器
splitStream.select(String... outputNames)
// Connect和 CoMap 合流
// 连接两个保持他们类型的数据流,两个数据流被Connect之后,只是被放在了同一个流中,内部依然保持各自的数据和形式不发生任何变化,两个流相互独立
dataStream.connect(DataStream<R> dataStream)
// 变换成统一类型的流
connectedStreams.map(CoMapFunction<IN1,IN2,R> coMapper)
// 实现CoMapFunction接口,在map1和map2方法里分别处理两种类型的数据返回对应的新类型的内容
connectedStreams.flatMap(CoFlatMapFunction<IN1,IN2,R> coFlatMapper)
// 实现CoFlatMapFunction接口,在flatMap1和flatMap2方法里分别处理两种类型的数据通过out.collect(T record)添加输出
// Union合流,类型必须是一样
dataStream.union(DataStream<T>... streams)
DataStream -> map/flatMap/filter/union -> DataStream
DataStream -> keyBy -> KeyedStream -> sum/max/min/Reduce -> DataStream
DataStream -> split -> SplitStream -> select -> DataStream
DataStream -> connect -> ConnectedStreams -> map/flatMap -> DataStream
支持的数据类型
所有的Java和Scala基础数据类型,Int, Double, Long, String, …
Java和Scala元组(Tuples)
Scala样例类(case classes)
Java简单对象(POJOs)
其它(Arrays, Lists, Maps, Enums, 等等)
富函数(Rich Functions)
富函数是DataStream API提供的一个函数类的接口,所有Flink函数类都有其Rich版本。它与常规函数的不同在于,可以获取运行环境的上下文,并拥有一些生命周期方法,所以可以实现更复杂的功能。
RichMapFunction
RichFlatMapFunction
RichFilterFunction
…
Rich Function有一个生命周期的概念。典型的生命周期方法有:
open()方法是rich function的初始化方法,当一个算子例如map或者filter被调用之前open()会被调用。
close()方法是生命周期中的最后一个调用的方法,做一些清理工作。
getRuntimeContext()方法提供了函数的RuntimeContext的一些信息,例如函数执行的并行度,任务的名字,以及state状态
数据输出-Sink
// 添加输出
dataStream.addSink(SinkFunction<T> sinkFunction)
// 输出到Kafka
Properties properties = new Properties();
properties.put("bootstrap.servers", "hadoop102:9092");
dataStream.addSink(new FlinkKafkaProducer<String>("clicks", new SimpleStringSchema(), properties);
// 输出到Redis
FlinkJedisPoolConfig conf = new FlinkJedisPoolConfig.Builder().setHost("hadoop102").build();
dataStream.addSink(new RedisSink<Event>(conf, new MyRedisMapper()));
public static class MyRedisMapper implements RedisMapper<Event> {
@Override
public RedisCommandDescription getCommandDescription() {
return new RedisCommandDescription(RedisCommand.HSET, "clicks");
}
@Override
public String getKeyFromData(Event data) {
return data.user;
}
@Override
public String getValueFromData(Event data) {
return data.url;
}
}
// 输出到Elasticsearch
ArrayList<HttpHost> httpHosts = new ArrayList<>();
httpHosts.add(new HttpHost("hadoop102", 9200, "http"));
ElasticsearchSinkFunction<Event> elasticsearchSinkFunction = new ElasticsearchSinkFunction<Event>() {
@Override
public void process(Event element, RuntimeContext ctx, RequestIndexer indexer) {
HashMap<String, String> data = new HashMap<>();
data.put(element.user, element.url);
IndexRequest request = Requests.indexRequest()
.index("clicks")
.type("type") // Es 6 必须定义 type
.source(data);
indexer.add(request);
}
};
dataStream.addSink(new ElasticsearchSink.Builder<Event>(httpHosts, elasticsearchSinkFunction).build());
// 自定义输出
dataStream.addSink(SinkFunction<T> sinkFunction)
// 可以实现一个SinkFunction接口或者继承一个RichSinkFunction类,在invoke方法中做数据输出