说明
flink是一个快速的实时处理引擎,他可以在收到数据的一瞬间就把数据给处理成我们想要的样子,是目前最高效率的实时数据处理技术,那么我们常用于手机实时数据的kafka中的数据flink可以实时接收并处理吗?
答案是肯定的,但是几个步骤
步骤
1.flink只兼容kafka2.2.X以上的版本,没有的小伙伴及时更新
2.在你的idea中导入kafka的连接依赖
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka_2.11</artifactId>
<version>1.10.0</version>
</dependency>
3.在程序当中创建一个properties实例,而后.setProperty()各种各样你需要的参数
4.创建一个flink个kafka的生产者的一个连接器并传参
new FlinkKafkaConsumer(话题名称,一个简单的框架,属性)
5.而后再add.Source(连接器的名字)添加一个来源
具体代码
public class ConnectKafka {
public static void main(String[] args)throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
//new 一个实例!
Properties properties = new Properties();
//告诉程序我们要接收那台机器上生产的数据
properties.setProperty("bootstrap.servers","doit01:9092,doit02:9092,doit03:9092");
//告诉程序开启分区,已经分区名称
properties.setProperty("group.id","qwer");
//告诉程序我需要开启一个偏移量的记录,并且是从头开始读的
properties.setProperty("auto.offset.reset","earliest");
//告诉kafka你不要自动提交偏移量了,每次搜自动提交偏移量
properties.setProperty("enable.auto.commit","false");
//如果FlinkKafkaConsumer没有开启checkpoint功能,为了不重复读取
//这种方式无法实现Exactly-Once(只执行一次)
FlinkKafkaConsumer <String>flinkKafkaConsumer = new FlinkKafkaConsumer("wordcount2", new SimpleStringSchema(), properties);
DataStreamSource<String> lines = env.addSource(flinkKafkaConsumer);
lines.print();
env.execute( "wordcoun");
//而后flink就可以监听kafka之中的数据了
}
}
当然这样的程序是不健壮的,也许有个报错就会导致程序废掉,所以我们要做到Exactly-Once(执行且只执行一次),就会让我们的程序变得很健壮,不会因为一点小病小灾就会夭折!
步骤
1.开启一个检查站(巡回执行,巡回检查),存储策略和执行(伪重启)策略
env.enableCheckpointing(时间参数毫秒级的例如:30000(30秒))
env.setStateBackend(new FsStateBackend(args[0]));
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(3,5000));
2.复制一个wordcount的逻辑到程序中程序
3.开启flink的服务而后将程序打成jar包在web页面去执行
目标
我们要将我们实时处理的程序写入到hfds中!
问题
当值你直接执行的时候会报Server Response Message:
Internal server error.(内部服务器异常)
我们需要将将提前准备好的跟flink对应版本的hadoop包(flink-shaded-hadoop-2-uber-2.7.5-10.0.jar)直接放在flink的集群中一劳永益
代码思路
读取kafka的数据写入到redis数据库中去,并且保证程序健壮
我们为了让程序只执行一次,更加健壮
我们将数据结果写入redis数据库中
1.首先我们导入连接redis数据库的依赖
<dependency>
<groupId>org.apache.bahir</groupId>
<artifactId>flink-connector-redis_2.11</artifactId>
<version>1.0</version>
</dependency>
2.
(1)调用sink将将计算好的结果保存到redis中
(redis支持多种数据类型的数据k,v类型的写到redis里面的数据必须是String)
我们在程序当中直接将官网当中的代码考过来(路径自己看)
3. //创建jedis(客户端)连接配置信息
new FlinkjedisPoolConfig.Builder()传入参数
4. //添加一个sink路径
addSink(new RedisSink<>(conf,new RedisWordCountMapper()));
RedisWordCountMapper是要重写的传入名称和k,v的字段位置即可将结果写入到redis中
代码实现
public class ConnectKafka {
public static void main(String[] args)throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
//1.开启一个检查站Checkpoint,
//2.(存储策略)将快照的数据存到一个指定的存储系统里面
//3.(重启策略)设置检查站每隔多长时间检查一次,一共检查多少次
env.enableCheckpointing(30000);
env.setStateBackend(new FsStateBackend(args[0]));
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(3,5000));
//new 一个实例!
Properties properties = new Properties();
//告诉程序我们要接收那台机器上生产的数据
properties.setProperty("bootstrap.servers","doit01:9092,doit02:9092,doit03:9092");
//告诉程序开启分区,已经分区名称
properties.setProperty("group.id","args[1]");
//告诉程序我需要开启一个偏移量的记录,并且是从头开始读的
properties.setProperty("auto.offset.reset","earliest");
//告诉kafka你不要自动提交偏移量了,每次搜自动提交偏移量
properties.setProperty("enable.auto.commit","false");
//如果FlinkKafkaConsumer没有开启checkpoint功能,为了不重复读取
//这种方式无法实现Exactly-Once(只执行一次)
FlinkKafkaConsumer <String>flinkKafkaConsumer = new FlinkKafkaConsumer("wordcount2", new SimpleStringSchema(), properties);
//检查的时候将kafka的偏移量保存到kafka特殊的topic,默认的是ture
flinkKafkaConsumer.setCommitOffsetsOnCheckpoints(false);
DataStreamSource<String> lines = env.addSource(flinkKafkaConsumer);
//我拷贝过来的wordcount的逻辑
SingleOutputStreamOperator<Tuple2<String, Integer>> wordAndOne = lines.flatMap(new FlatMapFunction<String, Tuple2<String, Integer>>() {
@Override
public void flatMap(String line, Collector<Tuple2<String, Integer>> out) throws Exception {
String[] words = line.split(" ");
for (String word : words) {
out.collect(Tuple2.of(word, 1));
}
}
});
//分组
KeyedStream<Tuple2<String, Integer>, Tuple> keyed = wordAndOne.keyBy(0);
//聚合
SingleOutputStreamOperator<Tuple2<String, Integer>> summed = keyed.sum(1);
//调用sink
FlinkJedisPoolConfig conf = new FlinkJedisPoolConfig.Builder()
.setHost(args[3])
.setPassword(args[4])
.build();
summed.addSink(new RedisSink<>(conf,new RedisWordCountMapper()));
env.execute( "wordcoun");
}
//重写的程序
private static class RedisWordCountMapper implements RedisMapper<Tuple2<String, Integer>> {
@Override
public RedisCommandDescription getCommandDescription() {
//指定写入Redis中的方法和最外面的大key的名称
return new RedisCommandDescription(RedisCommand.HSET,"wc");
}
@Override
public String getKeyFromData(Tuple2<String, Integer> data) {
//将数据中的哪个字段作为key写入
return data.f0;
}
@Override
public String getValueFromData(Tuple2<String, Integer> data) {
//将数据中的那个字段作为value写入
return data.f1.toString();
}
}
}