☰
Zeek Storage Framework 异步操作完全指南:Storage::Async 模块的 API 详解与源码级实战
2026/10/8 1:27:45 网站建设 项目流程
  • 网络安全
  • 网络
  • IDS

【免费下载链接】zeek

Zeek is a powerful network analysis framework that is much different from the typical IDS you may know.

项目地址:https://gitcode.com/gh_mirrors/ze/zeek
点击查看免费下载

导读

本文聚焦 Zeek 的 storage framework 中异步操作模块Storage::Async。storage framework 为 Zeek 提供基于插件的短/长期数据存储能力,以键值对形式在脚本层读写数据;而Storage::Async是其中以when条件配合使用的异步 API 模块,适用于不能阻塞 Zeek 主事件循环的场景。读完本文,你将掌握open_backend/close_backend/put/get/erase五个异步函数的完整签名、参数语义、返回值结构,理解异步与同步模式的分派机制,并能基于仓库中的真实测试用例写出可运行的 Zeek 存储脚本。


1. Storage::Async 模块概览

Storage::Async定义在 scripts/base/frameworks/storage/async.zeek,模块注释明确其职责为 "Asynchronous operation methods for the storage framework"(storage framework 的异步操作方法)。其 API 文档即 doc/scripts/base/frameworks/storage/async.zeek.rst,对应的接口摘要如下:

函数说明
Storage::Async::open_backend基于配置对象异步打开一个新的后端连接
Storage::Async::close_backend异步关闭一个已有的后端连接
Storage::Async::put异步向后端插入一条新记录
Storage::Async::get异步从后端取回一条记录
Storage::Async::erase异步从后端删除一条记录

该模块通过@load ./main依赖基础模块 scripts/base/frameworks/storage/main.zeek(命名空间Storage),因此在使用Storage::Async之前无需手动加载main,直接@load base/frameworks/storage/async即可,同时会获得Storage::BackendOptions、Storage::PutArgs、Storage::OperationResult等类型定义。模块的自动装载入口见 scripts/base/frameworks/storage/load.zeek。

1.1 异步模式与同步模式的分工

storage framework 将脚本层 API 拆分为两个平级模块:Storage::Async(异步)与Storage::Sync(同步,见 scripts/base/frameworks/storage/sync.zeek)。二者参数与返回值完全一致,唯一区别在于:

  • 异步函数必须写在when条件内调用,否则会返回错误;
  • 同步函数可直接调用,也可以放进when,但会阻塞直到后端返回数据。

两者均通过各函数体内的__前缀内建函数(如Storage::Async::__open_backend)最终接入 src/storage/storage.bif 所声明的 C++ 实现,因此异步 API 的底层由原生代码支撑,脚本层只是分派层。


2. 五个异步 API 的完整签名与语义

以下签名与参数说明均严格对齐 doc/scripts/base/frameworks/storage/async.zeek.rst 及源码 scripts/base/frameworks/storage/async.zeek。

2.1 open_backend:异步建立后端连接

function open_backend(btype: Storage::Backend, options: Storage::BackendOptions, key_type: any, val_type: any): Storage::OperationResult
参数类型说明
btypeStorage::Backend(enum)指示要打开哪种后端,取值由已加载的后端插件定义,内置Storage::STORAGE_BACKEND_REDIS与Storage::STORAGE_BACKEND_SQLITE
optionsStorage::BackendOptions(record)连接配置记录;后端插件可通过 redef 该记录追加自身字段
key_typeany后端中键的脚本层类型,用于校验传入其他方法(get、erase等)的键
val_typeany后端中值的脚本层类型,既用于校验传给put的值,也用于get返回值的类型转换

返回值:一个Storage::OperationResult记录;成功时其value字段为opaque of Storage::BackendHandle(后端句柄),失败时在error_str中给出错误信息。

后端句柄是opaque类型,与普通 Zeek 值一样可以存放在全局变量中,可在zeek_init事件里打开一次、在整个 Zeek 运行期间复用。打开成功后框架会抛出Storage::backend_opened事件。

