00:00
接下来我们就在代码当中来做一个具体的实现啊,那我们还是在当前的包下边新建一个scale object,我们要测试的是CP底层自动状态机的实现,那是一个NFA,所以我们直接把这个叫做n FA example。没方法写出来,哎,那整个这个处理流程呢,其实主要处理的还是连续登录失败的一个检测,我们就把之前这一部分代码直接copy过来。完整的流程先全部复制。上面同样还是先引入下划线,方便做流失处理的影视转换,呃,那后边呢,我们就跟CP没有关系了啊,要做一个自动状态机的实现,然后为了简单起见,我们这里边直接引入的都是顺序输入的数据,这样的话我们就可以不考虑时间相关的信息,直接按照先后到来的顺序去处理就可以了。所以接下来呢,我们要定义的。定义一个结果流stream,它就是基于之前读进来的logging even的stream,哎,那首先我们考虑到我们进行检测的时候,肯定还是按照每个用户自己的行为去进行判断的,那当然首先要做一个分组啊,按照user ID去做一个分组。
01:15
然后接下来呢,哎,我们自然想到需要用到状态编程,既然是状态机嘛,里边自然是要保存一个状态。进一步去思考的话,我们发现如果不考虑时间相关的信息,那其实就会简单很多,我们不用去考虑定时器的设置,也不用考虑水位线相关的信息,那这里的话只用状态编程就可以,所以最简单的方式,那就是我们去实现一个reach flat map方式,在里边去做状态编程就够了,那所以这里边调一个Fla map方法,里边实现一个自定义实现的状态机,哎,那所以这里边我们就叫做state machine。Map。啊,一个转换的过程。接下来的重点当然就是实现。
02:02
自定义的。Flat map方式。Class。我们把这个写出来,State machine map啊,然后接下来extend reach flat map function,那这里需要传入对应的泛型啊,那输入的类型当然是log了,输出的话我们还是直接字符串打印就可以了,String。接下来里边必须要实现的是一个flat map方法,哎,那这个flat map呢,当然就是每来一个数据之后,那我们就要判断当前的状态到底是什么,然后结合当前来的数据啊,它到底是费还是success,按照我们的状态转移图去跳转到下一个状态就可以了,哎,那跳转完了之后呢,我们可以判断一下当前这个状态有没有到match的呢?到match的那就输出对应的这个报警信息,那如果要是terminal的话,那就再跳回到移基手重新开始就可以了。
03:00
所以接下来我们应该在这个rich莱曼方式里边去专门定义一个状态。保存的就是我们当前状态机的状态啊,所以。定义一个。状态机的状态。我们这里定义的当然就是value state了,哎,那这样一个value state里边保存的其实就是我们这里到底是initial还是S1还是S2,还是match的还是terminal啊,那所以我们发现所有的这些啊,我们干脆就让它诶都继承自一个叫做state对应的类啊,或者说实现这样一个特性就可以了啊,一个treat就可以了,所以接下来呢,我们这里定义一个状态机的状态。那我们用这个laz的方式啊,做一个懒加载的定义,我们就把它叫做current state当前的状态。利用get状态con运行上下文,直接get sit,获取一个直状态。那里边我们要传入的是value state script对应我们保存的状态类型,那就叫做state,这个state我们是要单独去定义的。
04:08
啊,那里边的话,我们这个名字就叫state吧,然后另外还需要有一个class of state。所以接下来关键是这个状态,哎,我们这里需要去做一个专门的声明。前面我们说了,我们可以把这个状态直接定义成一个类啊,或者呢,我们还可以把它直接定义成一个特征,或者类似于一个接口啊,我们知道在SKY里面的特征treat就类似于Java里面的interface接口,哎,那所以这里边啊,我们将状态state定义为啊,我们可以定义为在当前类内部啊,一个封闭的特征。Treat。哎,那所以我们这里边使用一个SE的关键字,做一个封闭的定义,然后treat指定当前的state,直接把它定义出来。然后接下来呢,哎,那我们就可以使用case object去实现对应的这个啊,那所有的前面我们提到这些状态啊,具体的这个对象都是对于state的一个具体实现啊,那所以这里边我们就直接case object。
05:16
Initial。Extend,一个state。啊,那同样的啊,这一句我们可以copy一下,另外再定义一个terminal最后的终止状态。它也是一个状态。然后还有一个是最终能够检测到成功的匹配,那是match,哎,这也是一个状态,另外还有两个中间状态。一个叫做S1,另外一个叫做S2,哎,这就是我们对于状态的基本定义,先把所有的状态都声明出来,然后接下来呢,我们还应该定义一个关键的方法,哎,那这个方法或者说函数呢,就是要定义所有状态之间的转换规则,哎,所以这其实本质上就是我们的状态转移方程了,啊,那在下边啊,干脆就直接声明出来吧。
06:07
定义。状态转移函数。所以在这里我们直接DEF,我们就把这个状态转移函数叫做transition。里边需要去传入的参数,当然就是之前的状态到底是什么,这是一个state类型。然后呢,还需要有一个当前的事件到底是什么样的类型,哎,那其实这里当前这个事件呢,我也不需要完整的把老给一的传入,我只需要传入它到底是success还是feel就行了,啊,那所以只需要传一个even type。这是一个string类型。传入这两个参数之后,接下来我通过当前的状态转移函数就可以得到转移之后新的状态是什么,所以返回值类型又是一个state。接下来我们就可以做一个具体的考察,那其实这里就是要考察state和even type这两个值,按照不同的组合,我们就可以让它跳转到下一个状态了,哎,所以我们看在scalela语法里边啊,这种其实根本不用if,使用模式匹配就可以非常容易的实现这个过程啊,那这也是SC语法的一个优势啊,所以我们可以把state放在这里,然后另外还有一个even type,后边我们直接来一个match case,然后做一个模式匹配。
07:28
那首先我们要判断,就是首先在初始状态下,如果说这个时候来了一个success的类型,一个登录成功的事件,那没什么好说的,直接就是termin啊,这个非常简单。那同样。如果说是以你手初始状态下,如果要是来了一个feel的话。这就是我们说的直接就可以跳转到S1状态了,哎,这就是S1。接下来所有的这个过程就跟我们图上定义的一模一样,每一种状态和输入数据的组合,然后就指定它跳转到下一个状态去啊,那前面我们已经处理过了,这个已经受状态,接下来当然要处理的就是S1状态了。
08:13
S1状态如果来一个success的话,哎,那么我们就直接跳转到term,其实我们知道所有的success都是跳转到terminal啊,那S1状态如果要是来一个feel的话。跳转到的是S2状态。哎,那其实我们知道这个过程非常的类似啊,S2。同样也是success来了之后,那就跳转到terminal,如果是S2FEEL来了的话,这里跳转到的是match,直接就匹配成功了啊。所以真正能够接收数据,然后进行各种跳转的,本质上就是initial s1、S2这三个状态。那我们说剩下的两个terminal和match呢?它其实是一个中间的标记状态,因为一到terminal之后,马上它就应该跳转回initial了。
09:05
那如果移到match之后呢,诶,它就应该要跳转回S2了,所以接下来呢,那具体这个跳转我们在哪里去控制呢?不着急,我们可以在具体的map处理流程里边再去做一个对应的跳转,那跳转之前我们还可以该输出报警信息,做一个报警信息的输出啊,所以接下来呢,我们就是在Fla map里边每来一条数据要处理的逻辑,我们可以做一个判断,诶,那首先我们得判断一下到底是不是第一条数据啊,如果我们当前这个counterate里边根本就没有值的话。那就current state直接去获取当前它的value,如果直接就等于now。那很明显,我们就应该把当前的状态设置成初始状态,哎,所以这里边我们就直接currentate去做一个update,指定in initial状态。然后接下来呢,啊,那不管现在的这个状态是什么样的啊,接下来来了一个事件了,来了一个事件,那我们就可以做一个状态的跳转,所以当然就是要定义一个下一个状态,我们就把它叫做next state。
10:10
调用我们定义好的transition这个状态转移函数,传入当前的状态,那是current state value,拿出来当前的状态,然后接下来呢?哎,那还要把我们当前数据里边对应的那个类型,成功还是失败,要做一个传入,那就是硬点even type,哎,这样的话就得到了下一个转移的状态。然后接下来根据下一个这个next state不同,我们就可以做一个判断了,到底要不要输出啊,就可以做一个具体的操作,所以接下来。这个也非常简单啊,我们直接再来一个match case这样的一个模式匹配,假如说我们发现它已经达到了match的这个状态,诶,那就没什么好说的,我们直接可以输出对应的信息了,输出一个报警信息,那就collector,做一个collect,我们要输出的这个信息直接包装成一个字符串硬点。
11:06
User ID当前的这个用户,他连续三次登录。失败啊,那当然了,在我们这个状态机里边,因为没有保存之前每次登录失败的数据,所以这里面肯定就输出不了对应的那个时间戳了啊,只能知道他是否是登录失败了,输出一个报警信息。然后接下来我们会想到,诶,那如果要是达到了这个match的这个状态的话,接下来我们说当前的状态,它应该自动直接就跳转到S2这个状态,诶那是不是我们还要做一个操作,就是诶把这个呃,当前的current state要update成S2呢?其实没必要,因为我们想什么时候能跳转到match呢?那之前调用这个transition状态转移的时候,我们传进来的current state一定就是这里的S2。啊,那所以本来current state就是S2,哎,那你进入到match,然后再重置回S2,那我们什么都不用干,它本来就是S2啊,所以这里面根本就什么都用干。
12:10
当前的操作就做完了,直接输出这个结果就可以。好,那接下来呢,还有另外一种情况case,如果说当前遇到的是terminal这种特殊情况的话。那这个也非常简单,不需要做任何的输出,关键就是把当前的状态要重置,重置回初始状态,哎,那所以跟之前我们一开始判断为none的时候一模一样,直接在这里can't sit up initial就完事了。重置成初始状态。然后最后那如果要是其他情形呢。其他得到的next state,那我们就直接把这个状态更新到current state里边就完事了,所以这里边我们用SC里边的这个语法啊,直接用下划线表示默认的场景,那就把current state update成next state。这就是我们完整的处理逻辑,所以整体来看的话,这个状态机的实现还是思路非常清晰的啊,啊这样的话就是每来一条数据之后,结合之前的状态,就可以指定它跳转到哪个状态啊,那这样的话,输入数据之后,我们就能判断出来是否达到了match的这样一个要检测的匹配状态。
13:21
好,那接下来我们就运行一下吧,看看这个数据测试的结果是不是跟之前完全一样。我们可以看到,果然还是能够检测到USER1是连续三次登录失败,而USER2的话,中间来了success之后就不会输出报警信息了,这就是关于CP状态机的一个实现过程。
我来说两句