高效解码二进制和文本结构(数据包)

背景

有一个名为Wireshark的着名工具。 我已经使用了很多年了。 这很棒,但性能是问题所在。 通用使用场景包括若干数据准备步骤,以便提取稍后要分析的数据子集。 如果没有这一步,则需要几分钟才能进行过滤(Wireshark旁边的大痕迹无法使用)。

在此处输入图像描述

实际的想法是创建一个更快,更平行,更高效的更好的解决方案,用作数据聚合器/存储。

要求

实际要求是使用现代硬件提供的所有电源。 我应该说有一个不同类型的优化空间,我希望我在上层做得很好,但技术是现在的主要问题。 根据目前的设计,有几种类型的包解码器(解剖器):

  • 交互式解码器 :解码逻辑可以在运行时轻松更改。 这种方法对协议开发人员非常有用 – 解码速度并不重要,但灵活性和快速结果更为重要
  • 可嵌入的解码器 :可以用作库。这种类型应该具有良好的性能,并且足够灵活,可以使用所有可用的CPU和内核
  • 解码器即服务 :可以通过干净的API访问。 这种类型应提供最佳的品种性能和效率

结果

我目前的解决方案是基于JVM的解码器。 实际的想法是重用代码,消除移植等,但仍然具有良好的效率。

  • 交互式解码器 :在Groovy上实现
  • 可嵌入解码器 :在Java上实现
  • 解码器即服务 :Tomcat +优化+可嵌入解码器包装到servlet中(二进制输入,XML输出)

需要解决的问题

  • Groovy提供了强大的function和一切,但在这种特殊情况下运气表现力
  • 将协议解码为树结构是一个死胡同 – 太多的资源被浪费掉了
  • 内存消耗有点难以控制。 我做了几次优化但仍不满意分析结果
  • 带有各种铃声和口哨声的Tomcat仍然会引入很多开销(主要是连接处理)

我在各地使用JVM做得对吗? 您是否看到了实现最初目标的任何其他优秀和优雅的方法:获得易于编写的高度可扩展且高效的协议解码器?

协议,结果格式等不固定。

我发现了几个可能的改进:

交互式解码器

通过使用AST转换扩展Groovy语法,可以极大地改善Groovy表达性。 因此,有可能简化解码器创作仍然提供良好的性能。 AST(代表抽象语法树)是一种编译时技术。

当Groovy编译器编译Groovy脚本和类时,在该过程的某个时刻,源代码最终将以具体语法树的forms在内存中表示,然后转换为抽象语法树。 AST转换的目的是让开发人员进入编译过程,以便在将AST转换为将由JVM运行的字节码之前修改AST。

我不想重新发明轮子引入另一种语言来定义/描述协议结构(它足以拥有ASN.1 )。 这个想法是简化解码器开发,以提供一些快速原型技术。 基本上,将引入某种DSL。

进一步阅读

嵌入式解码器

Java可能会引入一些额外的开销。 有几个库可以解决这个问题:

  • HPPC
  • 特罗韦
  • Javolution
  • 下议院的基元

在此处输入图像描述

坦率地说,除了Java之外,我没有看到任何其他选项。

解码器即服务

此层不需要Java。 最后我有一个很好的选择,但价格相当高。 GWan看起来非常好。

在此处输入图像描述

需要一些额外的移植,但绝对值得。

这个问题似乎与许多高性能I / O实现问题具有相同的特征,即内存副本的数量在性能上占主导地位。 异步I / O的分散 – 聚集接口模式遵循此原则。 通过分散 – 聚集,可以在适当的位置操作存储器块。 只要协议解码器将块流作为输入而不是字节流,您就可以消除大量移动内存以保留字节流抽象的性能开销。 字节流是一种非常好的抽象,可以节省工程时间,但对于高性能I / O却不太好。

在一个相关的问题中,我只是因为基本类型String而要小心JVM。 我不能说我熟悉如何在JVM中实现String ,但我确实认为没有办法在不进行内存复制的情况下从块列表中创建字符串。 另一方面,本地类型的字符串可以兼容地与JVM String互操作,这可能是分裂差异的一种方式。


这个问题的另一个方面似乎是正式语言。 本着不复制内存块的精神,您也不希望一遍又一遍地扫描同一块内存。 由于您希望进行运行时更改,这意味着您可能不希望使用预编译状态机,而是使用递归下降解析器,可以在每个下降级别调度到适当的协议解释器。 当外层没有指定内层的类型时,涉及一些复杂性。 当你甚至没有得到内部内容的长度时,这些并发症会更糟,因为那时你依靠内在的内容来形成良好的以防止失控。 然而,值得注意的是要了解扫描一个块的次数。

网络流量正在增长( 某些分析 ),因此需要每秒处理越来越多的数据。

实现这一目标的唯一方法是使用更多CPU功率,但CPU频率稳定。 只有核心数量在增长。 看起来唯一的方法是更有效地使用可用内核并更好地扩展。

在此处输入图像描述