Flink流处理批处理socket实时处理详细讲解及从Kafka读取数据实操
文章目录
处理方式分为
- 流式处理(离线)
- 批处理(离线)
- Scoket(使用流处理方式实时处理)
Maven文件
文件名:pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<flink.version>1.10.1</flink.version>
<java.version>1.8</java.version>
<scala.binary.version>2.11.8</scala.binary.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<dependencies>
<!--导入flink对scala的依赖-->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-scala_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
<!--导入flink对java的依赖-->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>${flink.version}</version>
</dependency>
<!--引入flink对批处理的依赖-->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table_2.11</artifactId>
<version>1.7.2</version>
</dependency>
<!--引入flink scala版本对流式处理的依赖 -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-scala_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
<!--引入flink java版本对流式处理的依赖-->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
<!-- <!– 指定mysql-connector的依赖 –>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>-->
<!--log4j日志插件-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- scala编译插件 -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.1.6</version>
<configuration>
<scalaCompatVersion>2.11</scalaCompatVersion>
<scalaVersion>2.11.12</scalaVersion>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<id>compile-scala</id>
<phase>compile</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile-scala</id>
<phase>test-compile</phase>
<goals>
<goal>add-source</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 打jar包插件(会包含所有依赖) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<!-- 可以设置jar包的入口类(可选) -->
<mainClass>com.kevin.batch.BatchWordCountScala</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Flink版本用1.10.2进行批处理时会报错,所以用1.10.1
开启WebUI
添加Pom文件
<!--导入Flink的WebUI界面 端口号默认8081-->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-runtime-web_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
在代码中修改:
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(new Configuration())
代码详解
推荐先看完下面三种模式 ,再来看解释和区别比较清晰
流选择
StreamExecutionEnvironment流常用三种
//如果是在idea里面执行,会使用本地环境,在命令行里面执行jar文件,会使用当前的执行环境
StreamExecutionEnvironment.getExecutionEnvironment();
//使用本地环境
StreamExecutionEnvironment.createLocalEnvironment();
//使用远程环境,参数主机地址、端口号、需要提交的jar包
StreamExecutionEnvironment.createRemoteEnvironment(String host, int port, String... jarFiles);
开启WebUI:
StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(new Configuration())
源码:
public static StreamExecutionEnvironment createRemoteEnvironment(String host, int port, Configuration clientConfig, String... jarFiles) {
return new RemoteStreamEnvironment(host, port, clientConfig, jarFiles);
}
Configuration中可以修改web访问端口号(默认8081)其他设置应该也是可以生效的。
添加源
1.添加文件作为源
DataStream<String> text = env.readTextFile("f://words.txt");
2.添加输入流作为源
3.添加集合作为源
DataStream<Integer> text = env.fromElements(1, 2, 3, 4, 5);
4.添加地址作为源
DataStream<Tuple2<String, Integer>> dataStream = env.socketTextStream("localhost", 9999)
我们来查看socketTextStream源码:
@PublicEvolving
public DataStreamSource<String> socketTextStream(String hostname, int port) {
return this.socketTextStream(hostname, port, "\n");
}
@PublicEvolving
public DataStreamSource<String> socketTextStream(String hostname, int port, String delimiter) {
return this.socketTextStream(hostname, port, delimiter, 0L);
}
@PublicEvolving
public DataStreamSource<String> socketTextStream(String hostname, int port, String delimiter, long maxRetry) {
return this.addSource(new SocketTextStreamFunction(hostname, port, delimiter, maxRetry), (String)"Socket Stream");
}
一共3个方法,最多四个参数,hostname主机地址和port端口号是必须的,delimiter分割符,默认"\n",maxRetry最大重试时间,单位秒,默认0,代表出错立即停止连接,负数代表没有限制反复重连
流处理
对应离线的数据,则规划为有界流;
对于实时的数据划为无界流。
文件内容:
陈佳豪 50分钟
魏德仁 5分钟
狗 15小时
猪 5分钟
文件名:BatchWordCount.scala
import org.apache.flink.api.scala._
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
/**
* ClassName BatchWordCount
* Description: 使用流处理方式编写WordCount
* Create by 陈佳豪
* Date 2021/10/22 1:29
*/
object BatchWordCount {
def main(args: Array[String]): Unit = {
val input = "D:\\MapReduce_test\\wordCountText\\input"
//设置你的执行环境。 任务执行环境用于定义任务的属性、创建数据源以及最终启动任务的执行 设置批执行环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)//并行度设置为1
// 得到输入数据
val text = env.readTextFile(input)
//使用流式处理统计词频
val value = text.flatMap(_.toLowerCase().split(" "))//将数据转换为小写,然后按照空格进行切分
.map((_, 1))//将数据转化为(word,1)的形式
.filter(_.nonEmpty)//filter过滤掉空的
.keyBy(0)// keyBy(0)代表按照元组第一个元素进行分区
.sum(1)//按照第一个字段求和
value.print()
env.execute("Streaming Count")
如果不添加env.setParallelism(1)设置并行度为一,运行结果前面的1> 代表并行度,也就是多线程的数量的编号
批处理
文件名:BatchWordCount.scala
import org.apache.flink.streaming.api.scala._
import org.apache.flink.api.scala.{DataSet, ExecutionEnvironment}
object BatchWordCount {
def main(args: Array[String]): Unit = {
val filePath = "D:\\MapReduce_test\\wordCountText\\input"
//设置你的执行环境。 任务执行环境用于定义任务的属性、创建数据源以及最终启动任务的执行 设置批执行环境
val env = ExecutionEnvironment.getExecutionEnvironment
// get input data
val text: DataSet[String] = env.readTextFile(filePath)
//使用批处理统计处理词频
val counts = text.flatMap(_.toLowerCase().split(" "))
.map((_, 1)).groupBy(0).sum(1).print()
//执行任务
env.execute("Streaming Count")
}
}
Scoket模式
scala的Scoket可以调用javaAPI,所以与java代码差不多
实现功能大体如下:客户端发送一个序列化参数,里面以空格分割几个参数,在服务端进行接收,并分割字符串,获取到一个字符串数组,然后对其进行模式匹配,返回不同的处理结果,写入到输出流中,客户端获取到返回的结果并打印。
开启Scoket服务
方式1:Java代码开启UDP
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.*;
import java.util.Scanner;
public class SocketForWordCount {
public static void main(String[] args) throws IOException {
// TCP通信,服务端口8888
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("开始等待连接...");
Socket accept = serverSocket.accept();
System.out.println("连接成功...");
// 封装udp并发送数据
Scanner sc = new Scanner(System.in);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
try {
String line = "";
System.out.println("请输入单词串,各单词以空格分割.");
while (!"#".equals(line = sc.nextLine())) {
writer.write(line);
writer.newLine();
writer.flush();
System.out.println("发送:" + line);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
accept.close();
}
}
}
从Scoket读取数据之前要先开启Scoket服务(先运行SocketForWordCount类),然后再运行Scoket流
方式2:windows上部署netcat
下载链接:diegocr/netcat: NetCat for Windows (github.com)
配置环境变量 netcat下载和运行时需要关闭火绒不然会报毒
输入命令:nc -lL -p 8888回车等待启动代码程序
方式3:Scala方式开启Scoket
懒得写
参考java改下就行
Scala版本代码
实时处理数据
Scala版本
//设置执行环境 StreamExecutionEnvironment为流处理
val env = StreamExecutionEnvironment.getExecutionEnvironment
//指定数据源,读取socket
val socketStream = env.socketTextStream("localhost", 8888, '\n')
//对数据集指定转换操作逻辑
val count = socketStream
.flatMap(_.toLowerCase.split(" "))//读取数据转为小写然后按照空格切分
.filter(_.nonEmpty)//filter过滤掉空的
.map((_, 1))//将数据转化为(word,1)的形式
.keyBy(0)// keyBy(0)代表按照元组第一个元素进行分区
.sum(1)//按照第一个字段求和
//将计算结果打印到控制台
count.print()
//指定任务名称并触发流式任务
env.execute("Socket Stream")
}
Java版本代码
实时处理数据
java版本
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;
// 获取Socket传过来的数据进行word count
public class SocketWordCount {
public static void main(String[] args) throws Exception {
// 1.创建流处理环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// 2. 从Socket获取数据
DataStream<String> inputDataStream = env.socketTextStream("localhost",8888);
// 3.基于数据流进行计算
// 3.1 map之后进行flat扁平化,转换为 word,count 的形式 如<hello,1>
DataStream<Tuple2<String,Integer>> resultStream = inputDataStream.flatMap(new MyFlatMapper())
// 3.2 keyBy类似于group by
.keyBy(0)
.sum(1);
// 4.输出结果
resultStream.print();
// 5.启动流任务
env.execute();
}
private static class MyFlatMapper implements FlatMapFunction<String, Tuple2<String,Integer>> {
@Override
public void flatMap(String value, Collector<Tuple2<String, Integer>> out) throws Exception {
// 1.按空格分词
String[] words = value.split(" ");
// 2.将word封装为二元组
for (String word : words) {
Tuple2<String,Integer> tuple2 = new Tuple2<>(word,1);
// 使用out收集器收集数据
out.collect(tuple2);
}
}
}
}
批处理和流处理不同
(1)环境不同
批处理:
val env = ExecutionEnvironment.getExecutionEnvironment
流处理:
val env = StreamExecutionEnvironment.getExecutionEnvironment
(2)启动不同
流处理:
env.execute(“Streaming Count”)
批处理:
启动程序即可。
读取Kafka消息队列的数据作为来源
Maven: pom.xml
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka-0.11_2.11</artifactId>
<version>1.10.0</version>
</dependency>
SourceForKafka.scala
import java.util.Properties
import org.apache.flink.api.common.serialization.SimpleStringSchema
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer011
/**
* 以kafka消息队列的数据作为来源
*/
object SourceForKafka {
def main(args: Array[String]): Unit = {
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//设置kafka相关参数 当然 这是最基础的配置 还可以添加其他的配置
val properties: Properties = new Properties()
properties.setProperty("bootstrap.servers", "10.30.59.160: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")
//创建kafka的DataSource 使用addsource方法
//反序列化方案 SimpleStringSchema 将字节流转换成简单的字符串
val kafkaDstream:DataStream[String] = env.addSource( new FlinkKafkaConsumer011[String]("sensor", new SimpleStringSchema(), properties))
kafkaDstream.print("source for kafka")
env.execute("source test job")
}
}
开启kafka生产者
// kafka数据生产者
./bin/kafka-console-producer.sh --broker-list 10.30.59.160:9092 --topic sensor
ation.StringDeserializer")
properties.setProperty(“auto.offset.reset”, “latest”)
//创建kafka的DataSource 使用addsource方法
//反序列化方案 SimpleStringSchema 将字节流转换成简单的字符串
val kafkaDstream:DataStream[String] = env.addSource( new FlinkKafkaConsumer011[String](“sensor”, new SimpleStringSchema(), properties))
kafkaDstream.print("source for kafka")
env.execute("source test job")
}
}
开启kafka生产者
```shell
// kafka数据生产者
./bin/kafka-console-producer.sh --broker-list 10.30.59.160:9092 --topic sensor