通用规则引擎——Rush(三)通过WASM挂载规则

292 阅读3分钟

前言

再厉害的大佬,也不可能掌握所有的编程语言。那如何让所有人都能用自己喜欢的语言编写规则那?

我的选择是wasm,通过提供wasm运行时,用任意语言运行规则。

WASM Runtime

和之前的lua runtime 实现是类似的。和ruch_core并不强绑定,并提供rush_core这个feature进行关联。核心实现代码地址

支持同步和异步运行。

先看性能测试

wasm_async_flow      time:   [8.7260 µs 8.8046 µs 8.8806 µs]

编写一个wasm脚本

我这里直接用rust写一个脚本。内容很简单,导入两个函数,并将输入的内容拼接在input上进行回显。

  • success 函数 : 用来设置成功时的返回值
  • error 函数: 用来返回一个错误
  • 源代码链接
extern "C" {
    fn success(ptr: *const u8, len: usize) -> u32;
    fn error(ptr: *const u8, len: usize) -> u32;
}

#[no_mangle]
pub extern "C" fn handle(ptr: *mut c_char, len: u32) -> u32 {
    unsafe {
        let slice = std::slice::from_raw_parts(ptr, len as usize);
        let cs = CStr::from_ptr(slice.as_ptr());
        let cs = CString::from(cs);
        let s = cs.to_str().unwrap();
        let s = format!(r#"{{"input":{}}}"#, s);

        success(s.as_ptr(), s.len());
    }
    1u32
}

运行命令cargo build --target wasm32-unknown-unknown --release编译一个wasm文件,名字:wasm_example_one.wasm

我这里已经编译了一个现成的包,可以直接下载使用

调用这个包

仍然按照我们之前的约定,编写一个规则,指明引擎和文件位置,然后调用上面的wasm规则。

const WASM_RULE: &'static str = "
rule WASM_RULE _ wasm
wasm_file: wasm_example/wasm_example_one.wasm
";

#[tokio::test]
async fn test_wasm_build() {
    let rt = WasmRuntimeFactory::new()
        .async_build(WASM_RULE)
        .await
        .unwrap();

    let result: HashMap<String, String> =
        rt.async_flow(Value::String("hello".into())).await.unwrap();
    assert_eq!(result.get("input").unwrap().as_str(), "hello");
}

更多wasm调用的用例

自定义解析

和lua runtime一样,生产上并不一定从文件中解析规则,往往是从远程拉取,所以提供了自定义解析脚本的抽象。只需要实现这个trait,并注册到 WasmRuntimeFactory中。

#[async_trait::async_trait]
pub trait WasmLoader: Send + Sync {
    fn load(&self, _rule_name: String, file: String) -> anyhow::Result<Vec<u8>>;
    async fn async_load(&self, rule_name: String, file: String) -> anyhow::Result<Vec<u8>> {
        self.load(rule_name, file)
    }
}

尾语

细心地朋友肯定发现了,对wasm并没有提供类似expr和lua的环境变量的支持。这个主要是因为wasm基本已经脱离了书写规则的范畴,再进行统一的环境变量管理已经不合时宜,并且对后续的wasm维护存在影响。

虽然说wasm是多语言通用的,但目前仍然局限在浏览器上,而脱离浏览器的wasi目前看起来并不成熟。像我们之前的博文中体验了go语言wasip1,明显还是个半成品,其他语言的支持看起来遥遥无期。

但是,wasi的性能是远远超过目前wasm的,允许异步 允许多线程运行,并且脱离了浏览器。按照目前的计划,rush对wasm的支持最终会升级成wasi。而wasi已经类似于docker这种,形成了容器式的运行环境,解释了前面为什么不会提供类似expr和lua侵入式的环境变量。

目前已经将rush推送了crates.io中,对应的包名称和地址

包名称地址备注
rush_corecrates.io/crates/rush…核心抽象
rush_expr_enginecrates.io/crates/rush…表达式引擎,和rush_core强绑定
rush_lua_enginecrates.io/crates/rush…lua引擎,和rush_core弱绑定
rush_wasm_enginecrates.io/crates/rush…wasm引擎,和rush_core弱绑定

代码github地址,欢迎小伙伴的参与