Flink 提供两种主要的处理语义:
- At-least-once:至少一次,保证每个记录至少被处理一次,可能会被处理多次。这种语义易于实现但不能保证精确的结果。
- Exactly-once:精确一次,保证每个记录只被处理一次,实现精确的结果。这需要更复杂的传输和检查点机制来实现。
这两种语义的主要区别在于:
- 处理结果:至少一次可能重复,精确一次保证单一
- 数据传输:至少一次简单通过 ACK 确认,精确一次需要两阶段提交
- 容错机制:至少一次通过重试实现,精确一次需要基于检查点的容错
- 实现难度:至少一次简单,精确一次复杂
我们可以通过 StreamExecutionEnvironment 的 setProcessingTimeCharacteristic() 方法设置程序的处理语义。
例如:
- At-least-once:
env.setProcessingTimeCharacteristic(TimeCharacteristic.ProcessingTime);
- Exactly-once:
env.setProcessingTimeCharacteristic(TimeCharacteristic.IngestionTime);
然后 Flink 会根据设置采取不同的措施来提供对应的语义保证:
- At-least-once:通过 ACK 确认和重试机制保证每个记录最终处理。
- Exactly-once:通过两阶段提交协议和基于检查点的容错机制确保每个记录仅处理一次。
例如一个简单的 Flink 程序:
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setProcessingTimeCharacteristic(TimeCharacteristic.ProcessingTime);
DataStream<String> stream = env.socketTextStream("host", port);
stream.map(s -> {
// 处理逻辑
return s;
})
.print();
env.execute();
如果我们将 setProcessingTimeCharacteristic() 设置为 IngestionTime,该程序将以精确一次的语义执行;
如果设置为 ProcessingTime,程序将以至少一次的语义执行。
所以,理解 Flink 提供的不同处理语义及其实现方式,可以让我们根据实际需要选择合适的语义。Exactly-once 语义可以实现更精确的结果,但是开发和运行成本也更高。根据任务要求权衡取舍,可以让 Flink 的处理效果达到最佳。