flink读写kafka--写kafka
flink读写kafka--写kafka
介绍
主要介绍实际中flink如何读取写入设置kafka
flink版本:1.13.2
github地址:https://github.com/dahai1996/mdw-flink-quickstart
写入kafka
引入依赖
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
包装下常用设置
public class SinkKafka {
String topic;
int kafkaProducersPoolSize;
Properties propertiesSink = new Properties();
/**
* 获取kafka sink,该sink为string类型数据提供处理,需提交做好数据转换
*
* @param runEnv 执行环境
* @param requestTimeOutMs 超时时间,毫秒
* @param topic 写入的topic名
* @param kafkaProducersPoolSize 覆盖默认生产者池大小,默认为5
*/
public SinkKafka(RunEnv runEnv, String requestTimeOutMs, String topic, int kafkaProducersPoolSize) {
propertiesSink.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, runEnv.getKafkaHost());
propertiesSink.setProperty(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, requestTimeOutMs);
this.topic = topic;
this.kafkaProducersPoolSize = kafkaProducersPoolSize;
}
/**
* 获取kafka sink,该sink为string类型数据提供处理,需提交做好数据转换
*
* @param runEnv 执行环境
* @param requestTimeOutMs 超时时间,毫秒
* @param topic 写入的topic名
*/
public SinkKafka(RunEnv runEnv, String requestTimeOutMs, String topic) {
propertiesSink.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, runEnv.getKafkaHost());
propertiesSink.setProperty(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, requestTimeOutMs);
this.topic = topic;
this.kafkaProducersPoolSize = 5;
}
/**
* 获取kafka sink,使用默认序列化器
* @param kafkaSerializationSchema 指定序列化器
* @return 返回一个kafka sink
*/
public FlinkKafkaProducer<String> getSink(KafkaSerializationSchema<String> kafkaSerializationSchema) {
return new FlinkKafkaProducer<String>(
topic,
kafkaSerializationSchema,
propertiesSink,
FlinkKafkaProducer.Semantic.AT_LEAST_ONCE,
kafkaProducersPoolSize);
}
/**
* 获取kafka sink,使用默认序列化器
* @return 返回一个kafka sink
*/
public FlinkKafkaProducer<String> getSink() {
return new FlinkKafkaProducer<String>(
topic,
new DefaultKafkaSerializationSchema(topic),
propertiesSink,
FlinkKafkaProducer.Semantic.AT_LEAST_ONCE,
kafkaProducersPoolSize);
}
/**
* 获取端到端一致性的kafka sink,使用默认序列化器
* @param transactionTimeoutMs kafka事务提交时间,应该大于checkpoint时间间隔,小于kafka设置中transaction.max.timeout.ms(默认为15分钟)
* @return 返回一个提供事务的kafka sink
*/
public FlinkKafkaProducer<String> getExactlyOnceSink(String transactionTimeoutMs) {
propertiesSink.setProperty(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG,transactionTimeoutMs);
return new FlinkKafkaProducer<String>(
topic,
new DefaultKafkaSerializationSchema(topic),
propertiesSink,
FlinkKafkaProducer.Semantic.EXACTLY_ONCE,
kafkaProducersPoolSize);
}
/**
* 获取端到端一致性的kafka sink
* @param transactionTimeoutMs kafka事务提交时间,应该大于checkpoint时间间隔,小于kafka设置中transaction.max.timeout.ms(默认为15分钟)
* @param kafkaSerializationSchema 指定序列化器
* @return 返回一个提供事务的kafka sink
*/
public FlinkKafkaProducer<String> getExactlyOnceSink(String transactionTimeoutMs,KafkaSerializationSchema<String> kafkaSerializationSchema) {
propertiesSink.setProperty(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG,transactionTimeoutMs);
return new FlinkKafkaProducer<String>(
topic,
new DefaultKafkaSerializationSchema(topic),
propertiesSink,
FlinkKafkaProducer.Semantic.EXACTLY_ONCE,
kafkaProducersPoolSize);
}
public static class DefaultKafkaSerializationSchema implements KafkaSerializationSchema<String> {
String topic;
public DefaultKafkaSerializationSchema(String topic) {
this.topic = topic;
}
@Override
public ProducerRecord<byte[], byte[]> serialize(String element, @Nullable Long timestamp) {
return new ProducerRecord<byte[], byte[]>(topic, element.getBytes(StandardCharsets.UTF_8));
}
}
}
使用
一般情况
FlinkKafkaProducer<String> sink1 = new SinkKafka(uat, "60000","topicName").getSink( );
自定义序列化
FlinkKafkaProducer<String> sink = new SinkKafka(uat, "60000", "topicName").getSink(
new SinkKafka.DefaultKafkaSerializationSchema("topicName")
);
注:DefaultKafkaSerializationSchema 使用utf-8序列化数据,返回string类型
端到端一致性设置
FlinkKafkaProducer<String> sink2 = new SinkKafka(uat, "60000", "topicName").getExactlyOnceSink("60000");
注:这里表示,kafka写入数据的时候会有提交的动作,本质上就是一个标记。
在下一步处理的程序中中,读取kafka的设置中使用 setExactlyOnce()方法(见前文),表示读取有已提交标记的数据,就实现了端到端一致性。本质上是模拟两段式提交。
读取kafka时间戳作为水印
/**
* @param env 流执行环境
* @param sourceKafka kafka数据源
* @param duration 水印空闲时间
* @param name 该步骤name
* @return 一个带水印的kafka数据源,水印来自于kafka自带的时间戳
*/
public static SingleOutputStreamOperator<String> getKafkaSourceWithMonotonousWatermarks(StreamExecutionEnvironment env, FlinkKafkaConsumerBase<String> sourceKafka, Duration duration, String name) {
return env.addSource(sourceKafka).assignTimestampsAndWatermarks(WatermarkStrategy.<String>forMonotonousTimestamps().withIdleness(duration)).name(name);
}
注:这里包装作为了常用方法。读取kafka的时间戳作为水印
注2:本质上是使用:
public final class RecordTimestampAssigner<E> implements TimestampAssigner<E> {
public RecordTimestampAssigner() {
}
public long extractTimestamp(E element, long recordTimestamp) {
return recordTimestamp;
}
}
以此从消息中提取时间戳,这里kafka内部做了优化,帮助完善了内部实现。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~