2.2 close_backend:异步关闭后端连接

function close_backend(backend: opaque of Storage::BackendHandle) : Storage::OperationResult
  • backend:要关闭的后端连接句柄。
  • 返回值:包含操作状态与可选错误字符串的OperationResult。

关闭成功后抛出Storage::backend_lost事件;该事件同样会在连接意外中断时触发(如 Redis 服务端掉线),供脚本感知连接故障并执行重连等恢复逻辑。

2.3 put:异步写入键值对

function put(backend: opaque of Storage::BackendHandle, args: Storage::PutArgs): Storage::OperationResult
  • backend:后端连接句柄。
  • args:Storage::PutArgs记录,封装写入参数(见下文 §3.2)。
  • 返回值:操作状态与可选错误字符串。

2.4 get:异步读取键值对

function get(backend: opaque of Storage::BackendHandle, key: any) : Storage::OperationResult
  • backend:后端连接句柄。
  • key:要查找的键。
  • 返回值:成功时value字段携带所取回的值,其类型与open_backend时传入的val_type一致;若键不存在,后端应返回Storage::KEY_NOT_FOUND。

2.5 erase:异步删除键值对

function erase(backend: opaque of Storage::BackendHandle, key: any) : Storage::OperationResult
  • backend:后端连接句柄。
  • key:要删除的键。
  • 返回值:操作状态与可选错误字符串;键不存在时返回Storage::KEY_NOT_FOUND。

3. 支撑类型:BackendOptions、PutArgs 与 OperationResult

3.1 Storage::BackendOptions:后端连接配置

定义于 scripts/base/frameworks/storage/main.zeek(第 13–21 行),是传给open_backend的基础配置记录,后端插件可 redef 追加字段:

字段类型默认值说明
serializerStorage::Serializer(enum)Storage::STORAGE_SERIALIZER_JSON用于转换 Zeek 数据的序列化器
forced_syncboolStorage::default_forced_sync(默认F)是否强制进入同步模式;设为T后即便调用异步函数也会走同步路径,通常只在测试时使用

其中default_forced_sync是可 redef 的全局选项,默认F。加载具体后端策略后该记录会扩展字段,例如加载policy/frameworks/storage/backend/sqlite后新增sqlite: Storage::Backend::SQLite::Options(含database_path、table_name等),加载 Redis 策略后新增redis: Storage::Backend::Redis::Options,详见 doc/scripts/base/frameworks/storage/main.zeek.rst。

框架同时提供可 redef 的Storage::latency_metric_bounds(vector of double,默认[0.001, 0.01, 0.1, 1.0]),用于定义操作时延指标的直方图分桶(单位秒)。

3.2 Storage::PutArgs:put 的参数载体

字段类型默认值说明
keyany—存储所用的键
valueany—与该键关联的值
overwriteboolT键已存在时是否覆盖旧值
expire_timeinterval0sec条目自动过期并被后端移除的时间;0sec表示永不过期

3.3 Storage::OperationResult:统一返回结构

定义于 scripts/base/init-bare.zeek(第 6814–6826 行):

