Flink - FlinkKafkaProducer010
https://ci.apache.org/projects/flink/flink-docs-release-1.3/dev/connectors/kafka.html
使用的方式,
DataStream<String> stream = ...; FlinkKafkaProducer010Configuration myProducerConfig = FlinkKafkaProducer010.writeToKafkaWithTimestamps(
stream, // input stream
"my-topic", // target topic
new SimpleStringSchema(), // serialization schema
properties); // custom configuration for KafkaProducer (including broker list) // the following is necessary for at-least-once delivery guarantee
myProducerConfig.setLogFailuresOnly(false); // "false" by default
myProducerConfig.setFlushOnCheckpoint(true); // "false" by default
Besides enabling Flink’s checkpointing, you should also configure the setter methods setLogFailuresOnly(boolean)
andsetFlushOnCheckpoint(boolean)
appropriately, as shown in the above examples in the previous section:
setLogFailuresOnly(boolean)
: enabling this will let the producer log failures only instead of catching and rethrowing them. This essentially accounts the record to have succeeded, even if it was never written to the target Kafka topic. This must be disabled for at-least-once.setFlushOnCheckpoint(boolean)
: with this enabled, Flink’s checkpoints will wait for any on-the-fly records at the time of the checkpoint to be acknowledged by Kafka before succeeding the checkpoint. This ensures that all records before the checkpoint have been written to Kafka. This must be enabled for at-least-once.
Note: By default, the number of retries is set to “0”. This means that when setLogFailuresOnly
is set to false
, the producer fails immediately on errors, including leader changes. The value is set to “0” by default to avoid duplicate messages in the target topic that are caused by retries. For most production environments with frequent broker changes, we recommend setting the number of retries to a higher value.
setLogFailuresOnly
,如果true,发送kafka失败时,只是log,不会中断执行,这样可能丢数据
如果false,发送kafka失败时,抛异常,这样job会restart,不会丢数据,但是会中断执行;这里最好把produer的retires设成3,这样避免kafka临时不可用导致job中断,比如leader切换
setFlushOnCheckpoint
,如果true,在做checkpoint的时候,会等待所有pending的record被发送成功,这样保证数据不丢
首先FlinkKafkaProducer010是一种sink,
一般的使用方式是,steam.addSink(RichSinkFunction)
public DataStreamSink<T> addSink(SinkFunction<T> sinkFunction) {
this.transformation.getOutputType();
if(sinkFunction instanceof InputTypeConfigurable) {
((InputTypeConfigurable)sinkFunction).setInputType(this.getType(), this.getExecutionConfig());
} StreamSink sinkOperator = new StreamSink((SinkFunction)this.clean(sinkFunction));
DataStreamSink sink = new DataStreamSink(this, sinkOperator);
this.getExecutionEnvironment().addOperator(sink.getTransformation());
return sink;
}
这里用FlinkKafkaProducer010.writeToKafkaWithTimestamps封装这部分,比较tricky
/**
* Creates a FlinkKafkaProducer for a given topic. The sink produces a DataStream to
* the topic.
*
* This constructor allows writing timestamps to Kafka, it follow approach (b) (see above)
*
* @param inStream The stream to write to Kafka
* @param topicId The name of the target topic
* @param serializationSchema A serializable serialization schema for turning user objects into a kafka-consumable byte[] supporting key/value messages
* @param producerConfig Configuration properties for the KafkaProducer. 'bootstrap.servers.' is the only required argument.
* @param customPartitioner A serializable partitioner for assigning messages to Kafka partitions.
*/
public static <T> FlinkKafkaProducer010Configuration<T> writeToKafkaWithTimestamps(DataStream<T> inStream,
String topicId,
KeyedSerializationSchema<T> serializationSchema,
Properties producerConfig,
KafkaPartitioner<T> customPartitioner) { GenericTypeInfo<Object> objectTypeInfo = new GenericTypeInfo<>(Object.class);
FlinkKafkaProducer010<T> kafkaProducer = new FlinkKafkaProducer010<>(topicId, serializationSchema, producerConfig, customPartitioner);
SingleOutputStreamOperator<Object> transformation = inStream.transform("FlinKafkaProducer 0.10.x", objectTypeInfo, kafkaProducer);
return new FlinkKafkaProducer010Configuration<>(transformation, kafkaProducer);
}
可以看到这里实现了addSink的逻辑,返回FlinkKafkaProducer010Configuration,其实就是DataStreamSink
public static class FlinkKafkaProducer010Configuration<T> extends DataStreamSink<T> { private final FlinkKafkaProducerBase wrappedProducerBase;
private final FlinkKafkaProducer010 producer; private FlinkKafkaProducer010Configuration(DataStream stream, FlinkKafkaProducer010<T> producer) {
//noinspection unchecked
super(stream, producer);
this.producer = producer;
this.wrappedProducerBase = (FlinkKafkaProducerBase) producer.userFunction;
}
关键是FlinkKafkaProducer010扩展StreamSink并重写
processElement
public class FlinkKafkaProducer010<T> extends StreamSink<T> implements SinkFunction<T>, RichFunction { public FlinkKafkaProducer010(String topicId, KeyedSerializationSchema<T> serializationSchema, Properties producerConfig, KafkaPartitioner<T> customPartitioner) {
// We create a Kafka 09 producer instance here and only "override" (by intercepting) the
// invoke call.
super(new FlinkKafkaProducer09<>(topicId, serializationSchema, producerConfig, customPartitioner)); } @Override
public void processElement(StreamRecord<T> element) throws Exception {
invokeInternal(element.getValue(), element.getTimestamp());
}
StreamSink中processElement是这样实现的,
public class StreamSink<IN> extends AbstractUdfStreamOperator<Object, SinkFunction<IN>>
implements OneInputStreamOperator<IN, Object> { @Override
public void processElement(StreamRecord<IN> element) throws Exception {
userFunction.invoke(element.getValue());
}
可以看到FlinkKafkaProducer010绕开了对SinkFunction的调用,直接调用invokeInternal
所以SinkFunction的实现是无用的,不会被调用到
public void invoke(T value) throws Exception {
invokeInternal(value, Long.MAX_VALUE);
}
invokeInternal
private void invokeInternal(T next, long elementTimestamp) throws Exception { final FlinkKafkaProducerBase<T> internalProducer = (FlinkKafkaProducerBase<T>) userFunction; internalProducer.checkErroneous(); byte[] serializedKey = internalProducer.schema.serializeKey(next);
byte[] serializedValue = internalProducer.schema.serializeValue(next);
String targetTopic = internalProducer.schema.getTargetTopic(next);
if (targetTopic == null) {
targetTopic = internalProducer.defaultTopicId;
} Long timestamp = null;
if(this.writeTimestampToKafka) {
timestamp = elementTimestamp;
} ProducerRecord<byte[], byte[]> record;
if (internalProducer.partitioner == null) {
record = new ProducerRecord<>(targetTopic, null, timestamp, serializedKey, serializedValue);
} else {
record = new ProducerRecord<>(targetTopic, internalProducer.partitioner.partition(next, serializedKey, serializedValue, internalProducer.partitions.length), timestamp, serializedKey, serializedValue);
}
if (internalProducer.flushOnCheckpoint) {
synchronized (internalProducer.pendingRecordsLock) {
internalProducer.pendingRecords++; // 如果flushOnCheckpoint打开,需要记录正在发送的record数目
}
}
internalProducer.producer.send(record, internalProducer.callback);
}
代码很容易理解,正常的producer发送流程,
除了,
internalProducer.checkErroneous();
internalProducer.callback
internalProducer.callback是用来处理kafka返回的ack的
FlinkKafkaProducerBase
@Override
public void open(Configuration configuration) {if (logFailuresOnly) {
callback = new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception e) {
if (e != null) {
LOG.error("Error while sending record to Kafka: " + e.getMessage(), e);
}
acknowledgeMessage();
}
};
}
else {
callback = new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception != null && asyncException == null) {
asyncException = exception;
}
acknowledgeMessage();
}
};
}
}
可以看到logFailuresOnly是true的时候,对于Exception只是,log
如果是false,就会记录下这个Exception到asyncException
acknowledgeMessage,无论是否有错都需要ack
private void acknowledgeMessage() {
if (flushOnCheckpoint) {
synchronized (pendingRecordsLock) {
pendingRecords--;
if (pendingRecords == 0) {
pendingRecordsLock.notifyAll();
}
}
}
}
逻辑就是计数--,如果pendingRecords == 0,即没有正在发送的record,通知所有在等锁的
checkErroneous()
protected void checkErroneous() throws Exception {
Exception e = asyncException;
if (e != null) {
// prevent double throwing
asyncException = null;
throw new Exception("Failed to send data to Kafka: " + e.getMessage(), e);
}
}
就是把asyncException里面的异常抛出去
Flink - FlinkKafkaProducer010的更多相关文章
- flink引出的kafka不同版本的兼容性
参考: 官网协议介绍:http://kafka.apache.org/protocol.html#The_Messages_Fetch kafka协议兼容性 http://www.cnblogs.c ...
- Kafka设计解析(二十)Apache Flink Kafka consumer
转载自 huxihx,原文链接 Apache Flink Kafka consumer Flink提供了Kafka connector用于消费/生产Apache Kafka topic的数据.Flin ...
- 【译】Apache Flink Kafka consumer
Flink提供了Kafka connector用于消费/生产Apache Kafka topic的数据.Flink的Kafka consumer集成了checkpoint机制以提供精确一次的处理语义. ...
- flink统计根据账号每30秒 金额的平均值
package com.zetyun.streaming.flink; import org.apache.flink.api.common.functions.MapFunction;import ...
- FLINK流计算拓扑任务代码分析<一>
我打算以 flink 官方的 例子 <<Monitoring the Wikipedia Edit Stream>> 作为示例,进行 flink 流计算任务 的源码解析说明. ...
- Flink Flow
1. Create environment for stream computing StreamExecutionEnvironment env = StreamExecutionEnvironme ...
- Flink学习笔记:Connectors之kafka
本文为<Flink大数据项目实战>学习笔记,想通过视频系统学习Flink这个最火爆的大数据计算框架的同学,推荐学习课程: Flink大数据项目实战:http://t.cn/EJtKhaz ...
- 关于flink的时间处理不正确的现象复现&原因分析
跟朋友聊天,说输出的时间不对,之前测试没关注到这个,然后就在processing模式下看了下,发现时间确实不正确 然后就debug,看问题在哪,最终分析出了原因,记录如下: 最下面给出了复现方案 ...
- Flink实战(八) - Streaming Connectors 编程
1 概览 1.1 预定义的源和接收器 Flink内置了一些基本数据源和接收器,并且始终可用.该预定义的数据源包括文件,目录和插socket,并从集合和迭代器摄取数据.该预定义的数据接收器支持写入文件和 ...
随机推荐
- python打开文件的N种姿势
# python打开文件的N种姿势 print('[1]使用open()函数+简单for循环') f1 = open('python.txt') for line in f1: print(line. ...
- 【Java】经典示例代码
成鹏致远 | lcw.cnblogs.com | 2014-02-08 单例设计模式 class Singleton{ private static Singleton instance = new ...
- 【iCore1S 双核心板_ARM】例程二:读取ARM按键状态
实验原理: 按键的一端与STM32的GPIO(PB9)相连,且PB9外接一个1k大小的限流上接电阻. 初始化时把PB9设置成输入模式,当按键弹起时,PB9由于上拉电阻的作用呈高电平(3.3V): 当按 ...
- 开源分布式日志系统ExceptionLess部署杂乱笔记 加密
前两天看到了这篇文章,亲身体会了下,确实不错,按照官方的文档试了试本地部署,折腾一番后终于成功,记下心得在此,不敢独享. 本地部署官方wiki .NET 4.6.1 这个因为我装了VS2015,就没有 ...
- linux 环境变量字符串的优先顺序
/data/miniconda3dir/envs/mtfy/bin:$PATH 和$PATH:/data/miniconda3dir/envs/mtfy/bin 区别是非常大. 在linux中不同环境 ...
- [Node.js] 03 - Buffer, Stream and File IO
fs 模块,视频教学 os 模块,视频教学,api doc Buffer类 创建 Buffer 类 // 创建一个长度为 10.且用 0 填充的 Buffer. const buf1 = Buffer ...
- IOS开发之--iPhone XR,iPhone XS Max适配
因为iPhone X和iPhone XS的尺寸比是一样的,只需要把这两张图片补上就行. 具体原理性的东西就多说了,因为iPhoneX系列都一样,本文只说明一下具体怎么做,要适配屏幕,首先得让他以正确的 ...
- Spark排序之SortByKey
sortByKey函数作用于Key-Value形式的RDD,并对Key进行排序. package com.test.spark import org.apache.spark.{SparkConf, ...
- C#WinForm应用程序中嵌入ECharts图表
C#WinForm应用程序中嵌入ECharts图表 程序运行效果: 下载ECharts: 官网下载ECharts :http://echarts.baidu.com/download.html 或者直 ...
- MVC和普通三层架构的区别
MVC和普通三层架构的区别 其中这里的模型(Model)和视图(View )是完全区别于三层架构中的模型(Model)和视图(View)的. MVC 1)MVC中的模型(Model)指的是数据模型,用 ...