文章

网络流量处理中的协议解析:async/await

async/await

回顾一下前面的解析状态机。它在每一个流表模块提供的flow_t node也就是链接上执行,当执行不下去的时候就跳出来,出让执行权限给其他的链接。看起来像是为每一个链接都实现了一个微型的执行体,使得在同一个执行路径中,让流量处理程序可以并发处理大量的链接。这其实和async/await的异步模式很像。

async/await在实现上涉及到很多概念,但简单来说,它就相当于一个状态机。它作为一个状态机,有一个poll方法,还有返回的执行状态:pending,ready。poll方法用于被状态机驱动函数来执行。pending表示还未完成,类似前面解析状态机中的break。ready表示状态机已经执行完毕。

一个async函数就是一个可以被poll的状态机,其中的await的函数也是可以被poll的状态机,每个await都被转换为一个状态。和前面的解析状态机一样,它也需要一个驱动状态机的轮转的驱动函数,这就是执行器,执行器在不停地调用这个状态机的poll方法。如果遇到pending,相当于解析状态机的break,暂时无法继续。可以执行其他的工作。如果ready,那说明这个状态机结束了。

其实async/await是标准的实现异步操作的方法。流量中的协议解析又是一个典型的异步操作。只不过在用async/await之前,我们先用简陋的状态机来实现了。那么接下来,可以用更好的方式来实现协议解析。

事先的考虑

可以用async/await的语言可以选rust。因为它实现了async/await异步机制,它的”状态机”就是future。rsut中的异步需要一个执行环境才能执行。这些不同的执行环境有不同的适用场景。但对我们的协议解析来说,都过于复杂。前面的解析状态机可以看到,驱动状态机执行的过程并不需要很复杂,因为基于的流量处理模型仅仅需要对解析过程异步操作即可。所以我们只需要实现一个简单的执行器就可以了。

异步协议解析的最基础操作还是前面提到的get_line。前面的get_line在不足一行的时候,它会返回非0。但这种行为方式在async/await的异步操作中并不方便。我们需要让它在不足一行的时候返回pending,也就是说我们需要让这个get_line可以这样用:

get_line().await

那么这个函数就需要被实现为async函数。

get_line最终是要在重组的包中读取数据。重组后的包最终的目的是形成一条流。刚好rust有一个stream trait,它就可以异步地提供一个字节。我们只要在get_line中用stream trait一直读到行尾即可。

流重组就是一个数据包按照序列号排序的过程,这个比较简单。

这样最基础的get_line实现了异步。那我们就可以在一个异步函数中调用它,一行一行得到数据进行解析。

和前面的解析状态机一样,到目前为止,解析过程还只是静态的async/await定义。还需要一个执行器来驱动它。还好这里的场景很简单,不需要考虑调度执行之类的复杂情况,只需要执行async/await这个future的poll方法,判断是pending还是ready即可…

本文由作者按照 CC BY 4.0 进行授权