- 后端
- API网关
- 微服务
【免费下载链接】shenyu
Apache ShenYu is a Java native API Gateway for service proxy, protocol conversion and API governance.
本文以 ShenYu 仓库中shenyu-plugin-wasm-base模块的 Rust 测试样例rust-plugin-data-handler为主体,完整讲解该样例的 wasm 文件构建流程(安装工具链、cargo build --target wasm32-wasi、产物命名与重命名规则),并结合仓库源码剖析 Java 侧如何通过 Wasmtime 加载 wasm、通过shenyu导入模块实现 Rust 与 Java 的双向数据交换。读完本文,你可以独立完成一次完整的 “Rust 编写数据处理器 → 编译为 wasm → 以类名规则重命名 → 作为 classpath 资源被 Java 测试加载运行” 的闭环操作。
一、这个 README 在整个 wasm 插件体系中处于什么位置
ShenYu 对插件的扩展提供了 WebAssembly 支持,核心代码集中在两个模块:
- shenyu-plugin-wasm-api:提供 WasmLoader 加载器,负责定位
.wasm文件、实例化模块、把 Java 函数以导入(import)形式暴露给 wasm; - shenyu-plugin-wasm-base:提供三类
AbstractWasm*Handler抽象基类,其中 AbstractWasmPluginDataHandler 对应插件、选择器、规则的数据同步回调(handlerPlugin/removePlugin/handlerSelector/removeSelector/handlerRule/removeRule)。
本 README 所在的 rust-plugin-data-handler 目录,就是为验证AbstractWasmPluginDataHandler而编写的 Rust 侧配套工程。目录内的文件分工如下:
| 文件 | 作用 |
|---|---|
| src/lib.rs | Rust 插件逻辑,导出 6 个数据同步回调函数,并导入 Java 侧的get_args/put_result |
| Cargo.toml | 声明crate-type = ["cdylib"],使构建产物为可被 Wasmtime 加载的共享库形态 wasm |
| AbstractWasmPluginDataHandlerTest.java | Java 测试类,内部类TestWasmPluginDataHandler继承抽象基类并定义回调到 Java 的函数 |
| src/test/resources 下的 .wasm 文件 | 编译并改名后的 wasm 二进制,作为 classpath 资源供测试加载 |
二、构建前置:安装 Rust 工具链
README 给出的前两步是安装 rustup 与 Rust。补充一点实操细节:构建命令指定了--target wasm32-wasi,而该目标并不随 Rust 默认工具链安装,需要通过 rustup 显式添加:
rustup target add wasm32-wasi这是执行下文构建命令的前提条件(README 未展开此步,属于构建wasm32-wasi目标的通用要求)。
三、生成 wasm 文件:构建命令与产物路径
README 中的核心构建命令为:
cd {shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-plugin-data-handler cargo build --target wasm32-wasi --release其中{shenyu}为 ShenYu 仓库根目录的实际路径。构建完成后,README 指出产物位于:
{shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-plugin-data-handler/target/wasm32-wasi/release/rust_wasm_data_handler_plugin.wasm这里有一个值得注意的源码级事实:当前 Cargo.toml 中包名为rust-plugin-data-handler,且crate-type为cdylib。按照 Cargo 对 cdylib 的命名约定,实际产物文件名应为librust_plugin_data_handler.wasm(带lib前缀、连字符转下划线)。这说明 README 中记录的rust_wasm_data_handler_plugin.wasm对应的是较早版本包名,执行改名步骤时应以target/wasm32-wasi/release/目录下实际生成的.wasm文件为准,不要机械照抄旧文件名。
四、重命名规则:为什么 wasm 文件名必须叫...Test$TestWasmPluginDataHandler.wasm
README 第 4 步要求把产物重命名为:
org.apache.shenyu.plugin.wasm.base.handler.AbstractWasmPluginDataHandlerTest$TestWasmPluginDataHandler.wasm这个看似奇怪的命名(含$、长得像一个 Java 类名)并非随意规定,而是由加载器的资源定位逻辑决定的。查看 WasmLoader 构造函数:
this.wasmName = clazz.getName() + ".wasm"; // locate `.wasm` lib. URL resource = clazz.getClassLoader().getResource(wasmName); if (Objects.isNull(resource)) { throw new ShenyuWasmInitException("Can't find wasm file: " + wasmName); }要点有三:
- 文件名 = 类的全限定名 +
.wasm。AbstractWasmPluginDataHandler继承WasmLoader并走默认构造(this(null, null)),此时clazz即当前实例的运行时类——测试中的内部类TestWasmPluginDataHandler。Java 内部类的Class.getName()用$连接外层类名,所以得到org.apache.shenyu.plugin.wasm.base.handler.AbstractWasmPluginDataHandlerTest$TestWasmPluginDataHandler,这正是 README 要求的目标文件名; - wasm 文件必须位于 classpath 根路径下。
getClassLoader().getResource(wasmName)是按类路径资源查找的,因此改名后的.wasm要放到 src/test/resources 目录下,随测试 classpath 一起生效。仓库中已存在该文件,即为一次构建改名后的入库产物; - 找不到即快速失败,抛出
ShenyuWasmInitException。如果跳过 README 第 4 步的重命名(或放错目录),测试会在构造TestWasmPluginDataHandler时直接报 "Can't find wasm file" 异常,这也是该步骤不可省略的原因。
同一约定也适用于模块内另外三个 Rust 样例:rust-discovery-handler、rust-meta-data-handler、rust-shenyu-wasm-plugin的 README 均采用相同流程,且src/test/resources下对应存在四个.wasm资源文件,命名均与各自测试的内部类名一一对应。
五、源码纵深:Rust 与 Java 之间的双向调用协议
理解“为什么要构建这个 wasm”需要看懂两侧代码如何配合。
5.1 Rust 侧:导入 Java 函数 + 导出 6 个回调
lib.rs 开头声明了对外部模块shenyu的导入:
#[link(wasm_import_module = "shenyu")] extern "C" { fn get_args(arg_id: i64, addr: i64, len: i32) -> i32; fn put_result(arg_id: i64, addr: i64, len: i32) -> i32; }get_args用于从 Java 侧线性内存读取参数(如序列化后的 PluginData 内容),put_result用于把 Rust 处理结果写回 Java 侧内存。随后通过#[no_mangle]导出 6 个与AbstractWasmPluginDataHandler常量完全对应的回调:handlerPlugin、removePlugin、handlerSelector、removeSelector、handlerRule、removeRule(lib.rs L38-L72),每个回调内部都会调用impls(arg_id)演示一次完整的数据往返。
5.2 Java 侧:shenyu导入模块由 WasmLoader 统一命名
shenyu这个导入模块名在 Java 侧的常量是 WasmLoader 中的IMPORT_WASM_MODULE_NAME = "shenyu"。测试类通过覆写initWasmCallJavaFunc把get_args/put_result两个 Java 闭包注册进wasmCallJavaFuncMap,由Linker定义到shenyu模块下。测试中的实现(AbstractWasmPluginDataHandlerTest.java L146-L171):
get_args:把字符串"hello from java " + argId逐字节写入 wasm 线性内存(通过super.getBuffer()拿到ByteBuffer),断言收到的argId为0;put_result:按地址从线性内存读回字节,断言内容为 Rust 写回的"rust result"。
两侧断言互为镜像:Java 写"hello from java 0",Rust 断言读取成功;Rust 写"rust result",Java 断言读取成功——构成一次端到端的内存交换验证。
5.3 参数如何跨越 wasm 边界:argId间接寻址模式
Wasm 与 Java 之间无法像 JNI 那样直接传对象引用,AbstractWasmPluginDataHandler 的 callWASI 因此采用了间接寻址:
final Long argumentId = getPluginArgumentId(pluginData); PLUGIN_ARGUMENTS.put(argumentId, pluginData); WasmFunctions.consumer(super.getStore(), execute.func(), WasmValType.I64) .accept(argumentId); PLUGIN_ARGUMENTS.remove(argumentId);Java 先把PluginData按Long型 ID 存入静态ConcurrentHashMap,再向 wasm 函数只传一个 I64 参数;wasm 侧拿到 ID 后回调get_args(arg_id, addr, len),Java 闭包凭 ID 从 Map 中取回真实对象并序列化进内存。测试中getPluginArgumentId保持基类默认值0L,这正解释了测试里对"hello from java 0"的断言。
六、完整操作清单与验证方式
综合 README 与源码,一次完整的操作顺序如下:
# 1. 安装 rustup 与 Rust(略,按官方方式安装) # 2. 确保 wasm32-wasi 目标已安装 rustup target add wasm32-wasi # 3. 构建({shenyu} 替换为仓库根目录实际路径) cd {shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-plugin-data-handler cargo build --target wasm32-wasi --release # 4. 将 target/wasm32-wasi/release/ 下实际生成的 .wasm # 重命名为 org.apache.shenyu.plugin.wasm.base.handler.AbstractWasmPluginDataHandlerTest$TestWasmPluginDataHandler.wasm # 并放入 src/test/resources 目录(与仓库中已入库的同名文件位置一致)随后运行该模块的测试即可触发完整链路:
mvn test -pl shenyu-plugin/shenyu-plugin-wasm-base验证点:
- 测试通过即代表 wasm 加载、
shenyu模块导入绑定、6 个回调调用、双向内存交换全部成功; - 由于 WasmLoader 中
WasiCtxBuilder().inheritStdout().inheritStderr()继承了标准错误流,Rust 侧的eprintln!(如rust side-> recv:hello from java 0)与 Java 侧日志(java side->hello from java 0)会出现在测试输出中,可用于人工确认数据往返方向。
七、适用前提与注意事项
- 本文流程针对当前仓库(2.7.0-SNAPSHOT 版本线)下
shenyu-plugin-wasm-base的测试样例;shenyu-plugin-wasm-base的 pom.xml 依赖shenyu-plugin-wasm-api与shenyu-plugin-base,构建测试前需保证依赖可解析; - 仓库中已提交改名后的
.wasm二进制文件,若仅运行 Java 测试而无需改动 Rust 代码,可跳过构建步骤;只有修改了 lib.rs 逻辑后,才需要重新执行第二至四节并覆盖src/test/resources下的同名.wasm文件; - README 中记录的产物文件名与当前 Cargo 包名不一致,实际执行时以构建目录中真实生成的文件为准(见第三节说明);
- wasm 文件名与类名的绑定关系由
WasmLoader的clazz.getName() + ".wasm"约定强制约束,若你为自己的测试类新增内部 Handler 类,需同步保证资源文件名与其全限定名一致,否则会抛出ShenyuWasmInitException。
- 后端
- API网关
- 微服务
【免费下载链接】shenyu
Apache ShenYu is a Java native API Gateway for service proxy, protocol conversion and API governance.
相关推荐
Apache ShenYu Wasm 插件开发:构建 Rust 测试 Wasm 文件与 Java 侧加载机制解析
Apache ShenYu Wasm 插件开发:构建 Rust 测试 Wasm 文件与 Java 侧加载机制解析 本篇技术指南以 shenyu plugin w
后端API网关微服务Apache ShenYu Wasm 插件实战:编译 Rust 实现的 Discovery Handler(wasm32-wasi 构建与类路径命名规范)
Apache ShenYu Wasm 插件实战:编译 Rust 实现的 Discovery Handler(wasm32 wasi 构建与类路径命名规范) 本篇
后端API网关微服务Apache DataFusion WebAssembly 实战:用 wasm-pack 将 Rust 查询引擎编译到浏览器
Apache DataFusion WebAssembly 实战:用 wasm pack 将 Rust 查询引擎编译到浏览器 Apache DataFusion
大数据数据分析后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考