This is an automated email from the ASF dual-hosted git repository.
hefengen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/shenyu-website.git
The following commit(s) were added to refs/heads/main by this push:
new 342b38123a [DOC] add wasm plugin docs (#1006)
342b38123a is described below
commit 342b38123a03ddc1fc9287c991708ded4ec51172
Author: dragon-zhang <[email protected]>
AuthorDate: Tue Jan 30 09:48:03 2024 +0800
[DOC] add wasm plugin docs (#1006)
* [DOC] add wasm plugin docs
* fix doc
---------
Co-authored-by: hailang <[email protected]>
---
docs/developer/custom-plugin.md | 177 ++++++++++++++++++++-
.../current/developer/custom-plugin.md | 169 ++++++++++++++++++++
2 files changed, 342 insertions(+), 4 deletions(-)
diff --git a/docs/developer/custom-plugin.md b/docs/developer/custom-plugin.md
index a863107e6a..76645c8dcd 100644
--- a/docs/developer/custom-plugin.md
+++ b/docs/developer/custom-plugin.md
@@ -87,6 +87,140 @@ Detailed instruction of interface methods:
}
```
+## Single Responsibility Plugin in Multiple Languages
+
+* The above is about writing a single responsibility plugin in Java. If you
want to write plugins in another language, at least the language you are good
at supporting `WASM`, you can find resources
[here](https://shenyu.apache.org/docs/next/design/wasm-plugin-design/) . After
you have learned about `WASM`, let's introduce the following dependency to
build the Java part of the plugin:
+
+```xml
+<dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-plugin-wasm-api</artifactId>
+ <version>${project.version}</version>
+</dependency>
+```
+
+* Add a new class `MyShenyuWasmPlugin`, inherit from
`org.apache.shenyu.plugin.wasm.api.AbstractWasmPlugin`
+
+```java
+package x.y.z;
+
+public class MyShenyuWasmPlugin extends AbstractWasmPlugin {
+
+ private static final Map<Long, String> RESULTS = new ConcurrentHashMap<>();
+
+ @Override
+ public int getOrder() {
+ // your plugin order
+ return 0;
+ }
+
+ @Override
+ public String named() {
+ return "yourPluginName";
+ }
+
+ @Override
+ protected Mono<Void> doExecute(final ServerWebExchange exchange, final
ShenyuPluginChain chain, final Long argumentId) {
+ final String result = RESULTS.remove(argumentId);
+ // Results returned by calling other languages
+ return chain.execute(exchange);
+ }
+
+ @Override
+ protected Long getArgumentId(final ServerWebExchange exchange, final
ShenyuPluginChain chain) {
+ // Need to generate unique IDs for parameters based on exchange and
chain
+ return 0L;
+ }
+
+ @Override
+ protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store) {
+ Map<String, Func> funcMap = new HashMap<>();
+ funcMap.put("get_args", WasmFunctions.wrap(store, WasmValType.I64,
WasmValType.I64, WasmValType.I32, WasmValType.I32,
+ (argId, addr, len) -> {
+ // Callbacks for obtaining parameters from Java in other
languages
+ String config = "hello from java " + argId;
+ LOG.info("java side->" + config);
+ ByteBuffer buf = super.getBuffer();
+ for (int i = 0; i < len && i < config.length(); i++) {
+ buf.put(addr.intValue() + i, (byte) config.charAt(i));
+ }
+ return Math.min(config.length(), len);
+ }));
+ funcMap.put("put_result", WasmFunctions.wrap(store, WasmValType.I64,
WasmValType.I64, WasmValType.I32, WasmValType.I32,
+ (argId, addr, len) -> {
+ // Callbacks that pass call results to Java in other languages
+ ByteBuffer buf = super.getBuffer();
+ byte[] bytes = new byte[len];
+ for (int i = 0; i < len; i++) {
+ bytes[i] = buf.get(addr.intValue() + i);
+ }
+ String result = new String(bytes, StandardCharsets.UTF_8);
+ RESULTS.put(argId, result);
+ LOG.info("java side->" + result);
+ return 0;
+ }));
+ return funcMap;
+ }
+ }
+```
+
+* Create projects in other languages, using the Rust language as an example:
+
+```shell
+cd {shenyu}/shenyu-plugin/{your_plugin_moodule}/src/main
+cargo new --lib your_plugin_name
+```
+
+* Add `execute` method in `lib.rs`:
+
+```rust
+#[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;
+}
+
+// Adding `#[no_mangle]` to prevent the Rust compiler from modifying method
names is mandatory
+#[no_mangle]
+pub unsafe extern "C" fn execute(arg_id: i64) {
+ let mut buf = [0u8; 32];
+ let buf_ptr = buf.as_mut_ptr() as i64;
+ eprintln!("rust side-> buffer base address: {}", buf_ptr);
+ // Get parameters from Java
+ let len = get_args(arg_id, buf_ptr, buf.len() as i32);
+ let java_arg = std::str::from_utf8(&buf[..len as usize]).unwrap();
+ eprintln!("rust side-> recv:{}", java_arg);
+ // Add plugin logic for the Rust section here, such as rpc calls, etc
+ // Pass the call result of rust to Java
+ let rust_result = "rust result".as_bytes();
+ let result_ptr = rust_result.as_ptr() as i64;
+ _ = put_result(arg_id, result_ptr, rust_result.len() as i32);
+}
+```
+
+* Add `[lib]` to `Cargo.toml` and change `crate-type` to `["cdylib"]`.
Ultimately, your `Cargo.toml` should look like:
+
+```toml
+[package]
+name = "your_plugin_name"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+# ......
+
+[lib]
+crate-type = ["cdylib"]
+```
+
+* Generate the wasm file:
+
+```shell
+cargo build --target wasm32-wasi --release
+```
+
+* You will see
`{shenyu}/shenyu-plugin/{your_plugin_moodule}/src/main/{your_plugin_name}/target/wasm32-wasi/release/{your_plugin_name}.wasm`,
then rename it, due to the `x.y.z.MyShenyuWasmPlugin`,the final wasm file name
should be `x.y.z.MyShenyuWasmPlugin.wasm`, finally, put the wasm file in the
`resources` folder of your plugin module.
## Matching Traffic Processing Plugin
@@ -94,10 +228,10 @@ Detailed instruction of interface methods:
```xml
<dependency>
- <groupId>org.apache.shenyu</groupId>
- <artifactId>shenyu-plugin-base</artifactId>
- <version>${project.version}</version>
- </dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-plugin-base</artifactId>
+ <version>${project.version}</version>
+</dependency>
```
* Add a new class `CustomPlugin`, inherit from
`org.apache.shenyu.plugin.base.AbstractShenyuPlugin`
@@ -203,6 +337,41 @@ public class CustomPlugin extends AbstractShenyuPlugin {
}
```
+## Matching Traffic Processing Plugin in Multiple Languages
+
+* The general logic is similar to [Single Responsibility Plugin in Multiple
Languages](#Single Responsibility Plugin in Multiple Languages) , but the
dependency in Java and the methods that need to be added in other languages are
different from `Single Responsibility Plugin in Multiple Languages`. The
following are the dependency required for the Java part of the `Multi Language
Matching Traffic Processing Plugin`:
+
+```xml
+<dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-plugin-wasm-base</artifactId>
+ <version>${project.version}</version>
+</dependency>
+```
+
+* The following are the methods that must be added (using the Rust language as
an example):
+
+```rust
+#[no_mangle]
+pub unsafe extern "C" fn doExecute(arg_id: i64) {
+ //......
+}
+```
+
+* The following are optional methods (using the Rust language as an example):
+
+```rust
+#[no_mangle]
+pub unsafe extern "C" fn before(arg_id: i64) {
+ //......
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn after(arg_id: i64) {
+ //......
+}
+```
+
## Subscribe your plugin data and do customized jobs
* Declare a new class named `PluginDataHandler` and implements
`org.apache.shenyu.plugin.base.handler.PluginDataHandler`
diff --git
a/i18n/zh/docusaurus-plugin-content-docs/current/developer/custom-plugin.md
b/i18n/zh/docusaurus-plugin-content-docs/current/developer/custom-plugin.md
index 969720dc59..89fac1c54c 100644
--- a/i18n/zh/docusaurus-plugin-content-docs/current/developer/custom-plugin.md
+++ b/i18n/zh/docusaurus-plugin-content-docs/current/developer/custom-plugin.md
@@ -92,6 +92,140 @@ public interface ShenyuPlugin {
}
```
+## 单一职责插件与多语言
+
+*
上述用java写单一职责插件,如果想用其他语言写插件,至少需要你擅长的语言支持`WASM`,你可以在[这里](https://shenyu.apache.org/zh/docs/next/design/wasm-plugin-design/)
找到一些资料。在你了解过`WASM`后,我们引入以下依赖来构建插件的java部分:
+
+```xml
+<dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-plugin-wasm-api</artifactId>
+ <version>${project.version}</version>
+</dependency>
+```
+
+* 用户新增一个类 `MyShenyuWasmPlugin`,直接继承
`org.apache.shenyu.plugin.wasm.api.AbstractWasmPlugin`
+
+```java
+package x.y.z;
+
+public class MyShenyuWasmPlugin extends AbstractWasmPlugin {
+
+ private static final Map<Long, String> RESULTS = new ConcurrentHashMap<>();
+
+ @Override
+ public int getOrder() {
+ // 你的插件顺序
+ return 0;
+ }
+
+ @Override
+ public String named() {
+ return "你的插件名称";
+ }
+
+ @Override
+ protected Mono<Void> doExecute(final ServerWebExchange exchange, final
ShenyuPluginChain chain, final Long argumentId) {
+ final String result = RESULTS.remove(argumentId);
+ // 调用其他语言返回的结果
+ return chain.execute(exchange);
+ }
+
+ @Override
+ protected Long getArgumentId(final ServerWebExchange exchange, final
ShenyuPluginChain chain) {
+ // 需要根据exchange和chain生成参数的唯一id
+ return 0L;
+ }
+
+ @Override
+ protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store) {
+ Map<String, Func> funcMap = new HashMap<>();
+ funcMap.put("get_args", WasmFunctions.wrap(store, WasmValType.I64,
WasmValType.I64, WasmValType.I32, WasmValType.I32,
+ (argId, addr, len) -> {
+ // 其他语言从java获取参数的回调
+ String config = "hello from java " + argId;
+ LOG.info("java side->" + config);
+ ByteBuffer buf = super.getBuffer();
+ for (int i = 0; i < len && i < config.length(); i++) {
+ buf.put(addr.intValue() + i, (byte) config.charAt(i));
+ }
+ return Math.min(config.length(), len);
+ }));
+ funcMap.put("put_result", WasmFunctions.wrap(store, WasmValType.I64,
WasmValType.I64, WasmValType.I32, WasmValType.I32,
+ (argId, addr, len) -> {
+ // 其他语言把调用结果传给java的回调
+ ByteBuffer buf = super.getBuffer();
+ byte[] bytes = new byte[len];
+ for (int i = 0; i < len; i++) {
+ bytes[i] = buf.get(addr.intValue() + i);
+ }
+ String result = new String(bytes, StandardCharsets.UTF_8);
+ RESULTS.put(argId, result);
+ LOG.info("java side->" + result);
+ return 0;
+ }));
+ return funcMap;
+ }
+ }
+```
+
+* 创建其他语言的项目,下面以rust语言为例:
+
+```shell
+cd {shenyu}/shenyu-plugin/{your_plugin_moodule}/src/main
+cargo new --lib your_plugin_name
+```
+
+* 在`lib.rs`中新增`execute`方法:
+
+```rust
+#[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;
+}
+
+// 加上`#[no_mangle]`以防止rust编译器修改方法名,这是必须的
+#[no_mangle]
+pub unsafe extern "C" fn execute(arg_id: i64) {
+ let mut buf = [0u8; 32];
+ let buf_ptr = buf.as_mut_ptr() as i64;
+ eprintln!("rust side-> buffer base address: {}", buf_ptr);
+ // 从java那边获取参数
+ let len = get_args(arg_id, buf_ptr, buf.len() as i32);
+ let java_arg = std::str::from_utf8(&buf[..len as usize]).unwrap();
+ eprintln!("rust side-> recv:{}", java_arg);
+ // 这里添加rust部分的插件逻辑,比如rpc调用等等
+ // 把rust的调用结果传给java
+ let rust_result = "rust result".as_bytes();
+ let result_ptr = rust_result.as_ptr() as i64;
+ _ = put_result(arg_id, result_ptr, rust_result.len() as i32);
+}
+```
+
+* 在`Cargo.toml`中新增`[lib]`并把`crate-type`改为`["cdylib"]`,最终你的`Cargo.toml`应该像这样:
+
+```toml
+[package]
+name = "your_plugin_name"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+# ......
+
+[lib]
+crate-type = ["cdylib"]
+```
+
+* 生成wasm文件:
+
+```shell
+cargo build --target wasm32-wasi --release
+```
+
+*
你将看到`{shenyu}/shenyu-plugin/{your_plugin_moodule}/src/main/{your_plugin_name}/target/wasm32-wasi/release/{your_plugin_name}.wasm`,重命名wasm文件,结合`x.y.z.MyShenyuWasmPlugin`的类路径,wasm文件名是`x.y.z.MyShenyuWasmPlugin.wasm`,最后把wasm文件放到你插件模块的`resources`文件夹下。
## 匹配流量处理插件
@@ -208,6 +342,41 @@ public class CustomPlugin extends AbstractShenyuPlugin {
}
```
+## 匹配流量处理插件与多语言
+
+* 大体逻辑与[单一职责插件与多语言](#单一职责插件与多语言)
类似,但java部分的依赖、其他语言需要新增的方法与`单一职责插件与多语言`不同。以下是`多语言匹配流量处理插件`java部分所需要的依赖:
+
+```xml
+<dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-plugin-wasm-base</artifactId>
+ <version>${project.version}</version>
+</dependency>
+```
+
+* 以下是必须新增的方法(以rust语言为例):
+
+```rust
+#[no_mangle]
+pub unsafe extern "C" fn doExecute(arg_id: i64) {
+ //......
+}
+```
+
+* 以下是可选的方法(以rust语言为例):
+
+```rust
+#[no_mangle]
+pub unsafe extern "C" fn before(arg_id: i64) {
+ //......
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn after(arg_id: i64) {
+ //......
+}
+```
+
## 订阅你的插件数据,进行自定义的处理
* 新增一个类 `PluginDataHandler`,实现
`org.apache.shenyu.plugin.base.handler.PluginDataHandler`