文章目录
1. Flink 直接连接的数据源
* 将数据输出到控制台
stream.print()
2. Flink使用连接器连接的数据源
- 在 Flink 中,将数据写入外部系统,其实并不是一件难事。与外部系统的交互在任何一个处理算子中都可以实现。例如在 MapFunction 中,我们完全可以构建一个到 Redis 的连接,然后将当前处理的结果保存到 Redis 中。如果考虑到只需建立一次连接,我们也可以利用RichMapFunction,在 open() 生命周期中做连接操作。
- 这样看起来很方便,却会带来很多问题。Flink 作为一个快速的分布式实时流处理系统,对稳定性和容错性要求极高。一旦出现故障,我们应该有能力恢复之前的状态,保障处理结果的正确性。这种性质一般被称作“状态一致性”。Flink 内部提供了一致性检查点(checkpoint)来保障我们可以回滚到正确的状态;但如果我们在处理过程中任意读写外部系统,发生故障后就很难回退到从前了。
- 为了避免这样的问题,Flink 的 DataStream API 专门提供了通过连接器向外部写入数据的方法:
addSink(连接器)
2.1 将数据输出到Kafka (用于无界流数据的实际场景)
真正让Flink 与 Kafka密不可分的是,Flink 与 Kafka 的连接器提供了端到端的精确一次(exactly once)语义保证,这在实际项目中是最高级别的一致性保证。
-
首先在 pom.xml 文件中导入 kafka 依赖:
<dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-connector-kafka_${scala.binary.version}</artifactId> <version>${flink.version}</version> </dependency> <!-- 一般是将数据转为json格式写入到kafka,所以为了后面的例子演示,还导入json依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.75</version> </dependency>
-
启动 Kafka 集群
-
再使用
env.addSink(new FlinkKafkaProducer<>())
将数据写入到kafka。例如:Properties properties = new Properties(); properties.put("bootstrap.servers", "hadoop102:9092"); // kafka地址 env .fromCollection(waterSensors) .map(JSON::toJSONString) .addSink(new FlinkKafkaProducer<String>( "clicks", // 要写入的kafka的主题 new SimpleStringSchema(), // 序列化器 properties );
实际情况下更多的是使用FlinkKafkaProducer类的这个构造器:
Properties sinkConfig = new Properties(); sinkConfig.setProperty("bootstrap.servers", "hadoop102:9092,hadoop103:9092,hadoop104:9092"); env .fromCollection(waterSensors) .map(JSON::toJSONString) .addSink(new FlinkKafkaProducer<WaterSensor>( "default", // 默认topic, 这个一般用不上 new KafkaSerializationSchema<WaterSensor>() { // 自定义的序列化器 @Override public ProducerRecord<byte[], byte[]> serialize(WaterSensor element, @Nullable Long timestamp) { String s = JSON.toJSONString(element); return new ProducerRecord<>("s1", s.getBytes(StandardCharsets.UTF_8)); } }, sinkConfig, // kafka的配置 FlinkKafkaProducer.Semantic.AT_LEAST_ONCE // 一致性语义: 现在只能传入至少一次 );
2.2 将数据输出到Redis (用于无界流数据的实际场景)
Redis 是一个开源的内存式的数据存储,提供了像字符串(string)、哈希表(hash)、列表(list)、集合(set)、排序集合(sorted set)、位图(bitmap)、地理索引和流(stream)等一系列常用的数据结构。因为它运行速度快、支持的数据类型丰富,在实际项目中已经成为了架构优化必不可少的一员,一般用作数据库、缓存,也可以作为消息代理。
-
首先在 pom.xml 文件中导入 kafka 依赖:
<dependency> <groupId>org.apache.bahir</groupId> <artifactId>flink-connector-redis_2.11</artifactId> <version>1.0</version> </dependency>
-
启动 Redis 集群
-
再使用
env.addSink(new RedisSink<>())
将数据写入到Redis。例如:
FlinkJedisPoolConfig conf = new
FlinkJedisPoolConfig.Builder().setHost("hadoop102").build();
env
.addSource(new ClickSource())
.addSink(new RedisSink<Event>(
conf, // Jedis 的连接配置
new RedisMapper() { // 说明怎样将数据转换成可以写入 Redis 的类型
@Override // 返回命令描述符
public String getKeyFromData(Event e) {
return e.user;
}
@Override
public String getValueFromData(Event e) {
return e.url;
}
@Override
public RedisCommandDescription getCommandDescription() {
return new RedisCommandDescription(RedisCommand.HSET, "clicks");
}
}
);