type OperationResult: record { code: ReturnCode; # 后端可 redef 的返回码 error_str: string &optional; # 失败时设置,非 SUCCESS 时应存在 value: any &optional; # get 返回命中值;open_backend 返回后端句柄 };

code字段取自Storage::ReturnCode枚举,仓库内置以下取值:

返回码含义
SUCCESS操作成功
VAL_TYPE_MISMATCH传入值的类型与打开后端时声明的值类型不符
KEY_TYPE_MISMATCH传入键的类型与打开后端时声明的键类型不符
NOT_CONNECTED后端未连接
TIMEOUT操作超时
CONNECTION_LOST后端连接意外丢失
OPERATION_FAILED通用操作失败
KEY_NOT_FOUND键在后端中不存在
KEY_EXISTS待覆盖的键已存在
CONNECTION_FAILED连接建立失败(区别于已建立后丢失)
DISCONNECTION_FAILED断开失败
INITIALIZATION_FAILED初始化失败
IN_PROGRESS异步操作正在等待结果(由异步操作返回)

ReturnCode带&redef,后端可追加自定义状态码。注意并非所有返回码对所有操作都合法。


4. 源码级剖析:异步函数如何分派

阅读 scripts/base/frameworks/storage/async.zeek 第 81–126 行可以看到,Storage::Async的每个公开函数并非直接发起异步请求,而是先检查forced_sync状态再分派:

function open_backend(btype: Storage::Backend, options: Storage::BackendOptions, key_type: any, val_type: any): Storage::OperationResult { if ( options$forced_sync ) return Storage::Sync::__open_backend(btype, options, key_type, val_type); else return Storage::Async::__open_backend(btype, options, key_type, val_type); }

close_backend、put、get、erase四个函数则统一以Storage::is_forced_sync(backend)判断分派方向,例如:

function get(backend: opaque of Storage::BackendHandle, key: any) : Storage::OperationResult { if ( Storage::is_forced_sync(backend) ) return Storage::Sync::__get(backend, key); else return Storage::Async::__get(backend, key); }

Storage::is_forced_sync与Storage::is_open是 C++ 内建函数,声明在 src/storage/storage.bif:is_forced_sync在句柄无效时返回F,否则返回句柄所指向后端对象的IsForcedSync()结果。由此可以推断:

  • forced_sync是逐连接生效的:open_backend依据BackendOptions$forced_sync决定该连接进入同步模式;同一 Zeek 进程内可同时存在同步与异步后端句柄;
  • 异步与同步共享同一套参数/返回结构,模式切换对上层脚本透明,代价仅是阻塞行为不同。

另一个重要行为来自框架文档 doc/frameworks/storage.rst:当用-r参数离线读取 pcap 时,所有后端内部都以同步方式工作,以保证 Zeek 的定时器机制正确运转。此时异步函数仍必须写在when中,但底层会被转换为同步调用。


5. 实战示例:基于仓库测试用例的异步读写

仓库的 btest 测试 testing/btest/scripts/base/frameworks/storage/sqlite/basic.zeek 完整演示了Storage::Async的open_backend→put→get→close_backend全链路,且每个异步调用都嵌套在when中并带timeout兜底。下面是提取并整理的可运行示例:

@load base/frameworks/storage/async @load policy/frameworks/storage/backend/sqlite @load base/frameworks/telemetry redef exit_only_after_terminate = T; global b : opaque of Storage::BackendHandle; event Storage::backend_opened(tag: Storage::Backend, config: any) { print "Storage::backend_opened", tag, config; } event zeek_init() { # 配置 SQLite 后端:数据库文件与表名 local opts: Storage::BackendOptions; opts$serializer = Storage::STORAGE_SERIALIZER_JSON; opts$sqlite = [ $database_path="test.sqlite", $table_name="testing" ]; local key = "key1234"; local value = "value5678"; # 异步打开后端,注意必须放在 when 条件中 when [opts, key, value] ( local open_res = Storage::Async::open_backend( Storage::STORAGE_BACKEND_SQLITE, opts, string, string) ) { print "open result", open_res; b = open_res$value; when [key, value] ( local put_res = Storage::Async::put(b, [ $key=key, $value=value ]) ) { print "put result", put_res; when [key, value] ( local get_res = Storage::Async::get(b, key) ) { print "get result", get_res; if ( get_res$code == Storage::SUCCESS && get_res?$value ) print "get result same as inserted", value == ( get_res$value as string ); } timeout 5sec { print "get request timed out"; terminate(); } } timeout 5sec { print "put request timed out"; terminate(); } } timeout 5sec { print "open request timed out"; terminate(); } }

要点归纳:

  1. when是异步 API 的强制要求:文档与源码均明确,"must be called via awhencondition or an error will be returned"(必须通过when条件调用,否则返回错误)。when右侧的局部声明(如open_res)在条件满足后自动可用;
  2. key_type/val_type严格校验:示例中声明键、值均为string,后续put/get/erase传入其他类型会触发Storage::KEY_TYPE_MISMATCH或Storage::VAL_TYPE_MISMATCH;
  3. get返回值需按声明类型转换:OperationResult$value是any,示例中通过get_res$value as string转回原类型并比较;
  4. 每个异步调用都应配timeout:一旦后端无响应(如连接丢失),when的timeout 5sec分支会被触发,避免脚本悬挂。exit_only_after_terminate = T确保脚本直到显式terminate()才退出;
  5. close_backend同样异步:示例事件print_metrics_and_close()中再次用when包裹Storage::Async::close_backend(b),成功后再terminate()。

另一个测试 testing/btest/scripts/base/frameworks/storage/sqlite/basic-reading-pcap.zeek 展示了混合用法:用Storage::Sync::open_backend打开连接,随后对同一句柄调用Storage::Async::put/Storage::Async::get,最后用Storage::Sync::close_backend关闭——说明同步句柄与异步操作可以混用,只要后端连接本身有效,异步函数可作用在任何句柄上(其is_forced_sync决定实际执行路径)。


6. 使用异步存储框架的注意事项

综合框架文档 doc/frameworks/storage.rst 与源码,实践中有以下几点约束:

  • 后端与序列化器插件:Zeek 默认提供 Redis(需系统安装hiredis≥ 1.1.0,Redis 服务端 ≥ 6.2.0)与 SQLite 两个后端,以及 JSON 序列化器(Storage::STORAGE_SERIALIZER_JSON,默认启用)。切换后端只需更换btype标签与对应 options 记录,脚本层 API 保持一致;
  • SQLite 的 WAL 限制:SQLite 后端默认 pragma 将journal_mode设为WAL,该模式不适用于网络文件系统——数据库文件必须位于所有打开它的 Zeek 进程同一台机器上;若使用:memory:内存库,数据不会在节点间同步,每个进程持有独立数据库;
  • backend_lost的双重语义:该事件在主动close_backend成功与连接意外丢失时都会被抛出,脚本应据此实现重连逻辑;
  • forced_sync仅建议测试使用:BackendOptions$forced_sync = T会让所有操作(包括异步 API)走同步路径,main.zeek注释明确 "This should generally only be set toTduring testing"(通常仅在测试时设为T);仓库中的redis/forced-sync.zeek、redis/async-reading-pcap.zeek等测试即覆盖此类场景;
  • 同步/异步 API 二选一装载:若脚本只使用同步 API,可@load base/frameworks/storage/sync;两者同时装载亦无冲突,因为Storage::Async在forced_sync时会调用Storage::Sync::__*,反之亦然,二者互为补充。

7. 总结

Storage::Async是 Zeek storage framework 的异步操作入口,五个函数open_backend、close_backend、put、get、erase覆盖了后端生命周期管理与键值数据读写的全部场景。其核心设计包括:

  • 统一返回结构Storage::OperationResult与后端可扩展的Storage::ReturnCode枚举;
  • when条件驱动的非阻塞调用模型,配合timeout分支保证健壮性;
  • forced_sync双层分派机制:脚本层先按连接状态决定走同步还是异步内建实现,且-r读 pcap 时后端内部一律同步化;
  • 类型即契约:打开后端时声明的key_type/val_type会在所有后续操作中被严格校验。

如需继续深入,可查阅仓库中的 storage framework 框架文档、Storage 命名空间 API 文档、Storage::Sync 模块文档,以及 storage 的 C++ 接口声明 与 SQLite 后端测试用例,将异步存储能力接入自己的 Zeek 策略脚本。

  • 网络安全
  • 网络
  • IDS

【免费下载链接】zeek

Zeek is a powerful network analysis framework that is much different from the typical IDS you may know.

项目地址:https://gitcode.com/gh_mirrors/ze/zeek
点击查看免费下载
上一篇:戴森球计划蓝图库:3000+工厂设计让你的星际帝国建设效率翻倍
下一篇:aider 安装实战:用 aider-install 一键搭建隔离环境并跑通你的第一个 AI 结对编程会话

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询