Flink global snapshot, restore, two-phase commit
快照原理
-
chandy Lamport算法的一种变体被称为异步屏障快照
-
容错机制-障碍的实现原理
主要是通过不断生成快照来实现。快照主要包含两部分数据,一部分是数据流,另一部分是状态数据。
相应的快照机制有两个组成部分: Barrier和State。因为数据在DAG中流动,所以需要满足它以获取快照。在此时间之前的所有数据都被处理,在此时间之后的数据都不被处理.
Barrier 原理
一个Stream barrier 被插入到数据流中,并与数据一起流动,带有快照id。
一旦接收操作符收到所有输入数据流的barrier n,它将向检查点协调器发送一个快照 n 确认。当所有sink都确认快照 n 时,系统认为当前n的快照已经完成
Alignmen 机制
快速数据流将存储在运算符的输入缓冲区中。当所有屏障到达后,先对缓冲区中的数据进行处理,再进行处理。
事务 - 二阶段提交
- 简要介绍
它是最基本的分布式一致性协议
引入一个中心节点来对所有节点的执行逻辑和进度达成一致,其他节点称为参与者
-
过程
-
请求阶段
-
协调器将准备请求和事务内容发送给所有参与者,询问是否可以准备事务提交,并等待参与者的响应。
-
参与者在事务中执行操作,并记录撤消日志(用于回滚)和重做日志(用于重播),但不实际提交。
-
参与者将事务的执行结果返回给协调器。如果执行成功,则返回yes。否则,返回no
-
-
提交阶段(分为成功和失败)
-
如果所有参与者都返回yes,则可以提交事务。
-
协调器向所有参与者发送提交请求。
-
参与者收到提交请求后,提交事务,释放已占用的事务资源,并向协调器返回一个ack。
-
协调器接收来自所有参与者的ack消息,事务成功完成。
-
如果参与者返回no或不返回超时时间,则事务将被中断并需要回滚。
-
协调器向所有参与者发送回滚请求。
-
参与者收到回滚请求后,根据undo日志回滚到事务执行前的状态,释放已占用的事务资源,并向协调器返回ack。
-
协调器接收来自所有参与者的ack消息,事务回滚完成。
-
2pc(二阶段提交)的优缺点
优势
- 原理简单,易于理解和实现
缺点
-
协调器 coordinator 有一个单点问题
-
在大并发下存在阻塞问题,因为它是一个同步进程
-
存在由提交失败引起的不一致
基于2PC应用的flink
- flink的内部意图检查点机制和轻量级分布式快照算法ABS保证了精确一次。其次,如果我们想要实现端到端精确的一次性输出逻辑,我们需要施加以下两个限制之一: 幂等写和事务性写。
TwoPhaseCommitSinkFunction(基于2PC)帮助我们做一些基本的工作
- Flink官方建议所有需要确保只继承这个抽象类一次的接收逻辑。它具体定义了以下四个抽象方法。我们需要在子类中实现它。
//Start a transaction and return the handle of transaction information
protected abstract TXN beginTransaction() throws Exception;
//Logic of the pre submit (i.e. submit request) phase
protected abstract void preCommit(TXN transaction) throws Exception;
//Logic of formal submission phase
protected abstract void commit(TXN transaction);
//Cancel transaction
protected abstract void abort(TXN transaction);
- 2PC在flink和kafka集成中的具体过程
(只有kafka0.11及以上版本支持幂等生产者和事务,所以2PC有存在的意义)
kafka的事务和幂等引用。
-
Flink 的实现
//FlinkKafkaProducer011. The commit () method actually proxies kafkaproducer The committransaction () method formally submits the transaction to Kafka. @Override protected void commit(KafkaTransactionState transaction) { if (transaction.isTransactional()) { try { transaction.producer.commitTransaction(); } finally { recycleTransactionalProducer(transaction.producer); } } } //The call point of this method is located at twophasecommitsinkfunction In the notifycheckpointcomplete () method, as the name suggests, this method will be called when all checkpoints are successful. @Override public final void notifyCheckpointComplete(long checkpointId) throws Exception { Iterator<Map.Entry<Long, TransactionHolder<TXN>>> pendingTransactionIterator = pendingCommitTransactions.entrySet().iterator(); checkState(pendingTransactionIterator.hasNext(), "checkpoint completed, but no transaction pending"); Throwable firstError = null; while (pendingTransactionIterator.hasNext()) { Map.Entry<Long, TransactionHolder<TXN>> entry = pendingTransactionIterator.next(); Long pendingTransactionCheckpointId = entry.getKey(); TransactionHolder<TXN> pendingTransaction = entry.getValue(); if (pendingTransactionCheckpointId > checkpointId) { continue; } LOG.info("{} - checkpoint {} complete, committing transaction {} from checkpoint {}", name(), checkpointId, pendingTransaction, pendingTransactionCheckpointId); logWarningIfTimeoutAlmostReached(pendingTransaction); try { commit(pendingTransaction.handle); } catch (Throwable t) { if (firstError == null) { firstError = t; } } LOG.debug("{} - committed checkpoint transaction {}", name(), pendingTransaction); pendingTransactionIterator.remove(); } if (firstError != null) { throw new FlinkRuntimeException("Committing one of transactions failed, logging first encountered failure", firstError); } }
-
从代码中可以看出,该方法每次都从等待提交的事务句柄中取出一个,检查它的检查点ID,并调用commit()方法提交。该阶段的流程图如下:
在这里插入图片描述 -
可以看到,只有当所有检查点都成功时,写操作才会成功。这与前面描述的2PC过程是一致的。Jobmanager是协调器,每个操作符是参与者,sink中的一个参与者将执行提交。一旦检查点失败,notifyCheckpointComplete()方法将不会被执行。如果重试失败,将初始化重试。最后,调用abort()方法来回滚事务。
@Override protected void abort(KafkaTransactionState transaction) { if (transaction.isTransactional()) { transaction.producer.abortTransaction(); recycleTransactionalProducer(transaction.producer); } }
-