应用示例§
注意
这些步骤假设已使用每个应用的语言模块安装Unit。
Go§
让我们配置以下基本应用,保存为 /www/app.go
package main
import (
"io"
"net/http"
"unit.nginx.org/go"
)
func main() {
http.HandleFunc("/",func (w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello, Go on Unit!")
})
unit.ListenAndServe(":8080", nil)
}
首先,创建一个Go 模块,go get
Unit 的软件包,并构建你的应用程序
$ go mod init example.com/app
$ go get unit.nginx.org/go@1.32.1
$ go build -o /www/app /www/app.go
将应用配置上传到 Unit 并对其进行测试
# curl -X PUT --data-binary '{
"listeners": {
"*:8080": {
"pass": "applications/go"
}
},
"applications": {
"go": {
"type": "external",
"working_directory": "/www/",
"executable": "/www/app"
}
}
}' --unix-socket /path/to/control.unit.sock http://localhost/config/
$ curl http://localhost:8080
Hello, Go on Unit!
使用 Dockerfile 此处
试用此示例,或使用更精细的应用示例
package main
import (
"crypto/sha256";
"fmt";
"io";
"io/ioutil";
"encoding/json";
"net/http";
"strings";
"unit.nginx.org/go"
)
func formatRequest(r *http.Request) string {
h := make(map[string]string)
m := make(map[string]string)
t := make(map[string]interface{})
m["message"] = "Unit reporting"
m["agent"] = "NGINX Unit 1.32.1"
body, _ := ioutil.ReadAll(r.Body)
m["body"] = fmt.Sprintf("%s", body)
m["sha256"] = fmt.Sprintf("%x", sha256.Sum256([]byte(m["body"])))
data, _ := json.Marshal(m)
for name, _ := range r.Header {
h[strings.ToUpper(name)] = r.Header.Get(name)
}
_ = json.Unmarshal(data, &t)
t["headers"] = h
js, _ := json.MarshalIndent(t, "", " ")
return fmt.Sprintf("%s", js)
}
func main() {
http.HandleFunc("/",func (w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
io.WriteString(w, formatRequest(r))
})
unit.ListenAndServe(":8080", nil)
}
Java§
让我们配置以下基本应用,保存为 /www/index.jsp
<%@ page language="java" contentType="text/plain" %>
<%= "Hello, JSP on Unit!" %>
将应用配置上传到 Unit 并对其进行测试
# curl -X PUT --data-binary '{
"listeners": {
"*:8080": {
"pass": "applications/java"
}
},
"applications": {
"java": {
"type": "java",
"webapp": "/www/"
}
}
}' --unix-socket /path/to/control.unit.sock http://localhost/config/
$ curl http://localhost:8080
Hello, JSP on Unit!
使用 Dockerfile 此处
尝试此示例,或使用更精细的应用程序示例(您需要下载并添加 json-simple 库到应用程序的类路径选项)
<%@ page language="java" contentType="application/json; charset=utf-8" %>
<%@ page import="com.github.cliftonlabs.json_simple.JsonObject" %>
<%@ page import="com.github.cliftonlabs.json_simple.Jsoner" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.math.BigInteger" %>
<%@ page import="java.nio.charset.StandardCharsets" %>
<%@ page import="java.security.MessageDigest" %>
<%@ page import="java.util.Enumeration" %>
<%
JsonObject r = new JsonObject();
r.put("message", "Unit reporting");
r.put("agent", "NGINX Unit 1.32.1");
JsonObject headers = new JsonObject();
Enumeration h = request.getHeaderNames();
while (h.hasMoreElements()) {
String name = (String)h.nextElement();
headers.put(name, request.getHeader(name));
}
r.put("headers", headers);
BufferedReader br = request.getReader();
String body = "";
String line = br.readLine();
while (line != null) {
body += line;
line = br.readLine();
}
r.put("body", body);
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] bytes = md.digest(body.getBytes(StandardCharsets.UTF_8));
BigInteger number = new BigInteger(1, bytes);
StringBuilder hex = new StringBuilder(number.toString(16));
r.put("sha256", hex.toString());
out.println(Jsoner.prettyPrint((Jsoner.serialize(r))));
%>
Node.js§
让我们配置以下基本应用程序,将其保存为/www/app.js
#!/usr/bin/env node
require("unit-http").createServer(function (req, res) {
res.writeHead(200, {"Content-Type": "text/plain"});
res.end("Hello, Node.js on Unit!")
}).listen()
使其可执行,并链接您之前安装的 Node.js 语言包
$ cd /www
$ chmod +x app.js
$ npm link unit-http
将应用程序配置上传到 Unit 并对其进行测试
# curl -X PUT --data-binary '{
"listeners": {
"*:8080": {
"pass": "applications/node"
}
},
"applications": {
"node": {
"type": "external",
"working_directory": "/www/",
"executable": "app.js"
}
}
}' --unix-socket /path/to/control.unit.sock http://localhost/config/
$ curl http://localhost:8080
Hello, Node.js on Unit!
使用 Dockerfile 此处
尝试此示例,或使用更精细的应用程序示例
#!/usr/bin/env node
const cr = require("crypto")
const bd = require("body")
require("unit-http").createServer(function (req, res) {
bd (req, res, function (err, body) {
res.writeHead(200, {"Content-Type": "application/json; charset=utf-8"})
var r = {
"agent": "NGINX Unit 1.32.1",
"message": "Unit reporting"
}
r["headers"] = req.headers
r["body"] = body
r["sha256"] = cr.createHash("sha256").update(r["body"]).digest("hex")
res.end(JSON.stringify(r, null, " ").toString("utf8"))
})
}).listen()
注意
您可以运行同一应用程序的一个版本,无需显式要求unit-http 模块。
Perl§
让我们配置以下基本应用程序,将其保存为/www/app.psgi
my $app = sub {
return [
"200",
[ "Content-Type" => "text/plain" ],
[ "Hello, Perl on Unit!" ],
];
};
将应用程序配置上传到 Unit 并对其进行测试
# curl -X PUT --data-binary '{
"listeners": {
"*:8080": {
"pass": "applications/perl"
}
},
"applications": {
"perl": {
"type": "perl",
"working_directory": "/www/",
"script": "/www/app.psgi"
}
}
}' --unix-socket /path/to/control.unit.sock http://localhost/config/
$ curl http://localhost:8080
Hello, Perl on Unit!
使用 Dockerfile 此处
尝试此示例,或使用更精细的应用程序示例
use strict;
use Digest::SHA qw(sha256_hex);
use JSON;
use Plack;
use Plack::Request;
my $app = sub {
my $env = shift;
my $req = Plack::Request->new($env);
my $res = $req->new_response(200);
$res->header("Content-Type" => "application/json; charset=utf-8");
my $r = {
"message" => "Unit reporting",
"agent" => "NGINX Unit 1.32.1",
"headers" => $req->headers->psgi_flatten(),
"body" => $req->content,
"sha256" => sha256_hex($req->content),
};
my $json = JSON->new();
$res->body($json->utf8->pretty->encode($r));
return $res->finalize();
};
PHP§
让我们配置以下基本应用程序,将其保存为/www/index.php
<?php echo "Hello, PHP on Unit!"; ?>
将应用程序配置上传到 Unit 并对其进行测试
# curl -X PUT --data-binary '{
"listeners": {
"*:8080": {
"pass": "applications/php"
}
},
"applications": {
"php": {
"type": "php",
"root": "/www/"
}
}
}' --unix-socket /path/to/control.unit.sock http://localhost/config/
$ curl http://localhost:8080
Hello, PHP on Unit!
使用 Dockerfile 此处
尝试此示例,或使用更精细的应用程序示例
<?php
header("Content-Type: application/json; charset=utf-8");
$r = array (
"message" => "Unit reporting",
"agent" => "NGINX Unit 1.32.1"
);
foreach ($_SERVER as $header => $value)
if (strpos($header, "HTTP_") === 0)
$r["headers"][$header] = $value;
$r["body"] = file_get_contents("php://input");
$r["sha256"] = hash("sha256", $r["body"]);
echo json_encode($r, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
?>
Python§
让我们配置以下基本应用程序,将其另存为 /www/wsgi.py
def application(environ, start_response):
start_response("200 OK", [("Content-Type", "text/plain")])
return (b"Hello, Python on Unit!")
将 应用程序配置 上传到 Unit 并对其进行测试
# curl -X PUT --data-binary '{
"listeners": {
"*:8080": {
"pass": "applications/python"
}
},
"applications": {
"python": {
"type": "python",
"path": "/www/",
"module": "wsgi"
}
}
}' --unix-socket /path/to/control.unit.sock http://localhost/config/
$ curl http://localhost:8080
Hello, Python on Unit!
使用 Dockerfile 此处
试用此示例,或使用更精细的应用程序示例
import hashlib, json
def application(env, start_response):
start_response("200 OK", [("Content-Type",
"application/json; charset=utf-8")])
r = {}
r["message"] = "Unit reporting"
r["agent"] = "NGINX Unit 1.32.1"
r["headers"] = {}
for header in [_ for _ in env.keys() if _.startswith("HTTP_")]:
r["headers"][header] = env[header]
bytes = env["wsgi.input"].read()
r["body"] = bytes.decode("utf-8")
r["sha256"] = hashlib.sha256(bytes).hexdigest()
return json.dumps(r, indent=4).encode("utf-8")
Ruby§
让我们配置以下基本应用程序,将其另存为 /www/config.ru
app = Proc.new do |env|
["200", {
"Content-Type" => "text/plain",
}, ["Hello, Ruby on Unit!"]]
end
run app
将 应用程序配置 上传到 Unit 并对其进行测试
# curl -X PUT --data-binary '{
"listeners": {
"*:8080": {
"pass": "applications/ruby"
}
},
"applications": {
"ruby": {
"type": "ruby",
"working_directory": "/www/",
"script": "config.ru"
}
}
}' --unix-socket /path/to/control.unit.sock http://localhost/config/
$ curl http://localhost:8080
Hello, Ruby on Unit!
使用 Dockerfile 此处
试用此示例,或使用更精细的应用程序示例
require "digest"
require "json"
app = Proc.new do |env|
body = env["rack.input"].read
r = {
"message" => "Unit reporting",
"agent" => "NGINX Unit 1.32.1",
"body" => body,
"headers" => env.select { |key, value| key.include?("HTTP_") },
"sha256" => Digest::SHA256.hexdigest(body)
}
["200", {
"Content-Type" => "application/json; charset=utf-8",
}, [JSON.pretty_generate(r)]]
end;
run app
WebAssembly (Wasm)§
我们不必处理字节码,而是构建一个支持 Unit 的 Rust 应用程序,并将其编译成 WebAssembly (Wasm) 组件。
确保已安装 Rust 工具链(cargo、rustc 等)。我们建议使用 rustup 入门。
此示例使用 rustc 版本 1.76.0 构建。
首先,将 wasm32-wasi 支持作为 rustc 的编译目标添加
$ rustup target add wasm32-wasi
接下来,安装 cargo 组件。这简化了从 Rust 代码构建 WebAssembly 组件的过程,使其成为推荐的方法。
$ cargo install cargo-component
目前,使用 WASI 0.2 wasi-http API 开始使用 WebAssembly 组件的最快方法是 Dan Gohman 的 hello-wasi-http 演示应用程序。克隆存储库并运行以下命令构建组件
$ git clone https://github.com/sunfishcode/hello-wasi-http
$ cd hello-wasi-http
$ cargo component build
构建命令的输出应类似于此
$ cargo component build
Compiling hello-wasi-http v0.0.0 (/home/unit-build/hello-wasi-http)
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
Creating component /home/unit-build/hello-wasi-http/target/wasm32-wasi/debug/hello_wasi_http.wasm
$
这将创建一个 WebAssembly 组件,你可以使用以下 Unit 配置将其部署在 Unit 上。确保将 component 路径指向你刚刚创建的 WebAssembly 组件。创建一个 config.json 文件
{
"listeners": {
"127.0.0.1:8080": {
"pass": "applications/wasm"
}
},
"applications": {
"wasm": {
"type": "wasm-wasi-component",
"component": "/home/unit-build/hello-wasi-http/target/wasm32-wasi/debug/hello_wasi_http.wasm"
}
}
}
使用 CLI 应用 Unit 配置
$ unitc /config < config.json
或将其手动发送到 Units 控制 API
$ cat config.json | curl -X PUT -d @- --unix-socket /path/to/control.unit.sock http://localhost/config/
恭喜!你已在 Unit 上创建了第一个 WebAssembly 组件!向你配置的侦听器发送 GET 请求。
$ curl http://localhost:8080
警告
Unit 1.32.0 及更高版本支持 WebAssembly 组件模型和 WASI 0.2 API。我们建议使用新实现。
我们不处理字节码,而是构建一个支持 Unit 的 Rust 应用,并将其编译成 WebAssembly。
注意
目前,WebAssembly 支持作为技术预览提供。这包括使用我们的 SDK 以 libunit-wasm 库的形式,将 Rust 和 C 代码编译成兼容 Unit 的 WebAssembly。有关详细信息,请参阅我们在 GitHub 上的 unit-wasm 存储库。
首先,安装特定于 WebAssembly 的 Rust 工具
$ rustup target add wasm32-wasi
接下来,使用库目标初始化一个新的 Rust 项目(应用由 Unit 的 WebAssembly 模块加载为动态库)。然后,添加我们的 unit-wasm 箱子以启用 libunit-wasm 库
$ cargo init --lib wasm_on_unit
$ cd wasm_on_unit/
$ cargo add unit-wasm
将以下内容追加到 Cargo.toml
[lib]
crate-type = ["cdylib"]
将我们 unit-wasm 存储库中的部分示例代码保存为 src/lib.rs
wget -O src/lib.rs https://raw.githubusercontent.com/nginx/unit-wasm/main/examples/rust/echo-request/src/lib.rs
使用 WebAssembly 作为目标构建 Rust 模块
$ cargo build --target wasm32-wasi
这会生成 target/wasm32-wasi/debug/wasm_on_unit.wasm 文件(路径可能取决于其他选项)。
将 应用配置 上传到 Unit 并对其进行测试
# curl -X PUT --data-binary '{
"listeners": {
"127.0.0.1:8080": {
"pass": "applications/wasm"
}
},
"applications": {
"wasm": {
"type": "wasm",
"module": "/path/to/wasm_on_unit/target/wasm32-wasi/debug/wasm_on_unit.wasm",
"request_handler": "uwr_request_handler",
"malloc_handler": "luw_malloc_handler",
"free_handler": "luw_free_handler",
"module_init_handler": "uwr_module_init_handler",
"module_end_handler": "uwr_module_end_handler"
}
}
}' --unix-socket /path/to/control.unit.sock http://localhost/config/
$ curl http://localhost:8080
* Welcome to WebAssembly in Rust on Unit! [libunit-wasm (0.3.0/0x00030000)] *
[Request Info]
REQUEST_PATH = /
METHOD = GET
VERSION = HTTP/1.1
QUERY =
REMOTE = 127.0.0.1
LOCAL_ADDR = 127.0.0.1
LOCAL_PORT = 8080
SERVER_NAME = localhost
[Request Headers]
Host = localhost:8080
User-Agent = curl/8.2.1
Accept = */*
此外,你可以更深入地研究基于 Unit 的 WebAssembly 应用内部结构。克隆 unit-wasm 存储库并构建 C 和 Rust 中的示例(可能需要 clang 和 lld)
$ git clone https://github.com/nginx/unit-wasm/
$ cd unit-wasm
$ make help # Explore your options first
$ make WASI_SYSROOT=/path/to/wasi-sysroot/ examples # C examples
$ make WASI_SYSROOT=/path/to/wasi-sysroot/ examples-rust # Rust examples
注意
如果上述命令失败,如下所示
wasm-ld: error: cannot open .../lib/wasi/libclang_rt.builtins-wasm32.a: No such file or directory
clang: error: linker command failed with exit code 1 (use -v to see invocation)
下载并安装库到 clang 的运行时依赖项目录
$ wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/libclang_rt.builtins-wasm32-wasi-20.0.tar.gz \
| tar zxf - # Unpacks to lib/wasi/ in the current directory
$ clang -print-runtime-dir # Double-check the run-time directory, which is OS-dependent
/path/to/runtime/dir/linux
# mkdir /path/to/runtime/dir/wasi # Note the last part of the pathname
# cp lib/wasi/libclang_rt.builtins-wasm32.a /path/to/runtime/dir/wasi/