flink cep 源码分析
1 前文介绍
前文中我们已经介绍啦关于cep的原理和缓存以及数据结构
https://blog.youkuaiyun.com/u013052725/article/details/100631259
接着我们看一看如何flink当中是如何实现的。其中cep版本是1.6.0。
2 首先我们思考四个问题:
a)事件和事件间的关联以及版本是如何在flink当中存储的。
b)Computation state 在flink 中是如何实现的
c)事件到来是如何根据Computation state 来计算有哪些边(take,process,ignore) ,以及如何更新Computation state
d)事件间的版本号如何生成
e)如何根据缓存数据以及版本号进行回溯输出结果
3 源码分析
a)我们先看看flink中 shared version match buffer 是如何实现的。
flink中实现缓存的实现:SharedBuffer类
SharedBuffer类中有三个存储数据的结构如下:
注:其中MapState 是flink中的状态我们可以理解为就是一个map,不过其数据可能存储在内存也可能持久化在磁盘上,详细说明看官网:
https://ci.apache.org/projects/flink/flink-docs-release-1.9/zh/dev/stream/state/state.html
/** The buffer holding the unique events seen so far. */
private MapState<EventId, Lockable<V>> eventsBuffer;
/** The number of events seen so far in the stream per timestamp. */
private MapState<Long, Integer> eventsCount;
private MapState<NodeId, Lockable<SharedBufferNode>> entries;
EventId:每个事件会对应一个id
NodeId:包括EventId和事件所在state的名字
SharedBufferNode:存放一个节点所对应的边,可能有多条,以集合形式存在,其中每条边有版本号和target节点
eventsBuffer:key是EventId value是对应的值,存储所以匹配到的事件的id和对应的value值,用于输出的时候根据id取得数据。
eventsCount:key是事件的时间戳,value是这个事件在缓存中的数量
entries:key是NodeId 是事件在该缓存中的一个标志,value 是SharedBufferNode 存放这个节点所对应的各个版本的前置节点。匹配到的事件会存放在这个缓存当中,并且保存事件间的关系和版本号。匹配过程中主要使用的就是这个版本。
b)Computation state 在flink中的数据结构
Computation state 的数量代表啦当前run的数量每个 Computation state就是这个run的当前状态
// pointer to the NFA currentStateName of the computation
private final String currentStateName;
// The current version of the currentStateName to discriminate the valid pattern paths in the SharedBuffer
private final DeweyNumber version;
// Timestamp of the first element in the pattern
private final long startTimestamp;
@Nullable
private final NodeId previousBufferEntry;
@Nullable
private final EventId startEventID;
currentStateName:描述当前run所在的state。可以根据state取得当前 state的事件迁移条件来判断事件的迁移情况。
version:当前state所对应的版本号。可以用于下个state版本号的计算和终止时回溯事件
startTimestamp:事件开始事件,用于判断是否超时
startEventID:开始事件id
previousBufferEntry:当前run的最后事件节点 下一个事件到达如果take会作为新的最后节点。新节点指向之前的节点。
c)事件到来的处理流程
如下方法doProcess 事件到达的处理流程
private Collection<Map<String, List<T>>> doProcess(
final SharedBuffer<T> sharedBuffer,
final NFAState nfaState,
final EventWrapper event,
final AfterMatchSkipStrategy afterMatchSkipStrategy) throws Exception {
//需要匹配的ComputationState列表
final PriorityQueue<ComputationState> newPartialMatches = new PriorityQueue<>(NFAState.COMPUTATION_STATE_COMPARATOR);
//匹配完成的ComputationState列表
final PriorityQueue<ComputationState> potentialMatches = new PriorityQueue<>(NFAState.COMPUTATION_STATE_COMPARATOR);
//迭代当前所有需要匹配的ComputationState
for (ComputationState computationState : nfaState.getPartialMatches()) {
//根据当前computationState和事件计算出下一个或者多个ComputationState,并且存储匹配到事件到sharebuffer中,这个方法很重要下面讲解
final Collection<ComputationState> newComputationStates = computeNextStates(
sharedBuffer,
computationState,
event,
event.getTimestamp());
if (newComputationStates.size() != 1) {
nfaState.setStateChanged();
} else if (!newComputationStates.iterator().next().equals(computationState)) {
nfaState.setStateChanged();
}
//delay adding new computation states in case a stop state is reached and we discard the path.
final Collection<ComputationState> statesToRetain = new ArrayList<>();
//if stop state reached in this path
boolean shouldDiscardPath = false;
//遍历添加到添加到对应的列表中
for (final ComputationState newComputationState : newComputationStates) {
if (isFinalState(newComputationState)) {
potentialMatches.add(newComputationState);
} else if (isStopState(newComputationState)) {
//reached stop state. release entry for the