Hyperlane框架最全教学(1815)
二进制加解密库 (bin-encode-decode/README.md)
高性能二进制编码和解码库:支持超越 Base64 的自定义字符集
特性
- 自定义字符集:可定义自己的字符集用于编码和解码,从而实现灵活的数据表示。
- 高性能:经过速度优化,适用于需要高效加密操作的应用程序。
- 简单的 API:编码和解码过程都有直观且易于使用的接口。
- 强大的错误处理:提供清晰且具描述性的错误消息,便于调试。
- 丰富的文档:有全面的指南和示例,帮助你快速上手。
安装
要安装 bin-encode-decode,请运行以下命令:
cargo add bin-encode-decode
使用方法
编码
使用结构体
use bin_encode_decode::*;
let mut en_decode: Charset= Charset::new();
let test_str: &str = "test";
let mut charset: String = String::from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=");
en_decode.charset(&charset);
let encode: Result= en_decode.encode(test_str);
使用函数
use bin_encode_decode::*;
let test_str: &str = "test";
let mut charset: String = String::from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=");
let encode: Result= Encode::execute(&charset, test_str);
解码
使用结构体
use bin_encode_decode::*;
let mut en_decode: Charset= Charset::new();
let test_str: &str = "aab0aabLaabZaab0";
let mut charset: String = String::from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=");
en_decode.charset(&charset);
let decode: Result= en_decode.decode(test_str);
使用函数
use bin_encode_decode::*;
let charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=";
let encoded_str = "aab0aabLaabZaab0";
let decoded_str = Decode::execute(charset, encoded_str);
let charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=";
let original_str = "test";
let encoded_str = Encode::execute(charset, original_str);
中国身份证号校验库 (china-identification-card/README.md)
说明
[!tip]
一个用于根据官方规则和校验位对中国身份证号码进行校验的 Rust 库。
功能
- 校验中国身份证号码的长度和格式
- 根据官方的权重因子计算并校验校验位
- 轻量且易于集成
安装
使用此库,可以运行以下命令:
cargo add china_identification_card
示例
use china_identification_card::*;
let valid: bool = ChineseIdCard::is_valid_id_number("110101202311012176");
assert_eq!(valid, true);
let un_valid: bool = ChineseIdCard::is_invalid_id_number("110101202311012171");
assert_eq!(un_valid, true);
chunkify (chunkify/README.md)
一个简单高效的 Rust 分块处理库。
安装
使用该 crate,你可以运行以下命令:
cargo add chunkify
使用示例
use chunkify::*;
let chunk_strategy: ChunkStrategy= ChunkStrategy::new(
    0,
    "./uploads",
    "abcdefg",
    "test.txt",
    1,
    |file_id: &str, chunk_index: usize| format!("{file_id}.{chunk_index}"),
)
.unwrap();
chunk_strategy.save_chunk(b"test", 0).await.unwrap();
chunk_strategy.merge_chunks().await.unwrap();
clonelicious (clonelicious/README.md)
clonelicious 是一个 Rust 宏库,简化了克隆和闭包执行。
clone!宏自动克隆变量并立即执行闭包,使用克隆的值,这简化了 Rust 编程中的常见模式。
安装
要安装 clonelicious,运行以下命令:
cargo add clonelicious
使用方法
use clonelicious::*;
let s1: String = String::from("Hello");
let s2: String = String::from("World");
let res: String = clone!(s1, s2 => {
    assert_eq!(s1, String::from("Hello"));
    assert_eq!(s2, String::from("World"));
    format!("{} {}", s1, s2)
});
assert_eq!(res, format!("{} {}", s1, s2));
let s1: String = String::from("Hello");
let s2: String = String::from("World");
let res: String = clone!(s1, s2 => async move {
    assert_eq!(s1, String::from("Hello"));
    assert_eq!(s2, String::from("World"));
    format!("{} {}", s1, s2)
})
.await;
assert_eq!(res, format!("{} {}", s1, s2));
let s1: String = String::from("Hello");
let s2: String = String::from("World");
let res = clone!(s1, s2 => |data| {
    assert_eq!(s1, String::from("Hello"));
    assert_eq!(s2, String::from("World"));
    format!("{} {}{}", s1, s2, data)
});
assert_eq!(res("!"), format!("{} {}{}", s1, s2, "!"));
let s1: String = String::from("Hello");
let s2: String = String::from("World");
let res = clone!(s1, s2 =>  |data| async move {
    assert_eq!(s1, String::from("Hello"));
    assert_eq!(s2, String::from("World"));
    format!("{} {}{}", s1, s2, data)
});
assert_eq!(res("!").await, String::from("Hello World!"));
let s1: String = String::from("Hello");
let s2: String = String::from("World");
let res = clone!(s1, s2 => |data: &str| {
    assert_eq!(s1, String::from("Hello"));
    assert_eq!(s2, String::from("World"));
    format!("{} {}{}", s1, s2, data)
});
assert_eq!(res("!"), format!("{} {}{}", s1, s2, "!"));
let s1: String = String::from("Hello");
let s2: String = String::from("World");
let res = clone!(s1, s2 => |data: String| async move {
    assert_eq!(s1, String::from("Hello"));
    assert_eq!(s2, String::from("World"));
    format!("{} {}{}", s1, s2, data)
});
assert_eq!(res("!".to_owned()).await, format!("{} {}{}", s1, s2, "!"));
let s1: String = String::from("Hello");
let s2: String = String::from("World");
let res = clone!(s1, s2 => move |data| {
    assert_eq!(s1, String::from("Hello"));
    assert_eq!(s2, String::from("World"));
    format!("{} {}{}", s1, s2, data)
});
assert_eq!(res("!"), format!("{} {}{}", s1, s2, "!"));
let s1: String = String::from("Hello");
let s2: String = String::from("World");
let res = clone!(s1, s2 => move |data| async move {
    assert_eq!(s1, String::from("Hello"));
    assert_eq!(s2, String::from("World"));
    format!("{} {}{}", s1, s2, data)
});
assert_eq!(res("!").await, format!("{} {}{}", s1, s2, "!"));
let s1: String = String::from("Hello");
let s2: String = String::from("World");
let res = clone!(s1, s2 => move |data: &str| {
    assert_eq!(s1, String::from("Hello"));
    assert_eq!(s2, String::from("World"));
    format!("{} {}{}", s1, s2, data)
});
assert_eq!(res("!"), format!("{} {}{}", s1, s2, "!"));
let s1: String = String::from("Hello");
let s2: String = String::from("World");
let res = clone!(s1, s2 => move |data: String| async move {
    assert_eq!(s1, String::from("Hello"));
    assert_eq!(s2, String::from("World"));
    format!("{} {}{}", s1, s2, data)
});
assert_eq!(res("!".to_owned()).await, format!("{} {}{}", s1, s2, "!"));
cloud-file-storage (cloud-file-storage/README.md)
基于 Rust hyperlane 框架的云文件存储服务器,支持多种文件类型的上传。
使用现有地址(服务器不在大陆且经过多个服务器中转,接口会比较慢)
本地部署
克隆
git clone git@github.com:eastspire/cloud-file-storage.git
运行
cargo run
接口
添加资源接口
请求信息
| 请求方法 | 请求路径 | 查询参数 | 请求体 | 描述 | 
|---|---|---|---|---|
| POST | /add_file | key: file_name | 文件的二进制内容 | 上传文件, file_name为文件完整名称(包含后缀) | 
返回信息
| 字段 | 类型 | 描述 | 
|---|---|---|
| code | int | 请求处理结果,成功为 1,失败为 0 | 
| msg | string | 说明信息 | 
| data | string | 返回的 URL 地址 | 
接口返回示例
成功
{
  "code": 1,
  "msg": "ok",
  "data": "https://file.ltpp.vip/aaaVaabOaabVaabTaabLaaaVaabWaabPaabJaab0aab1aabYaabLaabFaabIaabLaabKaaaVaabMaabPaabSaabLaaaVaaaYaaaWaaaYaaa1aaaVaaaWaaaYaaaVaaaWaaa1aaaVaabJaaa0aaaWaaa2aabIaaaXaaa0aabLaaa1aaa5aabKaabIaaa0aabLaabJaaa2aabJaaa1aabHaaa1aabHaaa0aaa4aaa5aabKaaaWaaaWaaaXaabKaabMaabJaabLaabHaabHaaa3aaa4aaa2aaa0aabHaabMaaa5aaaWaaaZaabHaabMaabHaabLaaa0aaa1aabLaabHaaa3aabHaabIaaa0aaa5aaaWaaaXaaa5aabIaaaWaaa3aaa3aabH.png"
}
失败
{
  "code": 0,
  "msg": "missing file_name",
  "data": ""
}
资源加载接口
使用添加接口返回的地址即可
输出库 (color-output/README.md)
说明
[!tip]
一个基于 rust 的保证原子操作的输出库,支持函数,构造器等多种方式实现输出功能,支持文字颜色和背景颜色自定义
功能
- 支持输出格式化后时间
- 支持自定义文字颜色,背景颜色,文字粗细等配置
- 支持结构体定义,输出信息
- 支持构造器定义,输出信息
- 支持单行多任务输出
- 支持多行多任务输出
- 输出原子化
安装
cargo add color-output
代码示例
结构体输出
使用 output 函数
use color_output::*;
output(Output {
    text: "test_output_struct",
    color: ColorType::Use(Color::Default),
    bg_color: ColorType::Color256(0x000000),
    endl: true,
    ..Default::default()
});
使用 output 方法
use color_output::*;
Output {
    text: "test_output_struct_output",
    color: ColorType::Use(Color::Default),
    bg_color: ColorType::Use(Color::Blue),
    endl: true,
    ..Default::default()
}
.output();
数组结构体
use color_output::*;
OutputList(vec![
    Output {
        text: "test_output_list_struct_1",
        color: ColorType::Use(Color::Default),
        bg_color: ColorType::Color256(0x000000),
        endl: false,
        ..Default::default()
    },
    Output {
        text: "test_output_struct_output_2",
        color: ColorType::Use(Color::Default),
        bg_color: ColorType::Use(Color::Blue),
        endl: true,
        ..Default::default()
    },
])
.output();
构造器输出
使用 output 函数
use color_output::*;
output(
    OutputBuilder::new_from(Output::default())
        .text("test_output_builder")
        .color(ColorType::Color256(0xffffff))
        .bg_color(ColorType::Color256(0xffffff))
        .blod(true)
        .endl(true)
        .build(),
);
使用 output 方法
use color_output::*;
OutputBuilder::new()
    .text("test_output_builder_output")
    .bg_color(ColorType::Color256(0xffffff))
    .color(ColorType::Color256(0xffffff))
    .blod(true)
    .endl(true)
    .build()
    .output();
数组构造器
use color_output::*;
OutputListBuilder::new_from(vec![Output::default()])
    .add(
        OutputBuilder::new()
            .text("text")
            .bg_color(ColorType::Use(Color::Blue))
            .endl(false)
            .build(),
    )
    .add(Output {
        text: "test_new_from_output_list_builder_1",
        color: ColorType::Use(Color::Default),
        bg_color: ColorType::Color256(0x3f3f3f),
        endl: false,
        ..Default::default()
    })
    .add(Output {
        text: "test_new_from_output_list_builder_2",
        color: ColorType::Use(Color::Default),
        bg_color: ColorType::Use(Color::Cyan),
        endl: true,
        ..Default::default()
    })
    .run();
输出宏
结构体传入
use color_output::*;
output_macro!(Output {
    text: "test_proc_macro",
    color: ColorType::default(),
    bg_color: ColorType::Use(Color::Yellow),
    endl: true,
    ..Default::default()
});
构造器传入
use color_output::*;
output_macro!(OutputBuilder::new()
    .text("test_output_builder")
    .color(ColorType::Use(Color::Cyan))
    .blod(true)
    .endl(true)
    .build());
多个传入
use color_output::*;
output_macro!(
    Output {
        text: "test_proc_macro",
        color: ColorType::default(),
        bg_color: ColorType::Use(Color::Yellow),
        endl: true,
        ..Default::default()
    },
    OutputBuilder::new()
        .text("test_output_builder1")
        .color(ColorType::Color256(0xffffff))
        .blod(true)
        .endl(true)
        .build(),
    OutputBuilder::new()
        .text("test_output_builder2")
        .color(ColorType::Color256(0xffffff))
        .blod(true)
        .endl(true)
        .build()
);
println_success!
换行输出成功信息
use color_output::*;
println_success!("1234", "5678");
println_warning!
换行输出警告信息
use color_output::*;
println_warning!("1234", "5678");
println_error!
换行输出错误信息
use color_output::*;
println_error!("1234", "5678");
颜色使用
- ColorType::Use: 使用内置颜色
- ColorType::Color256: 十六进制
- ColorType::Rgb: rgb 颜色(r, g, b)
ColorType::Use
ColorType::Use(Color::White)
ColorType::Color256
ColorType::Color256(0xffffff)
ColorType::Rgb
ColorType::Rgb(255,255,255)
版本比较库 (compare-version/README.md)
说明
[!tip]
这是一个用于比较语义版本字符串和检查版本兼容性的 Rust 库。
特性
- 版本比较:比较两个语义版本字符串,以确定它们的顺序(大于、小于、等于)。
- 版本范围匹配:检查特定版本是否匹配指定范围,支持 ^和~语法。
- 预发布支持:正确处理预发布版本的比较逻辑。
- 错误处理:提供全面的错误类型,以优雅地处理版本解析和范围问题。
安装
要使用此库,可以运行以下命令:
cargo add COMPARE_VERSION
示例
use compare_version::*;
let result = CompareVersion::compare_version("1.2.3", "1.2.4");
assert_eq!(result, Ok(VersionComparison::Less));
let matches = CompareVersion::matches_version_range("1.2.3", "^1.2.0");
assert_eq!(matches, Ok(true));
let matches = CompareVersion::matches_version_range("1.2.3", "~1.2.4");
assert_eq!(matches, Ok(false));
文件操作库 (file-operation/README.md)
文件操作
一个 Rust 库,提供了一组常见的文件操作工具,如读取、写入和查询文件元数据(例如大小)。它旨在简化 Rust 项目中的文件处理,提供安全且高效的文件操作方法。
安装
要使用此库,可以运行以下命令:
cargo add file-operation
使用
写入文件
代码
let _ = write_to_file(FILE_PATH, "test".as_bytes());
描述
将给定的数据("test".as_bytes())写入指定路径的文件中。
- FILE_PATH- 目标文件路径。
- 返回值 - 一个 Result,表示操作成功或失败。
读取文件
代码
let res: Vec= read_from_file(FILE_PATH).unwrap_or_default();
描述
读取指定路径文件的内容。
- FILE_PATH- 目标文件路径。
- 返回值 - 一个 Vec,包含文件内容,如果读取失败则返回一个空的向量。
获取文件大小
代码
let size: Option= get_file_size(FILE_PATH);
描述
获取指定路径文件的大小。
- FILE_PATH- 目标文件路径。
- 返回值 - 一个 Option,表示文件大小(字节数),如果文件不存在则返回None。
复制目录文件
代码
let res: Result= copy_dir_files(FILE_DIR, NEW_FILE_DIR);
描述
将所有文件从 FILE_DIR 复制到 NEW_FILE_DIR。
- FILE_DIR- 源目录路径。
- NEW_FILE_DIR- 目标目录路径。
- 返回值 - 一个 Result,表示操作成功或失败。
删除文件
代码
let res: Result= delete_file(FILE_PATH);
描述
删除指定路径的文件。
- FILE_PATH- 目标文件路径。
- 返回值 - 一个 Result,表示操作成功或失败。
移动目录
代码
let res: Result= move_dir(FILE_DIR, NEW_TEST_DIR);
描述
将 FILE_DIR 目录移动到 NEW_TEST_DIR。
- FILE_DIR- 源目录路径。
- NEW_TEST_DIR- 目标目录路径。
- 返回值 - 一个 Result,表示操作成功或失败。
删除目录
代码
let res: Result= delete_dir(NEW_TEST_DIR);
描述
删除指定路径的目录。
- NEW_TEST_DIR- 目标目录路径。
- 返回值 - 一个 Result,表示操作成功或失败。
异步写入文件
代码
let _ = async_write_to_file(FILE_PATH, "test".as_bytes()).await;
描述
异步地将给定的数据("test".as_bytes())写入指定路径的文件中。
- FILE_PATH- 目标文件路径。
- 返回值 - 一个 Result,表示操作成功或失败。
异步读取文件
代码
let res: Vec= async_read_from_file(FILE_PATH).await.unwrap_or_default();
描述
异步地读取指定路径文件的内容。
- FILE_PATH- 目标文件路径。
- 返回值 - 一个 Vec,包含文件内容,如果读取失败则返回一个空的向量。
异步获取文件大小
代码
let size: Option= async_get_file_size(FILE_PATH).await;
描述
异步地获取指定路径文件的大小。
- FILE_PATH- 目标文件路径。
- 返回值 - 一个 Option,表示文件大小(字节数),如果文件不存在则返回None。
异步复制目录文件
代码
let res: Result= async_copy_dir_files(FILE_DIR, NEW_FILE_DIR).await;
描述
异步地将所有文件从 FILE_DIR 复制到 NEW_FILE_DIR。
- FILE_DIR- 源目录路径。
- NEW_FILE_DIR- 目标目录路径。
- 返回值 - 一个 Result,表示操作成功或失败。
异步删除文件
代码
let res: Result= async_delete_file(FILE_PATH).await;
描述
异步地删除指定路径的文件。
- FILE_PATH- 目标文件路径。
- 返回值 - 一个 Result,表示操作成功或失败。
异步移动目录
代码
let res: Result= async_move_dir(FILE_DIR, NEW_TEST_DIR).await;
描述
异步地将 FILE_DIR 目录移动到 NEW_TEST_DIR。
- FILE_DIR- 源目录路径。
- NEW_TEST_DIR- 目标目录路径。
- 返回值 - 一个 Result,表示操作成功或失败。
异步删除目录
代码
let res: Result= async_delete_dir(NEW_TEST_DIR).await;
描述
异步地删除指定路径的目录。
- NEW_TEST_DIR- 目标目录路径。
- 返回值 - 一个 Result,表示操作成功或失败。
future-fn (future-fn/README.md)
一个 Rust 库,提供宏来简化异步闭包的创建,并捕获外部状态。用于轻松清晰地构建异步代码。
安装
要安装 future-fn,请运行以下命令:
cargo add future-fn
使用
use future_fn::*;
use std::time::Duration;
use tokio::time::sleep;
let string: String = String::from("test");
let number: i32 = 1;
let future_fn = future_fn!(string, number, {
    let tmp_string: String = String::from("test");
    assert_eq!(string, tmp_string);
    assert_eq!(number, 1);
});
future_fn().await;
let future_fn = future_fn!(string, number, |data| {
    let tmp_string: String = String::from("test");
    sleep(Duration::from_millis(360)).await;
    assert_eq!(string, tmp_string);
    assert_eq!(data, 1);
    assert_eq!(number, 1);
});
future_fn(1).await;
let future_fn = future_fn!(string, number, |data: i32| {
    let tmp_string: String = String::from("test");
    sleep(Duration::from_millis(360)).await;
    assert_eq!(string, tmp_string);
    assert_eq!(data, 1);
    assert_eq!(number, 1);
    sleep(Duration::from_millis(360)).await;
});
future_fn(1).await;
let future_fn = future_fn!(string, number, |data: i32| {
    let tmp_string: String = String::from("test");
    sleep(Duration::from_millis(360)).await;
    assert_eq!(string, tmp_string);
    assert_eq!(data, 1);
    assert_eq!(number, 1);
});
future_fn(1).await;
GIT 工具 (gtl/README.md)
gtl是一个基于 Git 的工具,旨在简化多远程仓库的管理。它扩展了 Git 的功能,提供了一键初始化和推送到多个远程仓库的功能,特别适合需要同时维护多个远程仓库的开发者。
特性
- 多远程仓库管理:支持为一个本地仓库配置多个远程仓库。
- 一键初始化远程仓库:通过简单的命令,一次性初始化并配置多个远程仓库。
- 一键推送到多个远程仓库:可以通过一条命令将代码推送到所有已配置的远程仓库,节省时间和精力。
- Git 命令扩展:为 Git 提供了更多便捷的操作,提升工作效率。
安装
通过 cargo 安装 gtl:
cargo install gtl
使用
配置文件
路径: /home/.git_helper/config.json
{
  "D:\\code\\gtl": [
    { "name": "gitee", "url": "git@gitee.com:eastspire/gtl.git" },
    { "name": "origin", "url": "git@github.com:eastspire/gtl.git" }
  ]
}
初始化多个远程仓库
假设你已经有一个本地 Git 仓库,并希望将其与多个远程仓库关联,使用以下命令:
gtl init
一键推送到所有远程仓库
配置好多个远程仓库后,使用以下命令将代码推送到所有已配置的远程仓库:
gtl push
Git 添加 & 提交 & 推送
gtl acp
版本
gtl -v
gtl version
gtl --version
帮助
gtl help
赞赏
如果你觉得 hyperlane 对你有所帮助,欢迎捐赠。
热重启 (hot-restart/README.md)
一个热重启的 lib 项目
安装
使用以下命令添加此依赖:
cargo add hot-restart
使用示例
use hot_restart::*;
let res: ResultHotRestartError = hot_restart(&["--once", "-x", "check", "-x", "build --release"]);
println!("hot_restart {:?}", res);
HTTP 压缩解压库 (http-compress/README.md)
一个支持 Brotli、Deflate 和 Gzip 的轻量级 HTTP 响应解压库。
安装
要使用此 crate,可以运行以下命令:
cargo add http-compress
使用示例
Compress 类型
use http_compress::*;
use core::hash::BuildHasherDefault;
use std::{borrow::Cow, collections::HashMap};
let headers: HashMap> = HashMap::with_hasher(BuildHasherDefault::default());
let data: Vec= vec![];
let body: Cow> = Compress::from(&headers).decode(&data, 1_024_000);
assert_eq!(*body, data);
编码
use http_compress::*;
let _ = Compress::Gzip.encode(&[], 1_024_000);
let _ = Compress::Deflate.encode(&[], 1_024_000);
let _ = Compress::Br.encode(&[], 1_024_000);
解码
use http_compress::*;
let _ = Compress::Gzip.decode(&[], 1_024_000);
let _ = Compress::Deflate.decode(&[], 1_024_000);
let _ = Compress::Br.decode(&[], 1_024_000);
HTTP 常量库 (http-constant/README.md)
一个提供常见 HTTP 常量的全面库,包括头部名称、版本、MIME 类型和协议标识符。
安装
要使用此 crate,可以运行以下命令:
cargo add http-constant
使用示例
use http_constant::*;
HTTP 请求库 (http-request/README.md)
http-request 是一个轻量级、高效的库,用于在 Rust 应用程序中构建、发送和处理 HTTP/HTTPS 请求。它提供了简单直观的 API,允许开发者轻松与 Web 服务交互,无论使用的是 "HTTP" 还是 "HTTPS" 协议。该库支持各种 HTTP 方法、自定义头部、请求体、超时、自动处理重定向(包括检测重定向循环)以及增强的响应体解码(自动和手动),实现快速安全的通信。无论是处理安全的 "HTTPS" 连接还是标准的 "HTTP" 请求,该库都针对性能、最小资源使用和易于集成到 Rust 项目进行了优化。
特性
- 支持 HTTP/HTTPS:支持 HTTP 和 HTTPS 协议。
- WebSocket 支持:完整的 WebSocket 支持,提供同步和异步 API 用于实时通信。
- 轻量级设计:http_requestcrate 提供简单高效的 API 来构建、发送和处理 HTTP 请求,同时最小化资源消耗。
- 支持常见 HTTP 方法:支持常见的 HTTP 方法,如 GET 和 POST。
- 灵活的请求构建:通过 RequestBuilder提供丰富的配置选项来设置请求头、请求体和 URL。
- 简单的错误处理:利用 Result类型处理请求和响应中的错误,使错误处理变得简单直接。
- 自定义头部和请求体:轻松添加自定义头部和请求体。
- 响应处理:提供 HTTP 响应的简单包装器,便于访问和处理响应数据。
- 优化的内存管理:实现高效的内存管理,最小化不必要的内存分配并提高性能。
- 重定向处理:支持重定向处理,允许设置最大重定向次数,并包含重定向循环检测。
- 超时:支持超时。
- 自动和手动响应体解码:支持响应体的自动和手动解码,允许与不同内容类型(如 JSON、XML 等)无缝交互。
- 代理支持:全面的代理支持,包括 HTTP、HTTPS 和 SOCKS5 代理,支持 HTTP 请求和 WebSocket 连接的身份验证。
安装
要使用此 crate,您可以运行命令:
cargo add http-request
同步
发送 GET 请求
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .get("https://ltpp.vip/")
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .decode()
    .build_sync();
request_builder
    .send()
    .and_then(|response| {
        println!("{:?}", response.text());
        Ok(())
    })
    .unwrap_or_else(|e| println!("Error => {}", e));
发送 POST 请求
发送 JSON 请求体
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let body: JsonValue = json_value!({
    "test": 1
});
let mut request_builder = RequestBuilder::new()
    .post("http://code.ltpp.vip")
    .json(body)
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .build_sync();
request_builder
    .send()
    .and_then(|response| {
        println!("{:?}", response.decode(4096).text());
        Ok(())
    })
    .unwrap_or_else(|e| println!("Error => {}", e));
发送文本请求体
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .post("http://ide.ltpp.vip/?language=rust")
    .text("hello")
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .decode()
    .build_sync();
request_builder
    .send()
    .and_then(|response| {
        println!("{:?}", response.text());
        Ok(())
    })
    .unwrap_or_else(|e| println!("Error => {}", e));
发送二进制请求体
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .post("http://ide.ltpp.vip/?language=rust")
    .body("hello".as_bytes())
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .build_sync();
request_builder
    .send()
    .and_then(|response| {
        println!("{:?}", response.decode(4096).text());
        Ok(())
    })
    .unwrap_or_else(|e| println!("Error => {}", e));
使用 HTTP 代理发送请求
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .get("https://ltpp.vip/")
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .decode()
    .http_proxy("127.0.0.1", 7890)
    .build_sync();
request_builder
    .send()
    .and_then(|response| {
        println!("{:?}", response.text());
        Ok(())
    })
    .unwrap_or_else(|e| println!("Error => {}", e));
使用 HTTP 代理身份验证发送请求
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .get("https://ltpp.vip/")
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .decode()
    .http_proxy_auth("127.0.0.1", 7890, "username", "password")
    .build_sync();
request_builder
    .send()
    .and_then(|response| {
        println!("{:?}", response.text());
        Ok(())
    })
    .unwrap_or_else(|e| println!("Error => {}", e));
使用 SOCKS5 代理发送请求
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .get("http://ide.ltpp.vip/?language=rust")
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .decode()
    .socks5_proxy("127.0.0.1", 1080)
    .build_sync();
request_builder
    .send()
    .and_then(|response| {
        println!("{:?}", response.text());
        Ok(())
    })
    .unwrap_or_else(|e| println!("Error => {}", e));
使用 SOCKS5 代理身份验证发送请求
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .get("http://ide.ltpp.vip/?language=rust")
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .decode()
    .socks5_proxy_auth("127.0.0.1", 1080, "username", "password")
    .build_sync();
request_builder
    .send()
    .and_then(|response| {
        println!("{:?}", response.text());
        Ok(())
    })
    .unwrap_or_else(|e| println!("Error => {}", e));
WebSocket 连接
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("Authorization", "Bearer test-token");
let mut websocket_builder: WebSocket = WebSocketBuilder::new()
    .connect("ws://127.0.0.1:60006/api/ws?uuid=1")
    .headers(header)
    .timeout(10000)
    .buffer(4096)
    .protocols(&["chat", "superchat"])
    .build_sync();
websocket_builder
    .send_text("Hello WebSocket!")
    .and_then(|_| {
        println!("Sync WebSocket text message sent successfully");
        websocket_builder.send_binary(b"binary data")
    })
    .and_then(|_| {
        println!("Sync WebSocket binary message sent successfully");
        match websocket_builder.receive() {
            Ok(message) => match message {
                WebSocketMessage::Text(text) => println!("Received text: {}", text),
                WebSocketMessage::Binary(data) => println!("Received binary: {:?}", data),
                WebSocketMessage::Close => println!("Connection closed"),
                _ => println!("Received other message type"),
            },
            Err(e) => println!("Error receiving message: {}", e),
        }
        Ok(())
    })
    .and_then(|_| websocket_builder.close())
    .unwrap_or_else(|e| println!("Error => {}", e));
使用 HTTP 代理的 WebSocket
use http_request::*;
let mut websocket_builder: WebSocket = WebSocketBuilder::new()
    .connect("ws://127.0.0.1:60006/api/ws?uuid=1")
    .timeout(10000)
    .buffer(4096)
    .http_proxy("127.0.0.1", 7890)
    .build_sync();
match websocket_builder.send_text("Hello WebSocket with HTTP proxy!") {
    Ok(_) => println!("WebSocket HTTP proxy message sent successfully"),
    Err(e) => println!("WebSocket HTTP proxy error: {}", e),
}
使用 HTTP 代理身份验证的 WebSocket
use http_request::*;
let mut websocket_builder: WebSocket = WebSocketBuilder::new()
    .connect("ws://127.0.0.1:60006/api/ws?uuid=1")
    .timeout(10000)
    .buffer(4096)
    .http_proxy_auth("127.0.0.1", 7890, "username", "password")
    .build_sync();
match websocket_builder.send_text("Hello WebSocket with HTTP proxy auth!") {
    Ok(_) => println!("WebSocket HTTP proxy auth message sent successfully"),
    Err(e) => println!("WebSocket HTTP proxy auth error: {}", e),
}
使用 SOCKS5 代理的 WebSocket
use http_request::*;
let mut websocket_builder: WebSocket = WebSocketBuilder::new()
    .connect("ws://127.0.0.1:60006/api/ws?uuid=1")
    .timeout(10000)
    .buffer(4096)
    .socks5_proxy("127.0.0.1", 1080)
    .build_sync();
match websocket_builder.send_text("Hello WebSocket with SOCKS5 proxy!") {
    Ok(_) => println!("WebSocket SOCKS5 proxy message sent successfully"),
    Err(e) => println!("WebSocket SOCKS5 proxy error: {}", e),
}
使用 SOCKS5 代理身份验证的 WebSocket
use http_request::*;
let mut websocket_builder: WebSocket = WebSocketBuilder::new()
    .connect("ws://127.0.0.1:60006/api/ws?uuid=1")
    .timeout(10000)
    .buffer(4096)
    .socks5_proxy_auth("127.0.0.1", 1080, "username", "password")
    .build_sync();
match websocket_builder.send_text("Hello WebSocket with SOCKS5 proxy auth!") {
    Ok(_) => println!("WebSocket SOCKS5 proxy auth message sent successfully"),
    Err(e) => println!("WebSocket SOCKS5 proxy auth error: {}", e),
}
异步
发送 GET 请求
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .get("https://ltpp.vip/")
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .decode()
    .build_async();
match request_builder.send().await {
    Ok(response) => {
        println!("{:?}", response.text());
    }
    Err(e) => println!("Error => {}", e),
}
发送 POST 请求
发送 JSON 请求体
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let body: JsonValue = json_value!({
    "test": 1
});
let mut request_builder = RequestBuilder::new()
    .post("http://code.ltpp.vip")
    .json(body)
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .build_async();
match request_builder.send().await {
    Ok(response) => {
        println!("{:?}", response.decode(4096).text());
    }
    Err(e) => println!("Error => {}", e),
}
发送文本请求体
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .post("http://ide.ltpp.vip/?language=rust")
    .text("hello")
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .decode()
    .build_async();
match request_builder.send().await {
    Ok(response) => {
        println!("{:?}", response.text());
    }
    Err(e) => println!("Error => {}", e),
}
发送二进制请求体
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .post("http://ide.ltpp.vip/?language=rust")
    .body("hello".as_bytes())
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .build_async();
match request_builder.send().await {
    Ok(response) => {
        println!("{:?}", response.decode(4096).text());
    }
    Err(e) => println!("Error => {}", e),
}
使用 HTTP 代理发送请求
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .get("https://ltpp.vip/")
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .decode()
    .http_proxy("127.0.0.1", 7890)
    .build_async();
match request_builder.send().await {
    Ok(response) => {
        println!("{:?}", response.text());
    }
    Err(e) => println!("Error => {}", e),
}
使用 HTTP 代理身份验证发送请求
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .get("https://ltpp.vip/")
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .decode()
    .http_proxy_auth("127.0.0.1", 7890, "username", "password")
    .build_async();
match request_builder.send().await {
    Ok(response) => {
        println!("{:?}", response.text());
    }
    Err(e) => println!("Error => {}", e),
}
使用 SOCKS5 代理发送请求
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .get("http://ide.ltpp.vip/?language=rust")
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .decode()
    .socks5_proxy("127.0.0.1", 1080)
    .build_async();
match request_builder.send().await {
    Ok(response) => {
        println!("{:?}", response.text());
    }
    Err(e) => println!("Error => {}", e),
}
使用 SOCKS5 代理身份验证发送请求
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("header-key", "header-value");
let mut request_builder = RequestBuilder::new()
    .get("http://ide.ltpp.vip/?language=rust")
    .headers(header)
    .timeout(6000)
    .redirect()
    .max_redirect_times(8)
    .http1_1_only()
    .buffer(4096)
    .decode()
    .socks5_proxy_auth("127.0.0.1", 1080, "username", "password")
    .build_async();
match request_builder.send().await {
    Ok(response) => {
        println!("{:?}", response.text());
    }
    Err(e) => println!("Error => {}", e),
}
WebSocket 连接
use http_request::*;
let mut header: HashMapXxHash3_64= hash_map_xx_hash3_64();
header.insert("Authorization", "Bearer test-token");
let mut websocket_builder: WebSocket = WebSocketBuilder::new()
    .connect("ws://127.0.0.1:60006/api/ws?uuid=1")
    .headers(header)
    .timeout(10000)
    .buffer(4096)
    .protocols(&["chat", "superchat"])
    .build_async();
match websocket_builder.send_text_async("Hello WebSocket!").await {
    Ok(_) => {
        println!("Async WebSocket text message sent successfully");
        match websocket_builder.send_binary_async(b"binary data").await {
            Ok(_) => {
                println!("Async WebSocket binary message sent successfully");
                match websocket_builder.receive_async().await {
                    Ok(message) => match message {
                        WebSocketMessage::Text(text) => println!("Received text: {}", text),
                        WebSocketMessage::Binary(data) => println!("Received binary: {:?}", data),
                        WebSocketMessage::Close => println!("Connection closed"),
                        _ => println!("Received other message type"),
                    },
                    Err(e) => println!("Error receiving message: {}", e),
                }
            }
            Err(e) => println!("Error sending binary: {}", e),
        }
    }
    Err(e) => println!("Error sending text: {}", e),
}
websocket_builder
    .close_async_method()
    .await
    .unwrap_or_else(|e| println!("Error closing: {}", e));
使用 HTTP 代理的 WebSocket
use http_request::*;
let mut websocket_builder: WebSocket = WebSocketBuilder::new()
    .connect("ws://127.0.0.1:60006/api/ws?uuid=1")
    .timeout(10000)
    .buffer(4096)
    .http_proxy("127.0.0.1", 7890)
    .build_async();
match websocket_builder.send_text_async("Hello WebSocket with HTTP proxy!").await {
    Ok(_) => println!("Async WebSocket HTTP proxy message sent successfully"),
    Err(e) => println!("Async WebSocket HTTP proxy error: {}", e),
}
使用 HTTP 代理身份验证的 WebSocket
use http_request::*;
let mut websocket_builder: WebSocket = WebSocketBuilder::new()
    .connect("ws://127.0.0.1:60006/api/ws?uuid=1")
    .timeout(10000)
    .buffer(4096)
    .http_proxy_auth("127.0.0.1", 7890, "username", "password")
    .build_async();
match websocket_builder.send_text_async("Hello WebSocket with HTTP proxy auth!").await {
    Ok(_) => println!("Async WebSocket HTTP proxy auth message sent successfully"),
    Err(e) => println!("Async WebSocket HTTP proxy auth error: {}", e),
}
使用 SOCKS5 代理的 WebSocket
use http_request::*;
let mut websocket_builder: WebSocket = WebSocketBuilder::new()
    .connect("ws://127.0.0.1:60006/api/ws?uuid=1")
    .timeout(10000)
    .buffer(4096)
    .socks5_proxy("127.0.0.1", 1080)
    .build_async();
match websocket_builder.send_text_async("Hello WebSocket with SOCKS5 proxy!").await {
    Ok(_) => println!("Async WebSocket SOCKS5 proxy message sent successfully"),
    Err(e) => println!("Async WebSocket SOCKS5 proxy error: {}", e),
}
使用 SOCKS5 代理身份验证的 WebSocket
use http_request::*;
let mut websocket_builder: WebSocket = WebSocketBuilder::new()
    .connect("ws://127.0.0.1:60006/api/ws?uuid=1")
    .timeout(10000)
    .buffer(4096)
    .socks5_proxy_auth("127.0.0.1", 1080, "username", "password")
    .build_async();
match websocket_builder.send_text_async("Hello WebSocket with SOCKS5 proxy auth!").await {
    Ok(_) => println!("Async WebSocket SOCKS5 proxy auth message sent successfully"),
    Err(e) => println!("Async WebSocket SOCKS5 proxy auth error: {}", e),
}
帮助
确保系统上已安装 CMake。
HTTP 类型库 (http-type/README.md)
一个提供 HTTP 基本类型的库,包括请求体、响应头和其他核心 HTTP 抽象。
安装
要使用此 crate,可以运行以下命令:
cargo add http-type
使用示例
use http_type::*;
Hyperlane 广播库 (hyperlane-broadcast/README.md)
hyperlane-broadcast 是一个基于 Tokio 广播通道的轻量级、符合人体工学的封装库,旨在为异步 Rust 应用提供简洁易用的发布-订阅消息机制。它在保留 Tokio 原始特性的同时,极大简化了广播使用流程,降低了使用门槛。
安装方式
你可以使用如下命令添加依赖:
cargo add hyperlane-broadcast
使用示例
use hyperlane_broadcast::*;
let broadcast: Broadcast= Broadcast::new(10);
let mut rec1: BroadcastReceiver= broadcast.subscribe();
let mut rec2: BroadcastReceiver= broadcast.subscribe();
broadcast.send(20).unwrap();
assert_eq!(rec1.recv().await, Ok(20));
assert_eq!(rec2.recv().await, Ok(20));
let broadcast_map: BroadcastMap= BroadcastMap::new();
broadcast_map.insert("a", 10);
let mut rec1: BroadcastMapReceiver= broadcast_map.subscribe("a").unwrap();
let mut rec2: BroadcastMapReceiver= broadcast_map.subscribe("a").unwrap();
let mut rec3: BroadcastMapReceiver= broadcast_map.subscribe_unwrap_or_insert("b");
broadcast_map.send("a", 20).unwrap();
broadcast_map.send("b", 10).unwrap();
assert_eq!(rec1.recv().await, Ok(20));
assert_eq!(rec2.recv().await, Ok(20));
assert_eq!(rec3.recv().await, Ok(10));
hyperlane 日志库 (hyperlane-log/README.md)
一个支持异步和同步日志的 Rust 日志库。它提供了多种日志级别,如错误、信息和调试。用户可以定义自定义日志处理方法并配置日志文件路径。该库支持日志轮换,当当前文件达到指定的大小限制时,会自动创建一个新的日志文件。它允许灵活的日志记录配置,使其既适用于高性能异步应用程序,也适用于传统的同步日志记录场景。异步模式使用 Tokio 的异步通道进行高效的日志缓冲,而同步模式则将日志直接写入文件系统。
安装
要使用此库,您可以运行以下命令:
cargo add hyperlane-log
日志存储位置说明
会在用户指定的目录下生成三个目录,分别对应错误日志目录,信息日志目录,调试日志目录,这三个目录下还有一级目录使用日期命名,此目录下的日志文件命名是时间.下标.log
使用同步
use hyperlane_log::*;
let log: Log = Log::new("./logs", 1_024_000);
log.error("error data!", |error| {
    let write_data: String = format!("User error func => {:?}\n", error);
    write_data
});
log.error(String::from("error data!"), |error| {
    let write_data: String = format!("User error func => {:?}\n", error);
    write_data
});
log.info("info data!", |info| {
    let write_data: String = format!("User info func => {:?}\n", info);
    write_data
});
log.info(String::from("info data!"), |info| {
    let write_data: String = format!("User info func => {:?}\n", info);
    write_data
});
log.debug("debug data!", |debug| {
    let write_data: String = format!("User debug func => {:#?}\n", debug);
    write_data
});
log.debug(String::from("debug data!"), |debug| {
    let write_data: String = format!("User debug func => {:#?}\n", debug);
    write_data
});
使用异步
use hyperlane_log::*;
let log: Log = Log::new("./logs", 1_024_000);
log.async_error("async error data!", |error| {
    let write_data: String = format!("User error func => {:?}\n", error);
    write_data
}).await;
log.async_error(String::from("async error data!"), |error| {
    let write_data: String = format!("User error func => {:?}\n", error);
    write_data
}).await;
log.async_info("async info data!", |info| {
    let write_data: String = format!("User info func => {:?}\n", info);
    write_data
}).await;
log.async_info(String::from("async info data!"), |info| {
    let write_data: String = format!("User info func => {:?}\n", info);
    write_data
}).await;
log.async_debug("async debug data!", |debug| {
    let write_data: String = format!("User debug func => {:#?}\n", debug);
    write_data
}).await;
log.async_debug(String::from("async debug data!"), |debug| {
    let write_data: String = format!("User debug func => {:#?}\n", debug);
    write_data
}).await;
禁用日志
let log: Log = Log::new("./logs", DISABLE_LOG_FILE_SIZE);
hyperlane-macros (hyperlane-macros/README.md)
安装
要使用这个 crate,你可以运行以下命令:
cargo add hyperlane-macros
可用宏
服务器实例宏
- #[hyperlane(variable_name)]- 在函数开始时使用指定的变量名创建一个新的服务器实例
HTTP 方法宏
- #[methods(method1, method2, ...)]- 接受多个 HTTP 方法
- #[get]- GET 方法处理器
- #[post]- POST 方法处理器
- #[put]- PUT 方法处理器
- #[delete]- DELETE 方法处理器
- #[patch]- PATCH 方法处理器
- #[head]- HEAD 方法处理器
- #[options]- OPTIONS 方法处理器
- #[connect]- CONNECT 方法处理器
- #[trace]- TRACE 方法处理器
协议检查宏
- #[ws]- WebSocket 检查,确保函数仅对 WebSocket 升级请求执行
- #[http]- HTTP 检查,确保函数仅对标准 HTTP 请求执行
- #[h2c]- HTTP/2 明文检查,确保函数仅对 HTTP/2 明文请求执行
- #[http0_9]- HTTP/0.9 检查,确保函数仅对 HTTP/0.9 协议请求执行
- #[http1_0]- HTTP/1.0 检查,确保函数仅对 HTTP/1.0 协议请求执行
- #[http1_1]- HTTP/1.1 检查,确保函数仅对 HTTP/1.1 协议请求执行
- #[http1_1_or_higher]- HTTP/1.1 或更高版本检查,确保函数仅对 HTTP/1.1 或更新协议版本执行
- #[http2]- HTTP/2 检查,确保函数仅对 HTTP/2 协议请求执行
- #[http3]- HTTP/3 检查,确保函数仅对 HTTP/3 协议请求执行
- #[tls]- TLS 检查,确保函数仅对 TLS 安全连接执行
响应设置宏
- #[response_status_code(code)]- 设置响应状态码(支持字面量和全局常量)
- #[response_reason_phrase("phrase")]- 设置响应原因短语(支持字面量和全局常量)
- #[response_header("key", "value")]- 设置响应头(支持字面量和全局常量)
- #[response_body("data")]- 设置响应体(支持字面量和全局常量)
发送操作宏
- #[send]- 函数执行后发送完整响应(头部和正文)
- #[send_body]- 函数执行后仅发送响应体
- #[send_once]- 函数执行后精确发送一次完整响应
- #[send_once_body]- 函数执行后精确发送一次响应体
刷新宏
- #[flush]- 函数执行后刷新响应流以确保立即传输数据
中止宏
- #[aborted]- 处理中止的请求,为过早终止的连接提供清理逻辑
关闭操作宏
- #[closed]- 处理已关闭的流,为已完成的连接提供清理逻辑
过滤器宏
- #[filter_unknown_method]- 过滤未知 HTTP 方法,处理具有非标准方法的请求
- #[filter_unknown_upgrade]- 过滤未知升级请求,处理具有非标准升级协议的请求
- #[filter_unknown_version]- 过滤未知 HTTP 版本,处理具有非标准 HTTP 协议版本的请求
- #[filter_unknown]- 未知方法、升级和版本的组合过滤器
请求体宏
- #[request_body(variable_name)]- 将原始请求体提取到指定变量中,变量类型为 RequestBody
- #[request_body_json(variable_name: type)]- 将请求体解析为 JSON 格式并存储到指定变量和类型中
属性宏
- #[attribute(键 => 变量名: 类型)]- 通过键提取特定属性到类型化变量
属性集宏
- #[attributes(变量名)]- 获取所有属性作为 HashMap 以进行全面的属性访问
路由参数宏
- #[route_param(键 => 变量名)]- 通过键将特定路由参数提取到变量中
路由参数集宏
- #[route_params(变量名)]- 获取所有路由参数作为集合
请求查询宏
- #[request_query(键 => 变量名)]- 从 URL 查询字符串中提取特定查询参数
请求查询集宏
- #[request_querys(变量名)]- 获取所有查询参数作为集合
请求头宏
- #[request_header(键 => 变量名)]- 从请求中提取特定 HTTP 头
请求头集宏
- #[request_headers(变量名)]- 获取所有 HTTP 头作为集合
钩子宏
- #[pre_hook(函数名)]- 在主处理函数之前执行指定函数
- #[post_hook(函数名)]- 在主处理函数之后执行指定函数
响应头宏
- #[response_header(键 => 值)]- 使用给定的键和值设置特定的 HTTP 响应头
响应体宏
- #[response_body(值)]- 使用给定的值设置 HTTP 响应体
最佳实践警告
- 请求相关的宏大多是查询函数,而响应相关的宏大多是赋值函数。
- 当使用 pre_hook或post_hook宏时,不建议将它们与其他宏(如#[get]、#[post]、#[http]等)在同一个函数上组合使用。这些宏应该放在钩子函数本身中。如果您不清楚宏是如何展开的,组合使用可能会导致有问题的代码行为。
使用示例
use hyperlane::*;
use hyperlane_macros::*;
use serde::{Deserialize, Serialize};
const TEST_ATTRIBUTE_KEY: &str = "test_attribute_key";
const CUSTOM_STATUS_CODE: i32 = 200;
const CUSTOM_REASON: &str = "Accepted";
const CUSTOM_HEADER_NAME: &str = "X-Custom-Header";
const CUSTOM_HEADER_VALUE: &str = "custom-value";
const RESPONSE_DATA: &str = "{\"status\": \"success\"}";
#[derive(Debug, Serialize, Deserialize, Clone)]
struct TestData {
    name: String,
    age: u32,
}
#[get]
#[http]
async fn ctx_pre_hook(ctx: Context) {}
#[flush]
#[send]
#[response_status_code(200)]
async fn ctx_post_hook(ctx: Context) {}
#[send]
#[response_status_code(201)]
#[response_reason_phrase("Created")]
#[response_header("Content-Type" => "application/json")]
#[response_body("{\"message\": \"Resource created\"}")]
async fn test_new_macros_literals(ctx: Context) {}
#[send]
#[response_status_code(CUSTOM_STATUS_CODE)]
#[response_reason_phrase(CUSTOM_REASON)]
#[response_header(CUSTOM_HEADER_NAME => CUSTOM_HEADER_VALUE)]
#[response_body(RESPONSE_DATA)]
async fn response(ctx: Context) {}
#[connect]
async fn connect(ctx: Context) {
    let _ = ctx.set_response_body("connect").await.send().await;
}
#[delete]
async fn delete(ctx: Context) {
    let _ = ctx.set_response_body("delete").await.send().await;
}
#[head]
async fn head(ctx: Context) {
    let _ = ctx.set_response_body("head").await.send().await;
}
#[options]
async fn options(ctx: Context) {
    let _ = ctx.set_response_body("options").await.send().await;
}
#[patch]
async fn patch(ctx: Context) {
    let _ = ctx.set_response_body("patch").await.send().await;
}
#[put]
async fn put(ctx: Context) {
    let _ = ctx.set_response_body("put").await.send().await;
}
#[trace]
async fn trace(ctx: Context) {
    let _ = ctx.set_response_body("trace").await.send().await;
}
#[send]
#[h2c]
async fn h2c(ctx: Context) {
    let _ = ctx.set_response_body("h2c").await;
}
#[send]
#[http]
async fn http_only(ctx: Context) {
    let _ = ctx.set_response_body("http").await;
}
#[send]
#[http0_9]
async fn http0_9(ctx: Context) {
    let _ = ctx.set_response_body("http0.9").await;
}
#[send]
#[http1_0]
async fn http1_0(ctx: Context) {
    let _ = ctx.set_response_body("http1.0").await;
}
#[send]
#[http1_1]
async fn http1_1(ctx: Context) {
    let _ = ctx.set_response_body("http1.1").await;
}
#[send]
#[http2]
async fn http2(ctx: Context) {
    let _ = ctx.set_response_body("http2").await;
}
#[send]
#[http3]
async fn http3(ctx: Context) {
    let _ = ctx.set_response_body("http3").await;
}
#[send]
#[tls]
async fn tls(ctx: Context) {
    let _ = ctx.set_response_body("tls").await;
}
#[send]
#[http1_1_or_higher]
async fn http1_1_or_higher(ctx: Context) {
    let _ = ctx.set_response_body("http1.1+").await;
}
#[send]
#[filter_unknown_method]
async fn unknown_method(ctx: Context) {
    let _ = ctx.set_response_body("unknown method").await;
}
#[send]
#[filter_unknown_upgrade]
async fn unknown_upgrade(ctx: Context) {
    let _ = ctx.set_response_body("unknown upgrade").await;
}
#[send]
#[filter_unknown_version]
async fn unknown_version(ctx: Context) {
    let _ = ctx.set_response_body("unknown version").await;
}
#[send]
#[filter_unknown]
async fn unknown_all(ctx: Context) {
    let _ = ctx.set_response_body("unknown all").await;
}
#[send_body]
#[ws]
#[get]
async fn get(ctx: Context) {
    let _ = ctx.set_response_body("get").await;
}
#[send_once]
#[post]
async fn post(ctx: Context) {
    let _ = ctx.set_response_body("post").await;
}
#[send_once_body]
#[ws]
async fn websocket(ctx: Context) {
    let _ = ctx.set_response_body("websocket").await;
}
#[send]
#[pre_hook(ctx_pre_hook)]
#[post_hook(ctx_post_hook)]
async fn ctx_hook(ctx: Context) {
    let _ = ctx.set_response_body("Testing hook macro").await;
}
#[closed]
#[send]
#[response_reason_phrase("OK")]
#[response_status_code(200)]
#[methods(get, post)]
#[http]
async fn get_post(ctx: Context) {
    let _ = ctx.set_response_body("get_post").await;
}
#[send]
#[attributes(request_attributes)]
async fn attributes(ctx: Context) {
    let response: String = format!("{:?}", request_attributes);
    let _ = ctx.set_response_body(response).await;
}
#[send]
#[route_params(request_route_params)]
async fn route_params(ctx: Context) {
    let response: String = format!("{:?}", request_route_params);
    let _ = ctx.set_response_body(response).await;
}
#[send]
#[request_querys(request_querys)]
async fn request_querys(ctx: Context) {
    let response: String = format!("{:?}", request_querys);
    let _ = ctx.set_response_body(response).await;
}
#[send]
#[request_headers(request_headers)]
async fn request_headers(ctx: Context) {
    let response: String = format!("{:?}", request_headers);
    let _ = ctx.set_response_body(response).await;
}
#[send]
#[route_param("test" => request_route_param)]
async fn route_param(ctx: Context) {
    if let Some(data) = request_route_param {
        let _ = ctx.set_response_body(data).await;
    }
}
#[send]
#[request_query("test" => request_query_option)]
async fn request_query(ctx: Context) {
    if let Some(data) = request_query_option {
        let _ = ctx.set_response_body(data).await;
    }
}
#[send]
#[request_header(HOST => request_header_option)]
async fn request_header(ctx: Context) {
    if let Some(data) = request_header_option {
        let _ = ctx.set_response_body(data).await;
    }
}
#[send]
#[request_body(raw_body)]
async fn request_body(ctx: Context) {
    let response: String = format!("Raw body: {:?}", raw_body);
    let _ = ctx.set_response_body(response).await;
}
#[send]
#[attribute(TEST_ATTRIBUTE_KEY => request_attribute_option: TestData)]
async fn attribute(ctx: Context) {
    if let Some(data) = request_attribute_option {
        let response: String = format!("name={}, age={}", data.name, data.age);
        let _ = ctx.set_response_body(response).await;
    }
}
#[send]
#[request_body_json(request_data_result: TestData)]
async fn request_body_json(ctx: Context) {
    if let Ok(data) = request_data_result {
        let response: String = format!("name={}, age={}", data.name, data.age);
        let _ = ctx.set_response_body(response).await;
    }
}
#[tokio::main]
#[hyperlane(server)]
async fn main() {
    server.route("/get", get).await;
    server.route("/post", post).await;
    server.route("/connect", connect).await;
    server.route("/delete", delete).await;
    server.route("/head", head).await;
    server.route("/options", options).await;
    server.route("/patch", patch).await;
    server.route("/put", put).await;
    server.route("/trace", trace).await;
    server.route("/h2c", h2c).await;
    server.route("/http", http_only).await;
    server.route("/http0_9", http0_9).await;
    server.route("/http1_0", http1_0).await;
    server.route("/http1_1", http1_1).await;
    server.route("/http2", http2).await;
    server.route("/http3", http3).await;
    server.route("/tls", tls).await;
    server.route("/http1_1_or_higher", http1_1_or_higher).await;
    server.route("/unknown_method", unknown_method).await;
    server.route("/unknown_upgrade", unknown_upgrade).await;
    server.route("/unknown_version", unknown_version).await;
    server.route("/unknown_all", unknown_all).await;
    server.route("/websocket", websocket).await;
    server.route("/ctx_hook", ctx_hook).await;
    server.route("/get_post", get_post).await;
    server.route("/attributes", attributes).await;
    server.route("/route_params/:test", route_params).await;
    server.route("/request_querys", request_querys).await;
    server.route("/request_headers", request_headers).await;
    server.route("/route_param/:test", route_param).await;
    server.route("/request_query", request_query).await;
    server.route("/request_header", request_header).await;
    server.route("/request_body", request_body).await;
    server.route("/attribute", attribute).await;
    server.route("/request_body_json", request_body_json).await;
    server
        .route("/test_new_macros_literals", test_new_macros_literals)
        .await;
    server.route("/response", response).await;
    let _ = server.run().await;
}
HyperlaneWebSocket 插件 (hyperlane-plugin-websocket/README.md)
hyperlane 框架的 WebSocket 插件
安装
使用以下命令添加此依赖:
cargo add hyperlane-plugin-websocket
使用示例
use hyperlane::*;
use hyperlane_plugin_websocket::*;
static BROADCAST_MAP: OnceLock= OnceLock::new();
fn get_broadcast_map() -> &'static WebSocket {
    BROADCAST_MAP.get_or_init(|| WebSocket::new())
}
async fn on_ws_connected(ctx: Context) {
    let group_name: String = ctx.get_route_param("group_name").await.unwrap();
    let broadcast_type: BroadcastType= BroadcastType::PointToGroup(&group_name);
    let receiver_count: ReceiverCount =
        get_broadcast_map().receiver_count_after_increment(broadcast_type);
    let data: String = format!("receiver_count => {:?}", receiver_count).into();
    get_broadcast_map()
        .send(broadcast_type, data)
        .unwrap_or_else(|err| {
            println!("[on_ws_connected]send error => {:?}", err.to_string());
            None
        });
    println!("[on_ws_connected]receiver_count => {:?}", receiver_count);
    let _ = std::io::Write::flush(&mut std::io::stderr());
}
async fn group_chat(ws_ctx: Context) {
    let group_name: String = ws_ctx.get_route_param("group_name").await.unwrap();
    let key: BroadcastType= BroadcastType::PointToGroup(&group_name);
    let mut receiver_count: ReceiverCount = get_broadcast_map().receiver_count(key);
    let mut body: RequestBody = ws_ctx.get_request_body().await;
    if body.is_empty() {
        receiver_count = get_broadcast_map().receiver_count_after_decrement(key);
        body = format!("receiver_count => {:?}", receiver_count).into();
    }
    ws_ctx.set_response_body(body).await;
    println!("[group_chat]receiver_count => {:?}", receiver_count);
    let _ = std::io::Write::flush(&mut std::io::stderr());
}
async fn group_closed(ctx: Context) {
    let group_name: String = ctx.get_route_param("group_name").await.unwrap();
    let key: BroadcastType= BroadcastType::PointToGroup(&group_name);
    let receiver_count: ReceiverCount = get_broadcast_map().receiver_count_after_decrement(key);
    let body: String = format!("receiver_count => {:?}", receiver_count);
    ctx.set_response_body(body).await;
    println!("[group_closed]receiver_count => {:?}", receiver_count);
    let _ = std::io::Write::flush(&mut std::io::stderr());
}
async fn private_chat(ctx: Context) {
    let my_name: String = ctx.get_route_param("my_name").await.unwrap();
    let your_name: String = ctx.get_route_param("your_name").await.unwrap();
    let key: BroadcastType= BroadcastType::PointToPoint(&my_name, &your_name);
    let mut receiver_count: ReceiverCount = get_broadcast_map().receiver_count(key);
    let mut body: RequestBody = ctx.get_request_body().await;
    if body.is_empty() {
        receiver_count = get_broadcast_map().receiver_count_after_decrement(key);
        body = format!("receiver_count => {:?}", receiver_count).into();
    }
    ctx.set_response_body(body).await;
    println!("[private_chat]receiver_count => {:?}", receiver_count);
    let _ = std::io::Write::flush(&mut std::io::stderr());
}
async fn private_closed(ctx: Context) {
    let my_name: String = ctx.get_route_param("my_name").await.unwrap();
    let your_name: String = ctx.get_route_param("your_name").await.unwrap();
    let key: BroadcastType= BroadcastType::PointToPoint(&my_name, &your_name);
    let receiver_count: ReceiverCount = get_broadcast_map().receiver_count_after_decrement(key);
    let body: String = format!("receiver_count => {:?}", receiver_count);
    ctx.set_response_body(body).await;
    println!("[private_closed]receiver_count => {:?}", receiver_count);
    let _ = std::io::Write::flush(&mut std::io::stderr());
}
async fn sended(ctx: Context) {
    let msg: String = ctx.get_response_body_string().await;
    println!("[on_sended]msg => {}", msg);
    let _ = std::io::Write::flush(&mut std::io::stderr());
}
async fn private_chat_route(ctx: Context) {
    let my_name: String = ctx.get_route_param("my_name").await.unwrap();
    let your_name: String = ctx.get_route_param("your_name").await.unwrap();
    let key: BroadcastType= BroadcastType::PointToPoint(&my_name, &your_name);
    get_broadcast_map()
        .run(&ctx, 1024, key, private_chat, sended, private_closed)
        .await;
}
async fn group_chat_route(ctx: Context) {
    let your_name: String = ctx.get_route_param("group_name").await.unwrap();
    let key: BroadcastType= BroadcastType::PointToGroup(&your_name);
    get_broadcast_map()
        .run(&ctx, 1024, key, group_chat, sended, group_closed)
        .await;
}
#[tokio::main]
async fn main() {
    let server: Server = Server::new();
    server.host("0.0.0.0").await;
    server.port(60000).await;
    server.enable_nodelay().await;
    server.disable_linger().await;
    server.http_buffer_size(4096).await;
    server.ws_buffer_size(4096).await;
    server.disable_ws_handler("/{group_name}").await;
    server.route("/{group_name}", group_chat_route).await;
    server.disable_ws_handler("/{my_name}/{your_name}").await;
    server
        .route("/{my_name}/{your_name}", private_chat_route)
        .await;
    server.on_ws_connected(on_ws_connected).await;
    server.run().await.unwrap();
}
hyperlane 时间库 (hyperlane-time/README.md)
一个根据系统的本地设置获取当前时间的库。
安装
要使用这个库,你可以运行以下命令:
cargo add hyperlane-time
使用
use hyperlane_time::*;
println!("Current Time: {}", time());
println!("Current Date: {}", date());
println!("GMT Date: {}", gmt());
println!("Timestamp (s): {}", timestamp());
println!("Timestamp (ms): {}", timestamp_millis());
println!("Timestamp (μs): {}", timestamp_micros());
println!("Current Year: {}", year());
println!("Current Month: {}", month());
println!("Current Day: {}", day());
println!("Current Hour: {}", hour());
println!("Current Minute: {}", minute());
println!("Current Second: {}", second());
println!("Current Millis: {}", millis());
println!("Current Micros: {}", micros());
println!("Is Leap Year (1949): {}", is_leap_year(1949));
println!("Calculate Current Time: {:?}", calculate_time());
println!("Compute Date (10000 days): {:?}", compute_date(10000));
println!("Current Time with Millis: {}", time_millis());
println!("Current Time with Micros: {}", time_micros());
Hyperlane 工具库 (hyperlane-utils/README.md)
一个为 hyperlane 提供实用工具的 Rust 库。
安装
您可以使用以下命令安装该 crate:
cargo add hyperlane-utils
使用方式
use hyperlane_utils::*;
错误处理 (hyperlane/config/error_handler.md)
[!tip]
hyperlane框架内部会对panic进行捕获,用户可通过钩子进行设置(不设置,框架会默认会输出错误信息)。
server.error_handler(|err: String| {
    // do something
});
绑定 Host (hyperlane/config/host.md)
[!tip]
hyperlane框架绑定host方式如下:
// 省略 server 创建
server.host("0.0.0.0").await;
HTTP 缓冲区 (hyperlane/config/http-buffer-size.md)
设置 HTTP 缓冲区大小
[!tip]
hyperlane框架设置HTTP缓冲区大小方式如下(不设置或者设置为0则默认是4096字节):
server.http_buffer_size(4096).await;
内置 HTTP 处理 (hyperlane/config/http-handler.md)
[!tip]
hyperlane框架支持配置http内部处理方式,默认启用,
值得一提的是此配置支持动态路由设置。
启用框架内部 http 处理
静态路由
server.enable_http_handler("/路由").await;
朴素动态路由
server.enable_http_handler("/路由/{id}").await;
正则表达式动态路由
server.enable_http_handler("/路由/{number:\\d+}").await;
禁用 http 内部处理
静态路由
server.disable_http_handler("/路由").await;
朴素动态路由
server.disable_http_handler("/路由/:id").await;
正则表达式动态路由
server.disable_http_handler("/路由/:number:\\d+").await;
Linger (hyperlane/config/linger.md)
[!tip]
hyperlane框架支持配置linger,该选项基于Tokio的TcpStream::set_linger,用于控制SO_LINGER选项,以决定连接关闭时未发送数据的处理方式,从而影响连接终止时的行为。
启用 linger
// 省略 server 创建
server.enable_linger(Duration::from_millis(10)).await;
// 省略 server 创建
server.set_linger(Some(Duration::from_millis(10))).await;
禁用 linger
// 省略 server 创建
server.disable_linger().await;
// 省略 server 创建
server.set_linger(None).await;
中间件 (hyperlane/config/middleware.md)
[!tip]
hyperlane框架支持请求中间件和响应中间件,中间件参数类型参考 controller-data 文档。
请求中间件
注册请求中间件
// 省略 server 创建
server.request_middleware(|ctx: Context| async move {
    // code
}).await;
注册多个请求中间件
// 省略 server 创建
server.request_middleware(|ctx: Context| async move {
    // 1
}).await;
server.request_middleware(|ctx: Context| async move {
    // 2
}).await;
server.request_middleware(|ctx: Context| async move {
    // 3
}).await;
server.request_middleware(|ctx: Context| async move {
    // 4
}).await;
设置响应中间件
注册响应中间件
// 省略 server 创建
server.response_middleware(|ctx: Context| async move {
    // code
}).await;
注册多个响应中间件
// 省略 server 创建
server.response_middleware(|ctx: Context| async move {
    // 1
}).await;
server.response_middleware(|ctx: Context| async move {
    // 2
}).await;
server.response_middleware(|ctx: Context| async move {
    // 3
}).await;
server.response_middleware(|ctx: Context| async move {
    // 4
}).await;
Nodelay (hyperlane/config/nodelay.md)
[!tip]
hyperlane框架支持配置nodelay,该选项基于Tokio的TcpStream::set_nodelay,用于控制TCP_NODELAY选项,以减少Nagle算法的影响,提高低延迟场景下的数据传输效率。
启用 nodelay
// 省略 server 创建
server.enable_nodelay().await;
// 省略 server 创建
server.set_nodelay(true).await;
禁用 nodelay
// 省略 server 创建
server.disable_nodelay().await;
// 省略 server 创建
server.set_nodelay(false).await;
Websocket 连接回调 (hyperlane/config/on_ws_connected.md)
[!tip]
hyperlane框架支持配置websocket连接回调,此方法会在websocket握手成功后调用。
配置单个 websocket 连接回调
server.on_ws_connected(|ctx: Context| async move {
    // 处理
}).await;
配置多个 websocket 连接回调
server.on_ws_connected(|ctx: Context| async move {
    // 1
}).await;
server.on_ws_connected(|ctx: Context| async move {
    // 2
}).await;
server.on_ws_connected(|ctx: Context| async move {
    // 3
}).await;
server.on_ws_connected(|ctx: Context| async move {
    // 4
}).await;
绑定端口 (hyperlane/config/port.md)
[!tip]
hyperlane框架绑定端口方式如下:
// 省略 server 创建
server.port(60000).await;
Websocket 协议升级前回调 (hyperlane/config/pre_ws_upgrade.md)
[!tip]
hyperlane框架支持配置websocket协议升级前回调,此方法会在websocket协议升级前调用。
配置单个 websocket 协议升级前回调
server.pre_ws_upgrade(|ctx: Context| async move {
    // 处理
}).await;
配置多个 websocket 协议升级前回调
server.pre_ws_upgrade(|ctx: Context| async move {
    // 1
}).await;
server.pre_ws_upgrade(|ctx: Context| async move {
    // 2
}).await;
server.pre_ws_upgrade(|ctx: Context| async move {
    // 3
}).await;
server.pre_ws_upgrade(|ctx: Context| async move {
    // 4
}).await;
框架配置 (hyperlane/config/README.md)
路由 (hyperlane/config/route.md)
[!tip]
hyperlane框架使用route接口进行路由注册,第一个参数是路由名称,第二个参数是路由处理函数,
框架支持动态路由,更多路由详细使用请参考官方文档,
路由处理函数参数类型参考 controller-data 文档。
注册路由
// 省略 server 创建
server.route("路由名称", |ctx: Context| async move {
    // code
}).await;
运行时 (hyperlane/config/runtime.md)
[!tip]
hyperlane框架基于tokio,可以参考tokio官方文档 进行配置。
通过宏
#[tokio::main]
async fn main() {}
精细化配置
#[tokio::main]
async fn main() {
    let thread_count: usize = get_thread_count();
    let runtime: tokio::runtime::Runtime = tokio::runtime::Builder::new_multi_thread()
        .worker_threads(thread_count)
        .thread_stack_size(2097152)
        .max_blocking_threads(5120)
        .max_io_events_per_tick(5120)
        .enable_all()
        .build()
        .unwrap();
    runtime.spawn(async move {}).await.unwrap();
}
创建 Server (hyperlane/config/server.md)
[!tip]
hyperlane框架创建服务方式如下,需要调用run方法,服务才会正常运行。
let server: Server = Server::new();
server.run().await.unwrap();
Time To Live (hyperlane/config/ttl.md)
[!tip]
hyperlane框架支持配置ttl,该选项基于Tokio的TcpStream::set_ttl,用于控制IP_TTL选项,以设置传输数据包的生存时间(Time To Live),从而影响数据包在网络中的跳数限制。
设置 ttl
// 省略 server 创建
server.set_ttl(128).await;
Websocket 缓冲区 (hyperlane/config/ws-buffer-size.md)
设置 websocket 缓冲区大小
[!tip]
hyperlane框架设置websocket缓冲区大小方式如下:
不设置或者设置为0则默认是4096字节。
server.ws_buffer_size(4096).await;
内置 Websocket 处理 (hyperlane/config/ws-handler.md)
[!tip]
hyperlane框架支持配置websocket内部处理方式,默认启用,
值得一提的是此配置支持动态路由设置。
需要在路由注册的处理函数中手动处理请求。如果启用,需要设置具体的路由,则会自动处理请求缓存设置,一般用于单发场景。
如果不启用,则需要在业务代码中使用死循环去循环处理请求,一般用于群发场景。
启用框架内部 websocket 处理
静态路由
server.enable_ws_handler("/路由").await;
朴素动态路由
server.enable_ws_handler("/路由/{id}").await;
正则表达式动态路由
server.enable_ws_handler("/路由/{number:\\d+}").await;
禁用 websocket 内部处理
静态路由
server.disable_ws_handler("/路由").await;
动态路由
server.disable_ws_handler("/路由/{id}").await;
正则表达式动态路由
server.disable_ws_handler("/路由/{number:\\d+}").await;
异步 (hyperlane/help/async.md)
异步
[!tip]
由于hyperlane框架本身涉及到锁的数据均采取tokio中的读写锁实现,所以涉及到锁的方法调用均需要await。
构建 (hyperlane/help/build.md)
构建
cargo build --release
使用 docker 进行静态链接
Linux / MacOS
docker run --rm -v "$(pwd):/tmp/cargo_build" ccr.ccs.tencentyun.com/linux_environment/cargo:1.0.0 /bin/bash -c "source ~/.bashrc && cd /tmp/cargo_build && RUSTFLAGS='-C target-feature=-crt-static' cargo build --release --target x86_64-unknown-linux-gnu"
Windows
docker run --rm -v "${pwd}:/tmp/cargo_build" ccr.ccs.tencentyun.com/linux_environment/cargo:1.0.0 /bin/bash -c "source ~/.bashrc && cd /tmp/cargo_build && RUSTFLAGS='-C target-feature=-crt-static' cargo build --release --target x86_64-unknown-linux-gnu"
说明 (hyperlane/help/explain.md)
框架说明
[!tip]
hyperlane仅提供最核心的功能(路由、中间件、异常处理、请求处理等基础核心的功能)。其余功能支持全部复用crate.io生态,这意味着你可以在hyperlane里使用crate.io里的第三方库,在hyperlane里集成他们是非常容易的事情。
推荐阅读
[!tip]
推荐阅读 点击阅读 。
火焰图 (hyperlane/help/flamegraph.md)
[!tip]
hyperlane框架使用flamegraph,使用前提是需要有perf环境,生成火焰图步骤如下:
安装
cargo install flamegraph
使用
CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --release
安装 (hyperlane/help/install.md)
安装
[!tip]
如果不使用
Cargo.lock,请在版本号前加=来锁定版本。
[dependencies]
hyperlane = "=*.*.*"
帮助 (hyperlane/help/README.md)
跨域中间件 (hyperlane/middleware/cross.md)
[!tip]
hyperlane框架支持跨域中间件,用于处理跨域请求的情况。
跨域中间件
pub async fn cross_middleware(ctx: Context) {
    ctx.set_response_header(ACCESS_CONTROL_ALLOW_ORIGIN, ANY)
        .await
        .set_response_header(ACCESS_CONTROL_ALLOW_METHODS, ALL_METHODS)
        .await
        .set_response_header(ACCESS_CONTROL_ALLOW_HEADERS, ANY)
        .await;
}
async fn index(ctx: Context) {
    ctx.set_response_status_code(200)
        .await
        .set_response_body("Hello, world!")
        .await;
}
async fn response_middleware(ctx: Context) {
    ctx.send().await.unwrap();
}
#[tokio::main]
async fn main() {
    Server::new()
        .request_middleware(cross_middleware)
        .await
        .response_middleware(response_middleware)
        .await
        .route("/", index)
        .await
        .run()
        .await
        .unwrap();
}
中间件 (hyperlane/middleware/README.md)
超时中间件 (hyperlane/middleware/timeout.md)
[!tip]
hyperlane框架支持超时中间件,用于处理请求超时的情况。
超时中间件
async fn timeout_middleware(ctx: Context) {
    spawn(async move {
        timeout(Duration::from_millis(100), async move {
            ctx.aborted().await;
            ctx.set_response_status_code(200)
                .await
                .set_response_body("timeout")
                .unwrap();
        })
        .await
        .unwrap();
    });
}
async fn index(ctx: Context) {
    sleep(Duration::from_secs(1)).await;
    ctx.set_response_status_code(200)
        .await
        .set_response_body("Hello, world!")
        .await;
}
async fn response_middleware(ctx: Context) {
    ctx.send().await.unwrap();
}
#[tokio::main]
async fn main() {
    Server::new()
        .request_middleware(timeout_middleware)
        .await
        .response_middleware(response_middleware)
        .await
        .route("/", index)
        .await
        .run()
        .await
        .unwrap();
}
FileChunk (hyperlane/project/file-chunk.md)
[!tip]
克隆项目
git clone git@github.com:eastspire/hyperlane-quick-start.git;
进入项目目录
cd ./hyperlane-quick-start;
切换分支
git checkout playground;
运行
cargo run
浏览器访问
http://127.0.0.1:60006/upload
GroupChat (hyperlane/project/group-chat.md)
[!tip]
基于
hyperlane框架开发的全栈在线群聊项目
(点击此处在线体验)。
克隆项目
git clone git@github.com:eastspire/hyperlane-quick-start.git;
进入项目目录
cd ./hyperlane-quick-start;
切换分支
git checkout group-chat;
运行
cargo run
浏览器访问
http://127.0.0.1:60007/
Mysql (hyperlane/project/mysql.md)
[!tip]
基于
hyperlane框架,使用sqlx开发的mysql数据库demo。
克隆项目
git clone git@github.com:eastspire/hyperlane-quick-start.git;
进入项目目录
cd ./hyperlane-quick-start;
Mysql 配置
修改代码文件:src/config/mysql/constant.rs
切换分支
git checkout mysql;
运行
cargo run
示例项目 (hyperlane/project/README.md)
Redis (hyperlane/project/redis.md)
[!tip]
基于
hyperlane框架,使用redis和server-manager开发的redis数据库demo。
克隆项目
git clone git@github.com:eastspire/hyperlane-quick-start.git;
进入项目目录
cd ./hyperlane-quick-start;
Redis 配置
修改代码文件:src/config/redis.rs
切换分支
git checkout redis;
运行
cargo run
目录结构 (hyperlane/quick-start/directory.md)
[!tip]
基于
hyperlane设计的目录结构,配置和业务分离,扩展以插件形式存在,便于开发和维护。
├── app                      # app目录
│   ├── aspect               # 切面编程层
│   ├── controller           # 接口控制层
│   ├── exception            # 异常处理层
│   ├── filter               # 过滤器层
│   ├── mapper               # 数据访问层
│   ├── middleware           # 中间件层
│   ├── model                # 数据模型层
│      ├── application       # 应用对象
│      ├── bean              # 实体对象
│      ├── business          # 业务对象
│      ├── data              # 数据对象
│      ├── data_access       # 数据访问对象
│      ├── data_transfer     # 数据传输对象
│      ├── domain            # 领域对象
│      ├── param             # 参数对象
│      ├── persistent        # 持久化对象
│      ├── view              # 视图对象
│   ├── service              # 业务逻辑层
│   ├── utils                # 工具层
│   ├── view                 # 视图层
├── config                   # 配置目录
│   ├── business             # 业务配置
│   ├── framework            # 框架配置
│   ├── server_manager       # 服务管理配置
├── init                     # 初始化目录
│   ├── business             # 业务初始化
│   ├── framework            # 框架始化
├── plugin                   # 插件目录
│   ├── log                  # 日志插件
│   ├── server_manager       # 服务进程管理插件
├── resources                # 资源目录
│   ├── static               # 静态资源目录
│      ├── html              # HTML静态资源
│      ├── img               # 图片静态资源
│   ├── templates            # 模板目录
│      ├── html              # HTML模板
🗂 各层级调用关系详解
app/controller(接口控制层)
- 
调用: - service:处理业务逻辑。
- model/param:接收请求参数。
- model/view:返回视图对象。
- model/data_transfer:构建 DTO 返回。
- utils:使用工具函数处理请求数据。
- exception:统一异常抛出。
- filter/- middleware:作为请求链的入口或出口。
- aspect:被 AOP 织入切面逻辑。
- view:视图渲染。
- resources/templates:页面模板渲染。
- resources/static:静态资源引用。
- plugin/*:调用日志记录、服务管理等插件。
 
app/service(业务逻辑层)
- 
调用: - mapper:访问数据库。
- model/business:封装业务对象。
- model/domain:应用领域建模。
- model/data_transfer:服务返回值封装。
- exception:业务异常处理。
- utils:辅助计算、验证、转换等。
- plugin/*:调用插件完成增强能力。
 
- 
被调用: - controller
 
app/mapper(数据访问层)
- 
调用: - model/data_access:数据库表映射。
- model/persistent:持久化结构体。
- utils:SQL 构建等辅助操作。
 
- 
被调用: - service
 
app/model/*(数据模型层)
被多个模块依赖和使用,不主动调用其他层。
常用子模块说明:
| 子模块 | 使用场景 | 
|---|---|
| application | 应用级上下文对象,用于 service/mapper 层组合数据。 | 
| bean | 通用实体定义,如 User、Order 等。 | 
| business | 业务组合对象,如 OrderDetail + PaymentInfo。 | 
| data | 中间数据对象,在服务流程中传递状态。 | 
| data_access | 映射 DAO/ORM 结构,数据库字段。 | 
| data_transfer | DTO 层,controller → client 层数据输出。 | 
| domain | 领域建模,对应 DDD 的 Aggregate/Entity/VO。 | 
| param | controller 接收参数封装。 | 
| persistent | 映射数据库存储模型。 | 
| view | 用于最终渲染视图页面的模型。 | 
Model 详细介绍
| 目录名 | 中文名 | 典型职责 | 使用场景举例 | 与其它层关系 | 
|---|---|---|---|---|
| application | 应用对象 | 编排多个业务对象,处理用户用例 | 服务层 UserService聚合多个UserBO处理注册流程 | 调用 business,传递param、返回view | 
| bean | 实体对象 | 数据实体,表现为 Struct 或 ORM 实体 | UserEntity、ArticleEntity,保存于数据库 | 被 persistent持久化,供domain使用 | 
| business | 业务对象 | 封装核心业务逻辑(BO) | UserBO::register内部逻辑完整,不依赖框架 | 被 application调用 | 
| data | 数据对象 | 数据结构本身,不带行为(值对象、常量等) | GenderEnum、IdVO、DateRange | 被 domain和dto等层使用 | 
| data_access | 数据访问对象 | 封装数据库交互(DAO、Repository) | UserRepository::find_by_email() | 操作 bean或persistent | 
| data_transfer | 数据传输对象 | 接口中传输的数据载体,常用于请求响应、分页、统一结构 | ApiResponse、Page、UserDto | 被 controller、OpenAPI 文档广泛使用 | 
| param | 参数对象 | 接口入参、查询条件、分页等 | LoginParam、SearchQueryParam | 传入 application层 | 
| persistent | 持久化对象 | ORM 映射专用结构体,有时带属性注解 | UserPersistent映射数据库字段 | 与 bean相似,偏向实现层 | 
| domain | 领域对象 | 领域模型(实体和值对象),封装行为 | OrderAggregate,可带行为如Order::cancel() | 被 business聚合使用 | 
| view | 视图对象 | 接口输出结果的表现结构,适配前端需求 | UserProfileView、ArticleDetailView | 从 dto或bean转换而来 | 
app/exception(异常处理层)
- 
被调用: - controller
- service
- mapper
 
app/filter(过滤器层)
- 
被调用: - controller请求前过滤。
 
app/middleware(中间件层)
- 
被调用: - controller请求或响应阶段增强,如权限校验、Header 注入等。
 
app/aspect(切面编程层)
- 
被调用: - 自动织入 controller、service等层处理日志、安全等横切关注点。
 
- 自动织入 
app/utils(工具层)
- 
被调用: - controller
- service
- mapper
- model(可选)
 
app/view(视图层)
- 
被调用: - controller用于模板渲染(结合- resources/templates)
 
config(配置目录)
- 
被调用: - init:读取配置初始化。
- app:全局配置使用,如数据库、缓存、超时等。
 
- 
子目录说明: - business:业务层配置,如风控策略、规则开关。
- hyperlane:服务监听、路由、中间件配置。
- server_manager:进程托管策略。
 
init(初始化目录)
- 
调用: - config:读取配置。
- plugin:初始化日志、服务等插件。
- app:初始化 controller/service 等组件。
 
- 
被调用: - 由主程序启动时触发。
 
plugin(插件目录)
- 
被调用: - controller/- service/- init均可能调用。
 
- 
子模块: - log:日志记录、链路追踪。
- server_manager:守护进程、PID 控制等。
 
resources(资源目录)
- 
子目录说明: - static/html、- img:被- view层或浏览器直接访问。
- templates/html:被- controller或- view用于渲染页面。
 
快速开始 (hyperlane/quick-start/README.md)
Hello World
快速开始
[!tip]
这是基于hyperlane封装的项目(hyperlane-quick-start),旨在简化使用和规范项目代码结构。
克隆项目
git clone https://github.com/eastspire/hyperlane-quick-start.git
进入项目
cd hyperlane-quick-start
运行
[!tip]
此项目使用server-manager进行服务管理。
使用参考 官方文档。
运行
cargo run
在后台运行
cargo run -d
停止
cargo run stop
重启
cargo run restart
重启在后台运行
cargo run restart -d
热重启
cargo run hot
Web 后端框架 (hyperlane/README.md)
Hyperlane 是一个轻量级且高性能的 Rust HTTP 服务器库,旨在简化网络服务开发。它支持 HTTP 请求解析、响应构建和 TCP 通信,非常适合构建现代 Web 服务。此外,它还支持请求和响应中间件、WebSocket 和 Server-Sent Events (SSE),从而实现灵活高效的实时通信。Hyperlane 使用纯 Rust 和标准库构建,提供跨 Windows、Linux 和 macOS 的真正跨平台兼容性,且所有平台上的 API 体验一致,依托 Tokio 的异步运行时实现无缝网络通信,无需特定于平台的依赖。
安装
要使用此 crate,可以运行以下命令:
cargo add hyperlane
快速开始
git clone https://github.com/eastspire/hyperlane-quick-start.git
使用示例
use hyperlane::*;
async fn error_handler(error: PanicInfo) {
    eprintln!("{}", error.to_owned());
    let _ = std::io::Write::flush(&mut std::io::stderr());
}
async fn on_ws_connected(ctx: Context) {
    let _ = ctx.set_response_body("connected").await.send_body().await;
}
async fn request_middleware(ctx: Context) {
    let socket_addr: String = ctx.get_socket_addr_or_default_string().await;
    ctx.set_response_header(SERVER, HYPERLANE)
        .await
        .set_response_header(CONNECTION, KEEP_ALIVE)
        .await
        .set_response_header(CONTENT_TYPE, TEXT_PLAIN)
        .await
        .set_response_header("SocketAddr", socket_addr)
        .await;
}
async fn response_middleware(ctx: Context) {
    let _ = ctx.send().await;
}
async fn root_route(ctx: Context) {
    ctx.set_response_status_code(200)
        .await
        .set_response_body("Hello hyperlane => /")
        .await;
}
async fn ws_route(ctx: Context) {
    let key: String = ctx.get_request_header(SEC_WEBSOCKET_KEY).await.unwrap();
    let request_body: Vec= ctx.get_request_body().await;
    let _ = ctx.set_response_body(key).await.send_body().await;
    let _ = ctx.set_response_body(request_body).await.send_body().await;
}
async fn sse_route(ctx: Context) {
    let _ = ctx
        .set_response_header(CONTENT_TYPE, TEXT_EVENT_STREAM)
        .await
        .set_response_status_code(200)
        .await
        .send()
        .await;
    for i in 0..10 {
        let _ = ctx
            .set_response_body(format!("data:{}{}", i, HTTP_DOUBLE_BR))
            .await
            .send_body()
            .await;
    }
    let _ = ctx.closed().await;
}
async fn dynamic_route(ctx: Context) {
    let param: RouteParams = ctx.get_route_params().await;
    panic!("Test panic {:?}", param);
}
#[tokio::main]
async fn main() {
    let server: Server = Server::new();
    server.host("0.0.0.0").await;
    server.port(60000).await;
    server.enable_nodelay().await;
    server.disable_linger().await;
    server.http_buffer_size(4096).await;
    server.ws_buffer_size(4096).await;
    server.error_handler(error_handler).await;
    server.on_ws_connected(on_ws_connected).await;
    server.pre_ws_upgrade(request_middleware).await;
    server.request_middleware(request_middleware).await;
    server.response_middleware(response_middleware).await;
    server.route("/", root_route).await;
    server.route("/ws", ws_route).await;
    server.route("/sse", sse_route).await;
    server.route("/dynamic/{routing}", dynamic_route).await;
    server
        .route("/dynamic/routing/{file:^.*$}", dynamic_route)
        .await;
    server.run().await.unwrap();
}
关闭 Keep Alive (hyperlane/speed/close-keep-alive.md)
wrk
压测命令
wrk -c360 -d60s -H "Connection: close" http://127.0.0.1:60000/
压测结果
[!tip]
测试360并发,持续60s请求。QPS结果如下:
- 1
Hyperlane框架:51031.27- 2
Tokio:49555.87- 3
Rocket框架:49345.76- 4
Gin框架:40149.75- 5
Go标准库:38364.06- 6
Rust标准库:30142.55- 7
Node标准库:28286.96
hyperlane 框架
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.51ms    2.12ms 254.29ms   74.68%
    Req/Sec    25.69k     1.78k   42.56k    74.94%
  3066756 requests in 1.00m, 298.32MB read
Requests/sec:  51031.27
Transfer/sec:      4.96MB
Rust 标准库
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    13.39ms   39.09ms 938.33ms   93.24%
    Req/Sec    15.17k     1.25k   19.88k    71.08%
  1811006 requests in 1.00m, 151.99MB read
Requests/sec:  30142.55
Transfer/sec:      2.53MB
Tokio 框架
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.64ms    2.97ms 331.60ms   89.67%
    Req/Sec    24.93k     2.37k   31.57k    64.49%
  2976845 requests in 1.00m, 249.83MB read
Requests/sec:  49555.87
Transfer/sec:      4.16MB
Rocket 框架
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.70ms    3.23ms 246.75ms   92.68%
    Req/Sec    24.83k     2.31k   47.87k    71.72%
  2963056 requests in 1.00m, 729.05MB read
Requests/sec:  49345.76
Transfer/sec:     12.14MB
Gin 框架
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.69ms    2.66ms  37.49ms   68.89%
    Req/Sec    20.22k     3.79k   28.13k    59.02%
  2412349 requests in 1.00m, 322.08MB read
Requests/sec:  40149.75
Transfer/sec:      5.36MB
Go 标准库
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.96ms    3.17ms 248.63ms   75.61%
    Req/Sec    19.33k     4.01k   28.20k    59.12%
  2303964 requests in 1.00m, 307.61MB read
Requests/sec:  38364.06
Transfer/sec:      5.12MB
Node 标准库
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.76ms    3.48ms  55.44ms   68.85%
    Req/Sec    14.22k     2.88k   28.04k    83.54%
  1699058 requests in 1.00m, 233.33MB read
  Socket errors: connect 337, read 0, write 0, timeout 0
Requests/sec:  28286.96
Transfer/sec:      3.88MB
ab
压测命令
ab -n 1000000 -c 1000 -r http://127.0.0.1:60000/
压测结果
[!tip]
测试1000并发,一共100w请求。QPS结果如下:
- 1
Tokio:51825.13- 2
Hyperlane框架:51554.47- 3
Rocket框架:49621.02- 4
Go标准库:47915.20- 5
Gin框架:47081.05- 6
Node标准库:44763.11- 7
Rust标准库:31511.00
hyperlane 框架
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        5 bytes
Concurrency Level:      1000
Time taken for tests:   19.397 seconds
Complete requests:      1000000
Failed requests:        0
Total transferred:      107000000 bytes
HTML transferred:       5000000 bytes
Requests per second:    51554.47 [#/sec] (mean)
Time per request:       19.397 [ms] (mean)
Time per request:       0.019 [ms] (mean, across all concurrent requests)
Transfer rate:          5387.04 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    9   9.1      8    1069
Processing:     0   10   4.7     10     289
Waiting:        0    9   4.5      9     286
Total:          1   19  11.1     19    1085
Percentage of the requests served within a certain time (ms)
  50%     19
  66%     22
  75%     24
  80%     25
  90%     29
  95%     33
  98%     37
  99%     41
 100%   1085 (longest request)
Rust 标准库
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        5 bytes
Concurrency Level:      1000
Time taken for tests:   31.735 seconds
Complete requests:      1000000
Failed requests:        0
Total transferred:      88000000 bytes
HTML transferred:       5000000 bytes
Requests per second:    31511.00 [#/sec] (mean)
Time per request:       31.735 [ms] (mean)
Time per request:       0.032 [ms] (mean, across all concurrent requests)
Transfer rate:          2707.98 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   22 167.7      0    7232
Processing:     0    9  45.2      4    5771
Waiting:        0    9  45.2      4    5771
Total:          0   31 178.6      4    7441
Percentage of the requests served within a certain time (ms)
  50%      4
  66%      5
  75%      5
  80%      6
  90%      7
  95%      8
  98%    426
  99%   1050
 100%   7441 (longest request)
Tokio 框架
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        5 bytes
Concurrency Level:      1000
Time taken for tests:   19.296 seconds
Complete requests:      1000000
Failed requests:        0
Total transferred:      88000000 bytes
HTML transferred:       5000000 bytes
Requests per second:    51825.13 [#/sec] (mean)
Time per request:       19.296 [ms] (mean)
Time per request:       0.019 [ms] (mean, across all concurrent requests)
Transfer rate:          4453.72 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    9  19.4      8    1091
Processing:     0   10   5.4      9     284
Waiting:        0    9   5.2      8     284
Total:          0   19  20.6     18    1107
Percentage of the requests served within a certain time (ms)
  50%     18
  66%     21
  75%     23
  80%     25
  90%     29
  95%     33
  98%     38
  99%     42
 100%   1107 (longest request)
Rocket 框架
Server Software:        Rocket
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        13 bytes
Concurrency Level:      1000
Time taken for tests:   20.153 seconds
Complete requests:      1000000
Failed requests:        0
Total transferred:      247000000 bytes
HTML transferred:       13000000 bytes
Requests per second:    49621.02 [#/sec] (mean)
Time per request:       20.153 [ms] (mean)
Time per request:       0.020 [ms] (mean, across all concurrent requests)
Transfer rate:          11969.13 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    9  11.2      9    1094
Processing:     0   11   5.4     10     305
Waiting:        0   10   5.2      9     305
Total:          0   20  13.3     19    1107
Percentage of the requests served within a certain time (ms)
  50%     19
  66%     22
  75%     25
  80%     26
  90%     30
  95%     34
  98%     39
  99%     43
 100%   1107 (longest request)
Gin 框架
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        5 bytes
Concurrency Level:      1000
Time taken for tests:   21.240 seconds
Complete requests:      1000000
Failed requests:        0
Total transferred:      140000000 bytes
HTML transferred:       5000000 bytes
Requests per second:    47081.05 [#/sec] (mean)
Time per request:       21.240 [ms] (mean)
Time per request:       0.021 [ms] (mean, across all concurrent requests)
Transfer rate:          6436.86 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   10  13.0      9    1095
Processing:     0   12   6.0     11     288
Waiting:        0   11   5.8     10     286
Total:          1   21  15.1     20    1114
Percentage of the requests served within a certain time (ms)
  50%     20
  66%     23
  75%     26
  80%     27
  90%     32
  95%     35
  98%     40
  99%     44
 100%   1114 (longest request)
Go 标准库
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        13 bytes
Concurrency Level:      1000
Time taken for tests:   20.870 seconds
Complete requests:      1000000
Failed requests:        0
Total transferred:      149000000 bytes
HTML transferred:       13000000 bytes
Requests per second:    47915.20 [#/sec] (mean)
Time per request:       20.870 [ms] (mean)
Time per request:       0.021 [ms] (mean, across all concurrent requests)
Transfer rate:          6972.04 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    9  21.1      8    1103
Processing:     0   11   6.5     11     323
Waiting:        0   10   6.3     10     322
Total:          1   21  22.6     19    1120
Percentage of the requests served within a certain time (ms)
  50%     19
  66%     23
  75%     25
  80%     27
  90%     31
  95%     35
  98%     41
  99%     46
 100%   1120 (longest request)
Node 标准库
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        13 bytes
Concurrency Level:      1000
Time taken for tests:   22.340 seconds
Complete requests:      1000000
Failed requests:        0
Total transferred:      114000000 bytes
HTML transferred:       13000000 bytes
Requests per second:    44763.11 [#/sec] (mean)
Time per request:       22.340 [ms] (mean)
Time per request:       0.022 [ms] (mean, across all concurrent requests)
Transfer rate:          4983.39 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    6  42.1      4    1086
Processing:     0   16  11.7     15     453
Waiting:        0   13  11.2     12     452
Total:          1   22  43.7     20    1108
Percentage of the requests served within a certain time (ms)
  50%     20
  66%     22
  75%     23
  80%     24
  90%     27
  95%     29
  98%     33
  99%     37
 100%   1108 (longest request)
环境信息 (hyperlane/speed/env.md)
环境信息
- 系统: Ubuntu20.04.6 LTS
- CPU: i9-14900KF
- 内存: 192GB 6400MT/S(实际运行 4000MT/S)
- 硬盘: SKC3000D2048G * 2
- GPU: AMD Radeon RX 6750 GRE 10GB
调优
Linux 内核调优
打开文件
/etc/sysctl.conf,增加以下设置。
#该参数设置系统的TIME_WAIT的数量,如果超过默认值则会被立即清除
net.ipv4.tcp_max_tw_buckets = 20000
#定义了系统中每一个端口最大的监听队列的长度,这是个全局的参数
net.core.somaxconn = 65535
#对于还未获得对方确认的连接请求,可保存在队列中的最大数目
net.ipv4.tcp_max_syn_backlog = 262144
#在每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目
net.core.netdev_max_backlog = 30000
#此选项会导致处于NAT网络的客户端超时,建议为0。Linux从4.12内核开始移除了 tcp_tw_recycle 配置,如果报错"No such file or directory"请忽略
net.ipv4.tcp_tw_recycle = 0
#系统所有进程一共可以打开的文件数量
fs.file-max = 6815744
#防火墙跟踪表的大小。注意:如果防火墙没开则会提示error: "net.netfilter.nf_conntrack_max" is an unknown key,忽略即可
net.netfilter.nf_conntrack_max = 2621440
net.ipv4.ip_local_port_range = 10240 65000
控制台执行 ulimit
ulimit -n 1024000
打开文件数
修改
open files的数值重启后永久生效,修改配置文件:/etc/security/limits.conf。在这个文件后加上
* soft nofile 1024000
* hard nofile 1024000
root soft nofile 1024000
root hard nofile 1024000
运行命令
RUSTFLAGS="-C target-cpu=native -C link-arg=-fuse-ld=lld" cargo run --release
火焰图 (hyperlane/speed/flamegraph.md)
plaintext
query db
sleep 100ms
开启 Keep Alive (hyperlane/speed/open-keep-alive.md)
wrk
压测命令
wrk -c360 -d60s http://127.0.0.1:60000/
压测结果
[!tip]
测试360并发,持续60s请求。QPS结果如下:
- 1
Tokio:340130.92- 2
Hyperlane框架:324323.71- 3
Rocket框架:298945.31- 4
Rust标准库:291218.96- 5
Gin框架:242570.16- 6
Go标准库:234178.93- 7
Node标准库:139412.13
hyperlane 框架
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.46ms    7.74ms 230.59ms   99.57%
    Req/Sec   163.12k     9.54k  187.65k    67.75%
  19476349 requests in 1.00m, 1.94GB read
Requests/sec: 324323.71
Transfer/sec:     33.10MB
Rust 标准库
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.64ms    8.62ms 238.68ms   99.48%
    Req/Sec   146.49k    20.42k  190.38k    61.42%
  17494266 requests in 1.00m, 1.52GB read
Requests/sec: 291218.96
Transfer/sec:     25.83MB
Tokio 框架
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.22ms    5.96ms 230.76ms   99.76%
    Req/Sec   171.05k     7.56k  192.19k    70.08%
  20423683 requests in 1.00m, 1.77GB read
Requests/sec: 340130.92
Transfer/sec:     30.17MB
Rocket 框架
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.42ms    6.67ms 228.04ms   99.67%
    Req/Sec   150.37k     7.48k  172.42k    70.08%
  17955815 requests in 1.00m, 4.00GB read
Requests/sec: 298945.31
Transfer/sec:     68.14MB
Gin 框架
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.67ms    4.67ms 249.72ms   99.63%
    Req/Sec   122.08k     4.39k  133.88k    69.58%
  14577127 requests in 1.00m, 1.97GB read
Requests/sec: 242570.16
Transfer/sec:     33.54MB
Go 标准库
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.58ms    1.15ms  32.24ms   78.06%
    Req/Sec   117.80k     4.43k  130.07k    70.67%
  14064777 requests in 1.00m, 1.90GB read
Requests/sec: 234178.93
Transfer/sec:     32.38MB
Node 标准库
Running 1m test @ http://127.0.0.1:60000/
  2 threads and 360 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     2.58ms  837.62us  45.39ms   89.66%
    Req/Sec    70.11k     2.79k   74.29k    98.33%
  8371733 requests in 1.00m, 1.16GB read
Requests/sec: 139412.13
Transfer/sec:     19.81MB
ab
压测命令
ab -n 1000000 -c 1000 -r -k http://127.0.0.1:60000/
压测结果
[!tip]
测试1000并发,一共100w请求。QPS结果如下:
- 1
Tokio:308596.26- 2
Hyperlane框架:307568.90- 3
Rocket框架:267931.52- 4
Rust标准库:260514.56- 5
Go标准库:226550.34- 6
Gin框架:224296.16- 7
Node标准库:85357.18
hyperlane 框架
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        5 bytes
Concurrency Level:      1000
Time taken for tests:   3.251 seconds
Complete requests:      1000000
Failed requests:        0
Keep-Alive requests:    1000000
Total transferred:      107000000 bytes
HTML transferred:       5000000 bytes
Requests per second:    307568.90 [#/sec] (mean)
Time per request:       3.251 [ms] (mean)
Time per request:       0.003 [ms] (mean, across all concurrent requests)
Transfer rate:          32138.55 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0      11
Processing:     0    3   1.4      3      13
Waiting:        0    3   1.4      3      13
Total:          0    3   1.4      3      16
Percentage of the requests served within a certain time (ms)
  50%      3
  66%      4
  75%      4
  80%      4
  90%      5
  95%      6
  98%      7
  99%      7
 100%     16 (longest request)
Rust 标准库
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        5 bytes
Concurrency Level:      1000
Time taken for tests:   3.839 seconds
Complete requests:      1000000
Failed requests:        0
Keep-Alive requests:    1000000
Total transferred:      93000000 bytes
HTML transferred:       5000000 bytes
Requests per second:    260514.56 [#/sec] (mean)
Time per request:       3.839 [ms] (mean)
Time per request:       0.004 [ms] (mean, across all concurrent requests)
Transfer rate:          23660.01 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0  21.2      0    1069
Processing:     0    3   5.5      3     419
Waiting:        0    3   5.5      3     419
Total:          0    4  23.4      3    1286
Percentage of the requests served within a certain time (ms)
  50%      3
  66%      4
  75%      4
  80%      4
  90%      5
  95%      6
  98%      8
  99%      8
 100%   1286 (longest request)
Tokio 框架
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        5 bytes
Concurrency Level:      1000
Time taken for tests:   3.240 seconds
Complete requests:      1000000
Failed requests:        0
Keep-Alive requests:    1000000
Total transferred:      93000000 bytes
HTML transferred:       5000000 bytes
Requests per second:    308596.26 [#/sec] (mean)
Time per request:       3.240 [ms] (mean)
Time per request:       0.003 [ms] (mean, across all concurrent requests)
Transfer rate:          28026.81 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0      11
Processing:     0    3   1.3      3      16
Waiting:        0    3   1.3      3      16
Total:          0    3   1.4      3      16
Percentage of the requests served within a certain time (ms)
  50%      3
  66%      4
  75%      4
  80%      4
  90%      5
  95%      6
  98%      7
  99%      7
 100%     16 (longest request)
Rocket 框架
Server Software:        Rocket
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        13 bytes
Concurrency Level:      1000
Time taken for tests:   3.732 seconds
Complete requests:      1000000
Failed requests:        0
Keep-Alive requests:    1000000
Total transferred:      271000000 bytes
HTML transferred:       13000000 bytes
Requests per second:    267931.52 [#/sec] (mean)
Time per request:       3.732 [ms] (mean)
Time per request:       0.004 [ms] (mean, across all concurrent requests)
Transfer rate:          70907.66 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0      14
Processing:     0    4   1.4      4      17
Waiting:        0    4   1.4      4      17
Total:          0    4   1.4      4      21
Percentage of the requests served within a certain time (ms)
  50%      4
  66%      4
  75%      5
  80%      5
  90%      6
  95%      6
  98%      7
  99%      8
 100%     21 (longest request)
Gin 框架
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        5 bytes
Concurrency Level:      1000
Time taken for tests:   4.458 seconds
Complete requests:      1000000
Failed requests:        0
Keep-Alive requests:    1000000
Total transferred:      145000000 bytes
HTML transferred:       5000000 bytes
Requests per second:    224296.16 [#/sec] (mean)
Time per request:       4.458 [ms] (mean)
Time per request:       0.004 [ms] (mean, across all concurrent requests)
Transfer rate:          31760.69 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       7
Processing:     0    4   4.7      4     181
Waiting:        0    4   4.7      4     181
Total:          0    4   4.8      4     184
Percentage of the requests served within a certain time (ms)
  50%      4
  66%      5
  75%      5
  80%      6
  90%      8
  95%     10
  98%     12
  99%     13
 100%    184 (longest request)
Go 标准库
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        13 bytes
Concurrency Level:      1000
Time taken for tests:   4.414 seconds
Complete requests:      1000000
Failed requests:        0
Keep-Alive requests:    1000000
Total transferred:      154000000 bytes
HTML transferred:       13000000 bytes
Requests per second:    226550.34 [#/sec] (mean)
Time per request:       4.414 [ms] (mean)
Time per request:       0.004 [ms] (mean, across all concurrent requests)
Transfer rate:          34071.05 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       7
Processing:     0    4   3.9      4     172
Waiting:        0    4   3.9      4     172
Total:          0    4   4.0      4     176
Percentage of the requests served within a certain time (ms)
  50%      4
  66%      4
  75%      5
  80%      5
  90%      7
  95%      8
  98%      8
  99%      9
 100%    176 (longest request)
Node 标准库
Server Hostname:        127.0.0.1
Server Port:            60000
Document Path:          /
Document Length:        13 bytes
Concurrency Level:      1000
Time taken for tests:   11.715 seconds
Complete requests:      1000000
Failed requests:        811908
   (Connect: 0, Receive: 14737, Length: 499810, Exceptions: 297361)
Keep-Alive requests:    500200
Total transferred:      59523800 bytes
HTML transferred:       6502600 bytes
Requests per second:    85357.18 [#/sec] (mean)
Time per request:       11.715 [ms] (mean)
Time per request:       0.012 [ms] (mean, across all concurrent requests)
Transfer rate:          4961.70 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    3  33.5      0    1082
Processing:     0    8   9.6      7     247
Waiting:        0    7  10.5      3     247
Total:          0   12  35.3      9    1102
Percentage of the requests served within a certain time (ms)
  50%      9
  66%     15
  75%     17
  80%     18
  90%     21
  95%     23
  98%     27
  99%     30
 100%   1102 (longest request)
性能测试 (hyperlane/speed/README.md)
响应时间测试 (hyperlane/speed/request-time.md)
[!tip]
测试累计请求1w次
| 场景 | http-request 平均耗时 | hyper 平均耗时 | 
|---|---|---|
| TCP 失败 | 39us | 78us | 
| hyperlane | 100us | 150us | 
| 阿帕奇 | 300us | 2500us | 
Content-Type (hyperlane/type/content-type.md)
[!tip]
hyperlane框架的Content-Type内部value定义参考 Content-Type。
Context (hyperlane/type/context.md)
[!tip]
hyperlane框架的Context作为中间件和路由处理函数的唯一的参数类型,其内部保存了上下文数据,具体类型定义如下:
#[derive(Clone, Data, Default)]
pub struct InnerContext {
    aborted: bool,
    closed: bool,
    stream: OptionArcRwLockStream,
    request: Request,
    response: Response,
    attributes: HashMapArcAnySendSync,
    route_params: RouteParams,
}
#[derive(Clone, Default)]
pub struct Context(pub(super) ArcRwLock);
FileExtension (hyperlane/type/file-extension.md)
[!tip]
hyperlane框架的FileExtension内部具体类型定义参考 FileExtension。
HttpStatus (hyperlane/type/http-status.md)
[!tip]
hyperlane框架的HttpStatus内部具体类型定义如下
/// Represents common HTTP status codes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HttpStatus {
    /// 100 Continue
    Continue,
    /// 101 Switching Protocols
    SwitchingProtocols,
    /// 102 Processing (WebDAV)
    Processing,
    /// 103 Early Hints
    EarlyHints,
    /// 200 OK
    Ok,
    /// 201 Created
    Created,
    /// 202 Accepted
    Accepted,
    /// 203 Non-Authoritative Information
    NonAuthoritativeInformation,
    /// 204 No Content
    NoContent,
    /// 205 Reset Content
    ResetContent,
    /// 206 Partial Content
    PartialContent,
    /// 207 Multi-Status (WebDAV)
    MultiStatus,
    /// 208 Already Reported (WebDAV)
    AlreadyReported,
    /// 226 IM Used
    IMUsed,
    /// 300 Multiple Choices
    MultipleChoices,
    /// 301 Moved Permanently
    MovedPermanently,
    /// 302 Found
    Found,
    /// 303 See Other
    SeeOther,
    /// 304 Not Modified
    NotModified,
    /// 305 Use Proxy
    UseProxy,
    /// 307 Temporary Redirect
    TemporaryRedirect,
    /// 308 Permanent Redirect
    PermanentRedirect,
    /// 400 Bad Request
    BadRequest,
    /// 401 Unauthorized
    Unauthorized,
    /// 402 Payment Required
    PaymentRequired,
    /// 403 Forbidden
    Forbidden,
    /// 404 Not Found
    NotFound,
    /// 405 Method Not Allowed
    MethodNotAllowed,
    /// 406 Not Acceptable
    NotAcceptable,
    /// 407 Proxy Authentication Required
    ProxyAuthenticationRequired,
    /// 408 Request Timeout
    RequestTimeout,
    /// 409 Conflict
    Conflict,
    /// 410 Gone
    Gone,
    /// 411 Length Required
    LengthRequired,
    /// 412 Precondition Failed
    PreconditionFailed,
    /// 413 Payload Too Large
    PayloadTooLarge,
    /// 414 URI Too Long
    URITooLong,
    /// 415 Unsupported Media Type
    UnsupportedMediaType,
    /// 416 Range Not Satisfiable
    RangeNotSatisfiable,
    /// 417 Expectation Failed
    ExpectationFailed,
    /// 418 I'm a teapot
    ImATeapot,
    /// 421 Misdirected Request
    MisdirectedRequest,
    /// 422 Unprocessable Entity (WebDAV)
    UnprocessableEntity,
    /// 423 Locked (WebDAV)
    Locked,
    /// 424 Failed Dependency (WebDAV)
    FailedDependency,
    /// 425 Too Early
    TooEarly,
    /// 426 Upgrade Required
    UpgradeRequired,
    /// 428 Precondition Required
    PreconditionRequired,
    /// 429 Too Many Requests
    TooManyRequests,
    /// 431 Request Header Fields Too Large
    RequestHeaderFieldsTooLarge,
    /// 451 Unavailable For Legal Reasons
    UnavailableForLegalReasons,
    /// 500 Internal Server Error
    InternalServerError,
    /// 501 Not Implemented
    NotImplemented,
    /// 502 Bad Gateway
    BadGateway,
    /// 503 Service Unavailable
    ServiceUnavailable,
    /// 504 Gateway Timeout
    GatewayTimeout,
    /// 505 HTTP Version Not Supported
    HTTPVersionNotSupported,
    /// 506 Variant Also Negotiates
    VariantAlsoNegotiates,
    /// 507 Insufficient Storage (WebDAV)
    InsufficientStorage,
    /// 508 Loop Detected (WebDAV)
    LoopDetected,
    /// 510 Not Extended
    NotExtended,
    /// 511 Network Authentication Required
    NetworkAuthenticationRequired,
    /// Unknown status code
    Unknown,
}
Http 类型库 (hyperlane/type/http-type.md)
[!tip]
hyperlane框架的大部分类型封装在http-type库,内部提供大量常量以及类型定义,框架已默认导入和导出,无需额外安装和导入。
使用参考 官方文档。
HttpVersion (hyperlane/type/http-version.md)
[!tip]
hyperlane框架的UpgradeType内部具体类型定义如下
/// Represents the HTTP version used in the request or response.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HttpVersion {
    /// HTTP version 0.9
    HTTP0_9,
    /// HTTP version 1.0
    HTTP1_0,
    /// HTTP version 1.1
    HTTP1_1,
    /// HTTP version 2.0
    HTTP2,
    /// HTTP version 3.0
    HTTP3,
    /// Unknown version
    Unknown(String),
}
Method (hyperlane/type/method.md)
[!tip]
hyperlane框架的Method内部具体类型定义如下
/// Defines the `Method` enum, representing HTTP request methods.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Method {
    /// Represents the HTTP `GET` method.
    GET,
    /// Represents the HTTP `POST` method.
    POST,
    /// Represents the HTTP `PUT` method.
    PUT,
    /// Represents the HTTP `DELETE` method.
    DELETE,
    /// Represents the HTTP `PATCH` method.
    PATCH,
    /// Represents the HTTP `HEAD` method.
    HEAD,
    /// Represents the HTTP `OPTIONS` method.
    OPTIONS,
    /// Represents the HTTP `CONNECT` method.
    CONNECT,
    /// Represents the HTTP `TRACE` method.
    TRACE,
    /// Unknown
    UNKNOWN(String),
}
Protocol (hyperlane/type/protocol.md)
[!tip]
hyperlane框架的Protocol内部具体类型定义如下
/// Defines the `Protocol` enum, representing HTTP-related protocols.
///
/// The `Protocol` enum includes:
/// - `HTTP`: Represents the HTTP protocol.
/// - `HTTPS`: Represents the HTTPS protocol.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Protocol {
    /// Represents the HTTP protocol.
    HTTP,
    /// Represents the HTTPS protocol.
    HTTPS,
    /// Unknown
    Unknown(String),
}
类型定义 (hyperlane/type/README.md)
Request (hyperlane/type/request.md)
[!tip]
hyperlane框架的Request内部具体类型定义如下
/// HTTP request method.
pub type RequestMethod = Method;
/// The host part of an HTTP request.
pub type RequestHost = String;
/// The HTTP version (e.g., HTTP/1.1).
pub type RequestVersion = HttpVersion;
/// The path portion of the request URL.
pub type RequestPath = String;
/// Key type used in the request query parameters.
pub type RequestQuerysKey = String;
/// Value type used in the request query parameters.
pub type RequestQuerysValue = String;
/// All query parameters parsed from the request URL.
pub type RequestQuerys = HashMapXxHash3_64;
/// The raw binary body of the request.
pub type RequestBody = Vec;
/// The request body as a UTF-8 string.
pub type RequestBodyString = String;
/// Key type used in the request headers.
pub type RequestHeadersKey = String;
/// Value type used in the request headers.
pub type RequestHeadersValue = String;
/// All headers sent with the HTTP request.
pub type RequestHeaders = HashMapXxHash3_64;
/// The result type returned from a request reader handler.
pub type RequestReaderHandleResult = Result;
/// Read guard for a `Request` wrapped in a `RwLock`.
pub type RwLockReadGuardRequest= RwLockReadGuard;
/// Write guard for a `Request` wrapped in a `RwLock`.
pub type RwLockWriteGuardRequest= RwLockWriteGuard;
/// Optional value for a query parameter.
pub type OptionRequestQuerysValue = Option;
/// Optional value for a header.
pub type OptionRequestHeadersValue = Option;
/// Represents a parsed HTTP request.
#[derive(Debug, Clone, Getter, DisplayDebug)]
pub struct Request {
    /// The HTTP method of the request.
    pub(super) method: RequestMethod,
    /// The host of the request.
    pub(super) host: RequestHost,
    /// The HTTP version used in the request.
    pub(super) version: RequestVersion,
    /// The request path.
    pub(super) path: RequestPath,
    /// The query string of the request.
    pub(super) querys: RequestQuerys,
    /// A collection of HTTP headers as key-value pairs.
    pub(super) headers: RequestHeaders,
    /// The binary body of the request.
    pub(super) body: RequestBody,
}
Response (hyperlane/type/response.md)
[!tip]
hyperlane框架的Response内部具体类型定义如下
/// The binary body of the HTTP response.
pub type ResponseBody = Vec;
/// The body of the HTTP response represented as a UTF-8 string.
pub type ResponseBodyString = String;
/// The key type used in HTTP response headers.
pub type ResponseHeadersKey = String;
/// The value type used in HTTP response headers.
pub type ResponseHeadersValue = String;
/// A map of HTTP response headers.
pub type ResponseHeaders = HashMapXxHash3_64;
/// The HTTP version of the response (e.g., "HTTP/1.1").
pub type ResponseVersion = String;
/// The numeric status code of the HTTP response (e.g., 200, 404).
pub type ResponseStatusCode = usize;
/// The reason phrase associated with the HTTP status code (e.g., "OK", "Not Found").
pub type ResponseReasonPhrase = String;
/// The result type returned after writing an HTTP response.
pub type ResponseResult = Result;
/// The full serialized binary content of the HTTP response.
pub type ResponseData = Vec;
/// The full serialized content of the HTTP response as a UTF-8 string.
pub type ResponseDataString = String;
/// A read guard to a shared `Response` value protected by `RwLock`.
pub type RwLockReadGuardResponse= RwLockReadGuard;
/// A write guard to a shared `Response` value protected by `RwLock`.
pub type RwLockWriteGuardResponse= RwLockWriteGuard;
/// An optional value of a response header.
pub type OptionResponseHeadersValue = Option;
/// Represents a parsed HTTP response.
#[derive(Debug, Clone, Data, DisplayDebug)]
pub struct Response {
    /// The HTTP version used in the response.
    #[set(skip)]
    pub(super) version: ResponseVersion,
    /// The HTTP status code (e.g., 200, 404).
    pub(super) status_code: ResponseStatusCode,
    /// The reason phrase associated with the status code (e.g., "OK", "Not Found").
    #[set(skip)]
    pub(super) reason_phrase: ResponseReasonPhrase,
    /// The response headers as key-value pairs.
    pub(super) headers: ResponseHeaders,
    /// The binary body content of the response.
    #[set(skip)]
    pub(super) body: ResponseBody,
}
Stream (hyperlane/type/stream.md)
[!tip]
hyperlane框架的Stream内部具体类型定义如下
/// A thread-safe reference-counted `TcpStream`.
pub type ArcStream = Arc;
/// An optional thread-safe reference-counted `TcpStream`.
pub type OptionArcTcpStream = Option;
/// An optional thread-safe read-write locked `TcpStream` wrapper.
pub type OptionArcRwLockStream = Option;
/// A read guard for a `RwLock`.
pub type RwLockReadGuardTcpStream= RwLockReadGuard;
/// A write guard for a `RwLock`.
pub type RwLockWriteGuardTcpStream= RwLockWriteGuard;
/// A thread-safe reference to a `RwLock` write guard for `TcpStream`.
pub type ArcRwLockWriteGuardTcpStream= Arc>;
/// An optional thread-safe reference to a `RwLock` write guard for `TcpStream`.
pub type OptionArcRwLockWriteGuardTcpStream= Option>;
/// A thread-safe reference to a `Mutex` guard for `TcpStream`.
pub type ArcMutexGuardTcpStream= Arc>;
/// An optional thread-safe reference to a `Mutex` guard for `TcpStream`.
pub type OptionArcMutexGuardTcpStream= Option>;
/// A socket host represented by an IP address.
pub type SocketHost = IpAddr;
/// A socket port number.
pub type SocketPort = u16;
/// An optional socket host.
pub type OptionSocketHost = Option;
/// An optional socket port.
pub type OptionSocketPort = Option;
/// An optional full socket address.
pub type OptionSocketAddr = Option;
/// A wrapper around `Arc>`.
///
/// `ArcRwLockStream` provides shared, thread-safe access to a `TcpStream`
/// using an atomic reference counter (`Arc`) combined with a read-write lock (`RwLock`).
/// It is primarily used to safely share the stream across asynchronous tasks.
///
/// # Fields
/// - `0`: The inner `Arc>` stream.
#[derive(Clone, Debug)]
pub struct ArcRwLockStream(pub(super) ArcRwLock);
UpgradeType (hyperlane/type/upgrade-type.md)
[!tip]
hyperlane框架的UpgradeType内部具体类型定义如下
/// Represents different upgrade types.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UpgradeType {
    /// WebSocket protocol upgrade
    WebSocket,
    /// HTTP/2 cleartext upgrade (h2c)
    H2c,
    /// TLS upgrade (rare, experimental)
    Tls(String),
    /// Other custom or unknown upgrade protocols
    Unknown(String),
}
客户端地址 (hyperlane/usage-introduction/addr.md)
[!tip]
hyperlane框架封装了获取客户端地址的方法
使用
获取 SocketAddr
ctx.get_socket_addr().await;
获取 SocketAddr 如果失败使用默认值
ctx.get_socket_addr_or_default().await;
获取 SocketAddr 字符串
ctx.get_socket_addr_string().await;
获取 SocketAddr 字符串,如果失败使用默认值
ctx.get_socket_addr_or_default_string().await;
获取 SocketHost
ctx.get_socket_host().await;
获取 SocketPort
ctx.get_socket_port().await;
异步运行时 (hyperlane/usage-introduction/async.md)
[!tip]
hyperlane框架在v3.0.0之前不对异步做任何处理,如果需要异步操作,可以引入第三方库
hyperlane框架在v3.0.0之后内置异步机制
[!tip]
hyperlane框架在v4.0.0之前支持同步和异步中间件/路由共存。
hyperlane框架在v4.0.0之后为了性能移除了同步中间件和路由(all in async),在开启keep-alive情况下带来了效果QPS 10w+的提升
框架本身异步使用
server.route("/", move |_| async move {
    println!("hello");
}).await;
下面是使用 tokio 库的异步运行时示例代码
v4.0.0 之后版本的示例代码
use hyperlane::*;
use runtime::Runtime;
async fn some_async_task() -> i32 {
    println!("Starting the task...");
    // 模拟异步操作
    tokio::time::sleep(std::time::Duration::from_secs(2)).await;
    println!("Task completed!");
    0
}
#[tokio::main]
async fn main() {
    let mut server: Server = Server::new();
    server.host("0.0.0.0");
    server.port(60000);
    server.log_size(1_024_000);
    server.route("/", move |ctx: Context| {
        let rt = Runtime::new().unwrap();
        // 使用 block_on 启动异步代码
        rt.block_on(async move {
            let async_task = async move {
                let handle: task::JoinHandle= tokio::spawn(async {
                    let result: i32 = some_async_task().await;
                    println!("Result of the async task: {}", result);
                });
                // 模拟异步任务
                handle.await.unwrap();
            };
            // 使用 tokio::spawn 启动一个新的异步任务
            tokio::spawn(async_task).await.unwrap();
        });
    });
    server.listen();
}
异步闭包捕获外部变量
使用 async move
let test_string: String = "test".to_owned();
server.route("/test/async", move |_| {
    let tmp_test_string = test_string.clone();
    async move {
        println!("{:?}", tmp_test_string);
    }
}).await;
使用 future_fn!
let test_string: String = "test".to_owned();
let func = future_fn!(test_string, |_| {
    println!("async_move => {:?}", test_string);
});
server.route("/test/async", func).await;
属性 (hyperlane/usage-introduction/attribute.md)
[!tip]
hyperlane框架支持临时上下文属性以key-value形式存储,生命周期贯穿一个完整的请求和响应。
存储的value支持实现了Any + Send + Sync + Clone的trait的类型。
设置某个临时上下文属性
ctx.set_attribute("key", &"value").await;
获取某个临时上下文属性
let value: Option= ctx.get_attribute::("key").await;
移除某个临时上下文属性
ctx.remove_attribute("key").await;
清空临时上下文属性
ctx.clear_attribute().await;
额外示例
设置闭包
[!tip]
闭包需要实现Send + Sync的trait,否则无法跨线程调用。
不推荐value存储函数,这里只是提供一个示例
let func: &(dyn Fn(&str) + Send + Sync) = &|msg: &str| {
    println_success!("hyperlane: ", msg);
};
ctx.set_attribute("println_hyperlane", &func).await;
let println_hyperlane = ctx
    .get_attribute::("println_hyperlane")
    .await
    .unwrap();
println_hyperlane("test");
生命周期 (hyperlane/usage-introduction/lifetime.md)
中间件洋葱模型
hyperlane 框架版本 [!tip]
具体生命周期如下:
- 先根据注册顺序执行同步路由
- 最后执行同步路由
hyperlane 框架 >= v3.0.0 且 [!tip]
具体生命周期如下:
- 先根据注册顺序处理所有的异步中间件
- 再处理所有的异步中间件,异步中间件执行能保证和代码注册顺序一致
- 再根据注册顺序执行同步路由,如果同步路由存在则不会执行同名的异步路由
- 最后执行异步路由
hyperlane 框架 >= v4.0.0 的版本
[!tip]
具体生命周期如下:
- 先根据注册顺序处理所有的异步中间件
- 最后根据注册顺序执行异步路由
hyperlane 框架 >= v4.22.0 的版本
[!tip]
具体生命周期如下:
- 先根据注册顺序处理所有的异步请求中间件
- 再根据注册顺序执行异步路由
- 最后根据注册顺序处理所有的异步响应中间件
hyperlane 框架 >= v4.89.0 的版本
[!tip]
如果任一环节调用ctx.aborted().await或者ctx.set_aborted(true).await则会中止后续流程,
相关API(大部分场景用不到)
- 调用
ctx.cancel_aborted().await取消中止,继续后续流程- 调用
ctx.get_aborted().await来判断是否中止
hyperlane 框架 >= v5.25.1 的版本
[!tip]
如果任一环节调用ctx.closed().await或者ctx.set_closed(true).await则会停止后续响应的发送,
当前请求生命周期结束会断开TCP连接,不会进入下一次生命周期循环,而是需要重新建立TCP连接,
相关API(大部分场景用不到)
- 调用
ctx.cancel_closed().await取消中止,继续后续响应的发送- 调用
ctx.get_closed().await来判断是否关闭响应的发送
WebSocket 生命周期变化
多服务 (hyperlane/usage-introduction/multi-server.md)
[!tip]
hyperlane框架支持多服务模式,仅需创建多个server实例并进行监听即可
多服务
[!tip]
启动多个服务,监听多个端口
let app1 = spawn(async move {
    let server: Server = Server::new();
    server.host("0.0.0.0").await;
    server.port(80).await;
    server
        .route("/", |ctx: Context| async move {
            let _ = ctx.send_status_body(200, "hello world").await;
        })
        .await;
    let _ = server.listen().await;
});
let app2 = spawn(async move {
    let server: Server = Server::new();
    server.host("0.0.0.0").await;
    server.port(81).await;
    server
        .route("/", |ctx: Context| async move {
            let _ = ctx.send_status_body(200, "hello world").await;
        })
        .await;
    let _ = server.listen().await;
});
let _ = tokio::join!(app1, app2);
恐慌 (hyperlane/usage-introduction/panic.md)
[!tip]
hyperlane框架对于用户线程panic会进行捕获并写入错误日志
需注意对于一个请求如果在任一中间件环节触发panic当前请求的后续注册的路由处理函数将不会执行
代码示例
// 省略 server 创建
server.route("/", |_ctx| {
    panic!("test panic");
});
日志示例
2025-01-04 11:26:29: test panic
使用介绍 (hyperlane/usage-introduction/README.md)
请求 (hyperlane/usage-introduction/request.md)
[!tip]
hyperlane框架对ctx额外封装了子字段的方法,可以直接调用大部分子字段的get和set方法名称。
例如:调用request上的get_method方法,
一般需要从ctx解出request,再调用request.get_method(),
可以简化成直接调用ctx.get_request_method().await。调用规律
- 原
request的get方法的get名称后加request名称,中间使用_拼接。- 原
request的set方法的set名称后加request名称,中间使用_拼接。
获取请求信息
获取 request
let request: Request = ctx.get_request().await;
获取 method
let method: RequestMethod = ctx.get_request_method().await;
获取 host
let host: RequestHost = ctx.get_request_host().await;
获取 path
let path: RequestPath = ctx.get_request_path().await;
获取 querys
let querys: RequestQuerys = ctx.get_request_querys().await;
获取 header
[!tip]
hyperlane框架请求头的key是经过全小写处理,所以获取请求头时需要注意key使用全小写。
let header: OptionRequestHeadersValue = ctx.get_request_header("key").await;
获取 headers
let headers: RequestHeaders = ctx.get_request_headers().await;
获取请求体
let body: RequestBody = ctx.get_request_body().await;
获取 string 格式的请求体
let body: String = ctx.get_request_body_string().await;
获取 json 格式的请求体
let body: T = ctx.get_request_body_json::().await;
转字符串
通过 to_string
[!tip]
将获得完整的原始结构体字符串结构。
ctx.get_request().await.to_string();
通过 get_string
[!tip]
将获得简化的结构体字符串结构。
ctx.get_request().await.get_string();
响应 (hyperlane/usage-introduction/response.md)
[!tip]
hyperlane框架没有发送响应前通过ctx中get_response获取的只是响应的初始化实例,里面其实没有数据,
只有当用户发送响应时才会构建出完整http响应,此后再次get_response才能获取到响应内容。
[!tip]
hyperlane框架对ctx额外封装了子字段的方法,可以直接调用大部分子字段的get和set方法名称,
例如:调用response上的get_status_code方法。调用规律
- 原
response的get方法的get名称后加response名称,中间使用_拼接。- 原
response的set方法的set名称后加response名称,中间使用_拼接。
获取响应
获取 response
let response: Response = ctx.get_response().await;
获取响应版本
let version: ResponseVersion = ctx.get_response_version().await;
获取响应状态码
let status_code: ResponseStatusCode = ctx.get_response_status_code().await;
获取响应原因短语
let reason_phrase: ResponseReasonPhrase = ctx.get_response_reason_phrase().await;
获取完整响应头
let headers: ResponseHeaders = ctx.get_response_headers().await;
获取某个响应头
let value: ResponseHeadersValue = ctx.get_response_header("key").await;
获取请求体
let body: ResponseBody = ctx.get_response_body().await;
获取 string 格式的请求体
let body: String = ctx.get_response_body_string().await;
获取 json 格式的请求体
let body: T = ctx.get_response_body_json::().await;
设置响应
设置 response
ctx.set_response(Response::default()).await;
设置响应体
ctx.set_response_body(vec![]).await;
设置响应头
[!tip]
hyperlane框架对响应头的key是不做大小写处理的,这点与请求头的key处理方式不同。
ctx.set_response_header("server", "hyperlane").await;
设置状态码
ctx.set_response_status_code(200).await;
发送完整 HTTP 响应
[!tip]
如果你已经设置了响应信息,可以直接通过send或者send_once发送。此方法内部兼容了SSE和Websocket等协议,常用于响应中间件用于统一发送。
ctx.send
[!tip]
发送响应后TCP连接保留。
let send_res: ResponseResult = ctx.set_body("hello").send().await;
ctx.send_once
[!tip]
发送响应后TCP连接立即关闭。
let send_res: ResponseResult = ctx.set_body("hello").send_once().await;
仅发送响应体
[!tip]
支持多次主动发送响应。
response.send_body
[!tip]
发送响应体后TCP连接保留。
let send_res: ResponseResult = ctx.set_body("hello").send_body().await;
response.send_once_body
[!tip]
发送响应体后TCP连接立即关闭。
let send_res: ResponseResult = ctx.set_body("hello").send_once_body().await;
路由 (hyperlane/usage-introduction/route.md)
静态路由
[!tip]
hyperlane框架支持静态路由(如果重复注册相同的静态路由,框架会抛出异常,程序退出运行),使用方法如下:
注册
server.route("/test", |ctx: Context| {}).await;
动态路由
[!tip]
hyperlane框架支持动态路由(如果重复注册相同模式的动态路由,框架会抛出异常,程序退出运行),具体使用方法如下:
注册
[!tip]
动态路由使用{}包裹,有两种写法
{key}内直接些字符串,则将匹配的value存入key对应的value中。
{key:regex}则将正则表达式匹配的value存入key对应的value中,如果路径的最后是正则动态路由,则匹配后续所有路径,例如/test/{file:^.*$}匹配/test/a/b/c/d会成功,file的value为a/b/c/d。如果路径的最后不是正则动态路由,则仅使用正则匹配当前段的路由,例如/test/{file:^.*$}/b匹配/test/a/b会成功,file的value为a。
朴素动态路由
server.route("/test/{text}", |ctx: Context| {}).await;
正则表达式动态路由
server.route("/test/{number:\\d+}", |ctx: Context| {}).await;
获取全部动态路由参数
ctx.get_route_params().await;
获取某个动态路由参数
ctx.get_route_param("text").await;
SSE (hyperlane/usage-introduction/sse.md)
[!tip]
hyperlane框架支持sse,服务端主动推送,下面是每隔1s完成一次推送,并在10次后关闭连接。
[!tip]
sse规范: 服务器使用"content-type: text/event-stream"表示响应是一个sse事件流。
接着使用"data"字段来发送事件数据,每个事件以"data:"开头,后面跟着事件的内容和一个空行。
客户端收到这样的响应后,就可以解析其中的事件数据并进行相应的处理。
如果开发者非首次响应尝试调用send会正常发送响应,但是会包含整个http协议内容,所以对于sse,
非首次响应请统一使用send_body方法。
服务端代码
use crate::{tokio::time::sleep, *};
use std::time::Duration;
pub async fn root(ctx: Context) {
    let _ = ctx
        .set_response_header(CONTENT_TYPE, TEXT_EVENT_STREAM)
        .await
        .set_response_status_code(200)
        .await
        .send()
        .await;
    for i in 0..10 {
        let _ = ctx
            .set_response_body(format!("data:{}{}", i, HTTP_DOUBLE_BR))
            .await
            .send_body()
            .await;
        sleep(Duration::from_secs(1)).await;
    }
    let _ = ctx.closed().await;
}
客户端代码
客户端代码
断线重连
const eventSource = new EventSource('http://127.0.0.1:60000');
eventSource.onopen = function (event) {
  console.log('Connection opened.');
};
eventSource.onmessage = function (event) {
  const eventData = JSON.parse(event.data);
  console.log('Received event data:', eventData);
};
eventSource.onerror = function (event) {
  if (event.eventPhase === EventSource.CLOSED) {
    console.log('Connection was closed.');
  } else {
    console.error('Error occurred:', event);
  }
};
取消断线重连
const eventSource = new EventSource('http://127.0.0.1:60000');
eventSource.onopen = function (event) {
  console.log('Connection opened.');
};
eventSource.onmessage = function (event) {
  const eventData = JSON.parse(event.data);
  console.log('Received event data:', eventData);
};
eventSource.onerror = function (event) {
  if (event.eventPhase === EventSource.CLOSED) {
    console.log('Connection was closed.');
    // 关闭连接,防止自动重连
    eventSource.close();
  } else {
    console.error('Error occurred:', event);
  }
};
流 (hyperlane/usage-introduction/stream.md)
[!tip]
hyperlane框架接收请求和发送响应均依赖stream,类型是ArcRwLockStream需要注意框架提供的stream仅可读,使用方式如下:
获取 stream
let stream_lock: ArcRwLockStream = ctx.get_stream().await.clone().unwrap();
获取客户端地址
[!tip]
完整接口参阅官方文档,此处只介绍通过
stream解析使用。
let socket_addr: String = ctx
    .get_stream()
    .await
    .unwrap()
    .read()
    .await
    .peer_addr()
    .and_then(|host| Ok(host.to_string()))
    .unwrap_or("Unknown".to_owned());
关闭连接
[!tip]
此方法会关闭TCP连接,不会终止当前的生命周期(当前声明周期结束不会进入下一次生命周期循环,需要重新建立TCP连接),当前声明周期内的代码正常执行,但是不会再发送响应。
ctx.closed().await;
WebSocket (hyperlane/usage-introduction/websocket.md)
[!tip]
hyperlane框架支持websocket协议,服务端自动处理协议升级,支持请求中间件,路由处理,响应中间件。
服务端代码
[!tip]
hyperlane框架发送websocket响应使用send_body,与sse相同。
由于websocket协议基于http,所以可以像使用http一样处理请求。
如果开发者尝试调用send会返回错误,
(因为服务端发送响应前需要处理成符合websocket规范的响应,客户端才能正确解析)。所以对于websocket,
请统一使用send_body方法。
单点发送
pub async fn handle(ctx: Context) {
    let request_body: Vec= ctx.get_request_body().await;
    let _ = ctx.set_response_body(request_body).await.send_body().await;
}
广播发送
[!tip]
需要阻塞住当前处理函数,将后续所有请求在处理函数中处理。
这里使用tokio的select来处理多个请求,使用hyperlane-broadcast来实现广播。
需要特别注意,如果server没有配置disable_ws_handler,群发消息必须要求客户端连接后主动向服务端发送一条消息(空消息即可),否则不会接收到广播的信息,
因为服务端在框架内部会先完成握手,然后等待读取一次客户端请求,才会执行到用户代码。
如果配置了则连接后即可接收到广播的信息。
[!tip]
完整代码参考
GroupChat。
客户端代码
const ws = new WebSocket('ws://localhost:60000/websocket');
ws.onopen = () => {
  console.log('WebSocket opened');
  setInterval(() => {
    ws.send(`Now time: ${new Date().toISOString()}`);
  }, 1000);
};
ws.onmessage = (event) => {
  console.log('Receive: ', event.data);
};
ws.onerror = (error) => {
  console.error('WebSocket error: ', error);
};
ws.onclose = () => {
  console.log('WebSocket closed');
};
框架内置工具 (hyperlane/utils/inner-utils.md)
http-constant
[!tip]
hyperlane框架使用了http-constant库(框架已内置,无需额外安装和导入),
使用参考 官方文档。
http-compress
[!tip]
hyperlane框架使用了http-compress库(框架已内置,无需额外安装和导入),
使用参考 官方文档。
http-type
[!tip]
hyperlane框架使用了http-type库(框架已内置,无需额外安装和导入),
使用参考 官方文档。
工具使用 (hyperlane/utils/README.md)
框架推荐工具 (hyperlane/utils/recommend-utils.md)
hyperlane-utils
[!tip]
hyperlane框架推荐使用hyperlane-utils库(需额外安装和导入),
使用参考 官方文档。
lombok
[!tip]
hyperlane框架推荐使用lombok库(需额外安装和导入),
使用参考 官方文档。
clonelicious
[!tip]
hyperlane框架推荐使用clonelicious库,内部提供变量捕获和克隆(需额外安装和导入),
使用参考 官方文档。
future-fn
[!tip]
hyperlane框架推荐使用future-fn库(需额外安装和导入),
使用参考 官方文档。
std-macro-extensions
[!tip]
hyperlane框架推荐使用std-macro-extensions库(需额外安装和导入),
使用参考 官方文档。
color-output
[!tip]
hyperlane框架推荐使用color-output库(需额外安装和导入),
使用参考 官方文档。
bin-encode-decode
[!tip]
hyperlane框架推荐使用bin-encode-decode库(需额外安装和导入),
使用参考 官方文档。
file-operation
[!tip]
hyperlane框架推荐使用file-operation库(需额外安装和导入),
使用参考 官方文档。
compare-version
[!tip]
hyperlane框架推荐使用compare-version库(需额外安装和导入),
使用参考 官方文档。
hyperlane-log
[!tip]
hyperlane框架使用hyperlane-log库(需额外安装和导入),
使用参考 官方文档。
hyperlane-time
[!tip]
hyperlane框架推荐使用hyperlane-time库(需额外安装和导入),
使用参考 官方文档。
recoverable-spawn
[!tip]
hyperlane框架推荐使用recoverable-spawn库(需额外安装和导入),
使用参考 官方文档。
recoverable-thread-pool
[!tip]
hyperlane框架推荐使用recoverable-thread-pool库(需额外安装和导入),
使用参考 官方文档。
http-request
[!tip]
hyperlane框架推荐使用http-request库,支持http和https(需额外安装和导入),
使用参考 官方文档。
hyperlane-broadcast
[!tip]
hyperlane框架推荐使用hyperlane-broadcast库(需额外安装和导入),
使用参考 官方文档。
hyperlane-plugin-websocket
[!tip]
hyperlane框架推荐使用hyperlane-plugin-websocket库(需额外安装和导入),
使用参考 官方文档。
urlencoding
[!tip]
hyperlane框架推荐使用urlencoding库(需额外安装和导入),可以实现url编解码。
server-manager
[!tip]
hyperlane框架推荐使用server-manager库(需额外安装和导入),
使用参考 官方文档。
chunkify
[!tip]
hyperlane框架推荐使用chunkify库(需额外安装和导入),
使用参考 官方文档。
china_identification_card
[!tip]
hyperlane框架推荐使用china_identification_card库(需额外安装和导入),
使用参考 官方文档。
utoipa
[!tip]
hyperlane框架推荐使用utoipa库实现openapi,下面是一段简单的示例代码
use hyperlane::*;
use serde::Serialize;
use serde_json;
use utoipa::{OpenApi, ToSchema};
use utoipa_rapidoc::RapiDoc;
use utoipa_swagger_ui::SwaggerUi;
#[derive(Serialize, ToSchema)]
struct User {
    name: String,
    age: usize,
}
#[derive(OpenApi)]
#[openapi(
    components(schemas(User)),
    info(title = "Hyperlane", version = "1.0.0"),
    paths(index, user, openapi_json, swagger)
)]
struct ApiDoc;
async fn request_middleware(ctx: Context) {
    ctx.set_response_status_code(200).await;
}
#[utoipa::path(
    get,
    path = "/openapi.json",
    responses(
        (status = 200, description = "Openapi docs", body = String)
    )
)]
async fn openapi_json(ctx: Context) {
    ctx.set_response_body(ApiDoc::openapi().to_json().unwrap())
        .await
        .send()
        .await
        .unwrap();
}
#[utoipa::path(
    get,
    path = "/{file}",
    responses(
        (status = 200, description = "Openapi json", body = String)
    )
)]
async fn swagger(ctx: Context) {
    SwaggerUi::new("/{file}").url("/openapi.json", ApiDoc::openapi());
    let res: String = RapiDoc::with_openapi("/openapi.json", ApiDoc::openapi()).to_html();
    ctx.set_response_header(CONTENT_TYPE, TEXT_HTML)
        .await
        .set_response_body(res)
        .await
        .send()
        .await
        .unwrap();
}
#[utoipa::path(
    get,
    path = "/",
    responses(
        (status = 302, description = "Redirect to index.html")
    )
)]
async fn index(ctx: Context) {
    ctx.set_response_header(LOCATION, "/index.html")
        .await
        .set_response_body(vec![])
        .await
        .send()
        .await
        .unwrap();
}
#[utoipa::path(
    get,
    path = "/user/{name}",
    responses(
        (status = 200, description = "User", body = User)
    )
)]
async fn user(ctx: Context) {
    let name: String = ctx.get_route_param("name").await.unwrap();
    let user: User = User { name, age: 0 };
    ctx.set_response_body(serde_json::to_vec(&user).unwrap())
        .await
        .send()
        .await
        .unwrap();
}
#[tokio::main]
async fn main() {
    let server: Server = Server::new();
    server.request_middleware(request_middleware).await;
    server.route("/", index).await;
    server.route("/user/{name}", user).await;
    server.route("/openapi.json", openapi_json).await;
    server.route("/{file}", swagger).await;
    server.run().await.unwrap();
}
lombok 属性宏 (lombok-macros/README.md)
一组提供 Lombok 类似功能的 Rust 宏。
安装
要使用此 crate,可以运行以下命令:
cargo add lombok-macros
使用
DisplayDebug
代码
use lombok_macros::*;
use std::fmt::Debug;
#[derive(Data, Debug, Clone, DisplayDebug)]
struct LombokTest{
    #[get(pub(crate))]
    #[set(pub(crate))]
    list: Vec,
    #[get(pub(crate))]
    opt_str_lifetime_a: Option,
    #[set(private)]
    #[get_mut(pub(crate))]
    opt_str_lifetime_b: Option,
}
fn main() {
    let mut data: LombokTest= LombokTest {
        list: Vec::new(),
        opt_str_lifetime_a: None,
        opt_str_lifetime_b: None,
    };
    let list: Vec= vec!["hello".to_string(), "world".to_string()];
    data.set_list(list.clone());
    match data.get_list() {
        left_val => {
            assert_eq!(*left_val, list);
        }
    }
    let get_opt_str_lifetime_a: Option= data.get_opt_str_lifetime_a().cloned();
    assert_eq!(get_opt_str_lifetime_a, None);
    let get_mut_opt_str_lifetime_b: &mut Option= data.get_mut_opt_str_lifetime_b();
    *get_mut_opt_str_lifetime_b = Some("opt_str_lifetime_b");
    assert_eq!(
        data.get_mut_opt_str_lifetime_b().clone(),
        Some("opt_str_lifetime_b")
    );
    println!("{}", data.to_string());
}
宏展开结果
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use lombok_macros::*;
use std::fmt::Debug;
struct LombokTest{
    #[get(pub(crate))]
    #[set(pub(crate))]
    list: Vec,
    #[get(pub(crate))]
    opt_str_lifetime_a: Option,
    #[set(private)]
    #[get_mut(pub(crate))]
    opt_str_lifetime_b: Option,
}
implLombokTest{
    pub(crate) fn get_list(&self) -> &Vec{
        &self.list
    }
    pub(crate) fn set_list(&mut self, val: Vec) -> &mut Self {
        self.list = val;
        self
    }
    pub fn get_mut_list(&mut self) -> &mut Vec{
        &mut self.list
    }
    pub(crate) fn get_opt_str_lifetime_a(&self) -> &Option{
        &self.opt_str_lifetime_a
    }
    pub fn get_mut_opt_str_lifetime_a(&mut self) -> &mut Option{
        &mut self.opt_str_lifetime_a
    }
    pub fn set_opt_str_lifetime_a(&mut self, val: Option) -> &mut Self {
        self.opt_str_lifetime_a = val;
        self
    }
    fn set_opt_str_lifetime_b(&mut self, val: Option) -> &mut Self {
        self.opt_str_lifetime_b = val;
        self
    }
    pub(crate) fn get_mut_opt_str_lifetime_b(&mut self) -> &mut Option{
        &mut self.opt_str_lifetime_b
    }
    pub fn get_opt_str_lifetime_b(&self) -> &Option{
        &self.opt_str_lifetime_b
    }
}
#[automatically_derived]
impl::core::fmt::Debug
for LombokTest{
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(
            f,
            "LombokTest",
            "list",
            &self.list,
            "opt_str_lifetime_a",
            &self.opt_str_lifetime_a,
            "opt_str_lifetime_b",
            &&self.opt_str_lifetime_b,
        )
    }
}
#[automatically_derived]
impl::core::clone::Clone
for LombokTest{
    fn clone(&self) -> LombokTest{
        LombokTest {
            list: ::core::clone::Clone::clone(&self.list),
            opt_str_lifetime_a: ::core::clone::Clone::clone(&self.opt_str_lifetime_a),
            opt_str_lifetime_b: ::core::clone::Clone::clone(&self.opt_str_lifetime_b),
        }
    }
}
implstd::fmt::Display for LombokTest{
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_fmt(format_args!("{0:?}", self))
    }
}
fn main() {
    let mut data: LombokTest= LombokTest {
        list: Vec::new(),
        opt_str_lifetime_a: None,
        opt_str_lifetime_b: None,
    };
    let list: Vec= ::into_vec(
        #[rustc_box]
        ::alloc::boxed::Box::new(["hello".to_string(), "world".to_string()]),
    );
    data.set_list(list.clone());
    match data.get_list() {
        left_val => {
            match (&*left_val, &list) {
                (left_val, right_val) => {
                    if !(*left_val == *right_val) {
                        let kind = ::core::panicking::AssertKind::Eq;
                        ::core::panicking::assert_failed(
                            kind,
                            &*left_val,
                            &*right_val,
                            ::core::option::Option::None,
                        );
                    }
                }
            };
        }
    }
    let get_opt_str_lifetime_a: Option= data.get_opt_str_lifetime_a().cloned();
    match (&get_opt_str_lifetime_a, &None) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(
                    kind,
                    &*left_val,
                    &*right_val,
                    ::core::option::Option::None,
                );
            }
        }
    };
    let get_mut_opt_str_lifetime_b: &mut Option= data
        .get_mut_opt_str_lifetime_b();
    *get_mut_opt_str_lifetime_b = Some("opt_str_lifetime_b");
    match (&data.get_mut_opt_str_lifetime_b().clone(), &Some("opt_str_lifetime_b")) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(
                    kind,
                    &*left_val,
                    &*right_val,
                    ::core::option::Option::None,
                );
            }
        }
    };
    {
        ::std::io::_print(format_args!("{0}\n", data.to_string()));
    };
}
DisplayDebugFormat
代码
use lombok_macros::*;
use std::fmt::Debug;
#[derive(Data, Debug, Clone, DisplayDebugFormat)]
struct LombokTest{
    #[get(pub(crate))]
    #[set(pub(crate))]
    list: Vec,
    #[get(pub(crate))]
    opt_str_lifetime_a: Option,
    #[set(private)]
    #[get_mut(pub(crate))]
    opt_str_lifetime_b: Option,
}
fn main() {
    let mut data: LombokTest= LombokTest {
        list: Vec::new(),
        opt_str_lifetime_a: None,
        opt_str_lifetime_b: None,
    };
    let list: Vec= vec!["hello".to_string(), "world".to_string()];
    data.set_list(list.clone());
    match data.get_list() {
        left_val => {
            assert_eq!(*left_val, list);
        }
    }
    let get_opt_str_lifetime_a: Option= data.get_opt_str_lifetime_a().cloned();
    assert_eq!(get_opt_str_lifetime_a, None);
    let get_mut_opt_str_lifetime_b: &mut Option= data.get_mut_opt_str_lifetime_b();
    *get_mut_opt_str_lifetime_b = Some("opt_str_lifetime_b");
    assert_eq!(
        data.get_mut_opt_str_lifetime_b().clone(),
        Some("opt_str_lifetime_b")
    );
    println!("{}", data.to_string());
}
宏展开结果
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use lombok_macros::*;
use std::fmt::Debug;
struct LombokTest{
    #[get(pub(crate))]
    #[set(pub(crate))]
    list: Vec,
    #[get(pub(crate))]
    opt_str_lifetime_a: Option,
    #[set(private)]
    #[get_mut(pub(crate))]
    opt_str_lifetime_b: Option,
}
implLombokTest{
    pub(crate) fn get_list(&self) -> &Vec{
        &self.list
    }
    pub(crate) fn set_list(&mut self, val: Vec) -> &mut Self {
        self.list = val;
        self
    }
    pub fn get_mut_list(&mut self) -> &mut Vec{
        &mut self.list
    }
    pub(crate) fn get_opt_str_lifetime_a(&self) -> &Option{
        &self.opt_str_lifetime_a
    }
    pub fn get_mut_opt_str_lifetime_a(&mut self) -> &mut Option{
        &mut self.opt_str_lifetime_a
    }
    pub fn set_opt_str_lifetime_a(&mut self, val: Option) -> &mut Self {
        self.opt_str_lifetime_a = val;
        self
    }
    fn set_opt_str_lifetime_b(&mut self, val: Option) -> &mut Self {
        self.opt_str_lifetime_b = val;
        self
    }
    pub(crate) fn get_mut_opt_str_lifetime_b(&mut self) -> &mut Option{
        &mut self.opt_str_lifetime_b
    }
    pub fn get_opt_str_lifetime_b(&self) -> &Option{
        &self.opt_str_lifetime_b
    }
}
#[automatically_derived]
impl::core::fmt::Debug
for LombokTest{
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(
            f,
            "LombokTest",
            "list",
            &self.list,
            "opt_str_lifetime_a",
            &self.opt_str_lifetime_a,
            "opt_str_lifetime_b",
            &&self.opt_str_lifetime_b,
        )
    }
}
#[automatically_derived]
impl::core::clone::Clone
for LombokTest{
    fn clone(&self) -> LombokTest{
        LombokTest {
            list: ::core::clone::Clone::clone(&self.list),
            opt_str_lifetime_a: ::core::clone::Clone::clone(&self.opt_str_lifetime_a),
            opt_str_lifetime_b: ::core::clone::Clone::clone(&self.opt_str_lifetime_b),
        }
    }
}
implstd::fmt::Display for LombokTest{
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_fmt(format_args!("{0:#?}", self))
    }
}
fn main() {
    let mut data: LombokTest= LombokTest {
        list: Vec::new(),
        opt_str_lifetime_a: None,
        opt_str_lifetime_b: None,
    };
    let list: Vec= ::into_vec(
        #[rustc_box]
        ::alloc::boxed::Box::new(["hello".to_string(), "world".to_string()]),
    );
    data.set_list(list.clone());
    match data.get_list() {
        left_val => {
            match (&*left_val, &list) {
                (left_val, right_val) => {
                    if !(*left_val == *right_val) {
                        let kind = ::core::panicking::AssertKind::Eq;
                        ::core::panicking::assert_failed(
                            kind,
                            &*left_val,
                            &*right_val,
                            ::core::option::Option::None,
                        );
                    }
                }
            };
        }
    }
    let get_opt_str_lifetime_a: Option= data.get_opt_str_lifetime_a().cloned();
    match (&get_opt_str_lifetime_a, &None) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(
                    kind,
                    &*left_val,
                    &*right_val,
                    ::core::option::Option::None,
                );
            }
        }
    };
    let get_mut_opt_str_lifetime_b: &mut Option= data
        .get_mut_opt_str_lifetime_b();
    *get_mut_opt_str_lifetime_b = Some("opt_str_lifetime_b");
    match (&data.get_mut_opt_str_lifetime_b().clone(), &Some("opt_str_lifetime_b")) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(
                    kind,
                    &*left_val,
                    &*right_val,
                    ::core::option::Option::None,
                );
            }
        }
    };
    {
        ::std::io::_print(format_args!("{0}\n", data.to_string()));
    };
}
可恢复线程 (recoverable-spawn/README.md)
一个库,用于在 panic 后自动重启线程。它在发生意外错误时,确保线程继续运行。它提供了一种简单有效的机制,捕获 panic,重启线程,并可以选择性地记录错误,用于监控和调试。
安装
要使用此库,可以运行以下命令:
cargo add recoverable-spawn
使用
recoverable_spawn
use recoverable_spawn::*;
let msg: &str = "test";
let res: SyncSpawnResult = recoverable_spawn(move || {
    panic!("{}", msg);
});
let res: SyncSpawnResult = recoverable_spawn_with_error_handle(
    move || {
        panic!("{}", msg);
    },
    |err| {
        println!("handle error => {}", err);
    },
);
recoverable_spawn_with_error_handle
use recoverable_spawn::*;
let msg: &str = "test";
let res: SyncSpawnResult = recoverable_spawn_with_error_handle(
    move || {
        panic!("{}", msg);
    },
    |err| {
        println!("handle error => {}", err);
    },
);
async_recoverable_spawn
use recoverable_spawn::*;
let msg: &str = "test";
let res: AsyncSpawnResult = async_recoverable_spawn(move || async move {
    panic!("{}", msg);
});
async_recoverable_spawn_catch
use recoverable_spawn::*;
let msg: &str = "test";
let res: AsyncSpawnResult = async_recoverable_spawn_catch(
    move || async move {
        panic!("{}", msg);
    },
    move |err| async move {
        println!("handle error => {}", err);
    },
);
async_recoverable_spawn_catch_finally
use recoverable_spawn::*;
let msg: &str = "test";
let res: AsyncSpawnResult = async_recoverable_spawn_catch_finally(
    move || async move {
        panic!("{}", msg);
    },
    move |err| async move {
        println!("handle error => {}", err);
        panic!("{}", err);
    },
    move || async move {
        println!("finally");
    },
);
可恢复线程池 (recoverable-thread-pool/README.md)
一个支持自动从恐慌中恢复的线程池,允许线程在发生恐慌后重新启动。适用于网络和 Web 编程中的高容错并发场景。
安装
要使用此 crate,可以运行以下命令:
cargo add recoverable-thread-pool
使用示例
同步
use recoverable_thread_pool::*;
use std::{thread::sleep, time::Duration};
let thread_pool: ThreadPool = ThreadPool::new(1);
let first_res: SendResult = thread_pool.execute(|| {
    println!("first");
});
println!("{:?}", first_res);
let panic_res: SendResult = thread_pool.execute_with_catch(
    || {
        panic!("[panic]");
    },
    |err| {
        println!("Catch panic {}", err);
    },
);
println!("{:?}", panic_res);
let second_res: SendResult = thread_pool.execute_with_catch_finally(
    || {
        panic!("[panic]");
    },
    |_err| {
        panic!("[panic]");
    },
    || {
        println!("finally");
    },
);
println!("{:?}", second_res);
sleep(Duration::from_secs(10));
异步
use recoverable_thread_pool::*;
use std::{thread::sleep, time::Duration};
let thread_pool: ThreadPool = ThreadPool::new(1);
let first_res: SendResult = thread_pool.async_execute(|| async {
    println!("first");
});
println!("{:?}", first_res);
let panic_res: SendResult = thread_pool.async_execute_with_catch(
    || async {
        panic!("[panic]");
    },
    |err| async move {
        println!("Catch panic {}", err);
    },
);
println!("{:?}", panic_res);
let second_res: SendResult = thread_pool.async_execute_with_catch_finally(
    || async {
        panic!("[panic]");
    },
    |_err| async {
        panic!("[panic]");
    },
    || async {
        println!("finally");
    },
);
println!("{:?}", second_res);
sleep(Duration::from_secs(10));
server-manager (server-manager/README.md)
server-manager 是一个用于管理服务器进程的 rust 库。它封装了服务的启动、停止以及后台守护(daemon)模式,用户可以通过自定义配置来指定 PID 文件、日志文件等路径,同时可以将自己的异步服务器函数传入进行调用。该库支持同步和异步操作,在 Unix 和 Windows 平台下可以使用后台守护进程功能。
安装
在项目目录下执行下面的命令,将 server-manager 添加为依赖项:
cargo add server-manager
使用
use server_manager::*;
use std::fs;
use std::time::Duration;
let pid_file: String = "./process/test_pid.pid".to_string();
let _ = fs::remove_file(&pid_file);
let config: ServerManagerConfig = ServerManagerConfig {
    pid_file: pid_file.clone(),
};
let dummy_server = || async {
    tokio::time::sleep(Duration::from_secs(1)).await;
};
let manager = ServerManager::new(config, dummy_server);
let res: Result> = manager.start_daemon();
println!("start_daemon {:?}", res);
let res: Result> = manager.stop();
println!("stop {:?}", res);
manager.start().await;
let _ = fs::remove_file(&pid_file);
let res: ServerManagerResult =
    manager.hot_restart(&["--once", "-x", "check", "-x", "build --release"]);
println!("hot_restart {:?}", res);
标准库宏扩展 (std-macro-extensions/README.md)
说明
[!tip]
这是一个为 Rust 标准库数据结构提供的宏扩展集合,简化了常见集合(如HashMap、Vec等)的创建和操作。
特性
- 简化初始化:使用宏轻松创建常见数据结构的实例。
- 支持多种数据结构:包括 Vec、HashMap、Arc等的宏。
- 易于使用:直观的语法使数据结构的创建更加迅速。
安装
要安装 std-macro-extensions,请运行以下命令:
cargo add std-macro-extensions
用法
以下是如何使用该 crate 提供的宏的一些示例:
示例:使用 arc!
use std_macro_extensions::*;
fn main() {
    let value = arc!(5);
}
示例:使用 b_tree_map!
use std_macro_extensions::*;
fn main() {
    let empty_map: BTreeMap= b_tree_map!();
    let b_tree_map_a: BTreeMap= b_tree_map!(
        "a" => "a",
        "b" => "b"
    );
}
示例:使用 b_tree_map!
use std_macro_extensions::*;
fn main() {
    let empty_set: BTreeSet= b_tree_set!();
    let number_set: BTreeSet= b_tree_set!(1, 2, 3);
}
示例:使用 boxed!
use std_macro_extensions::*;
fn main() {
    let boxed_value = boxed!(10);
}
示例:使用 cell!
use std_macro_extensions::*;
fn main() {
    let cell_value: Cell= cell!(5);
}
示例:使用 hash_map!
use std_macro_extensions::*;
fn main() {
    let my_map: HashMap= hash_map!();
    let my_map: HashMap= hash_map!("a" => 1, "b" => 2);
}
示例:使用 hash_set!
use std_macro_extensions::*;
fn main() {
    let my_set: HashSet= hash_set!();
    let my_set: HashSet= hash_set!(1, 2, 3);
}
示例:使用 linked_list!
use std_macro_extensions::*;
fn main() {
    let my_list: LinkedList= linked_list!();
    let my_list: LinkedList= linked_list!(1, 2, 3);
}
示例:使用 mutex!
use std_macro_extensions::*;
fn main() {
    let my_mutex: Mutex= mutex!(5);
    let lock: MutexGuard= my_mutex.lock().unwrap();
}
示例:使用 rc!
use std_macro_extensions::*;
fn main() {
    let my_rc = rc!(5);
}
示例:使用 refcell!
use std_macro_extensions::*;
fn main() {
    let my_refcell = refcell!(5);
}
示例:使用 rw_lock!
use std_macro_extensions::*;
fn main() {
    let my_rwlock = rw_lock!(5);
}
示例:使用 string!
use std_macro_extensions::*;
fn main() {
    let empty_string = string!();
    let hello_string = string!("Hello");
}
示例:使用 vector!
use std_macro_extensions::*;
fn main() {
    let empty_vector: Vec= vector!();
    let numbers = vector!(1, 2, 3);
}
示例:使用 vector_deque!
use std_macro_extensions::*;
fn main() {
    let empty_deque: VecDeque= vector_deque!();
    let numbers = vector_deque!(1, 2, 3);
}
示例:使用 join_paths!
let combined_path: String = join_paths!("/home/", "/user/", "/documents", "file.txt");
let another_path: String = join_paths!("C:/", "/Program Files", "App");
示例:使用 cin!
let input: String = cin!();
println!("You typed: {}", input);
示例:使用 cin_parse!
let input: &str = "1 2 3";
let numbers: Vec= cin_parse!(input, Vec);
assert_eq!(numbers, vec![1, 2, 3]);
let single_input: &str = "12";
let number: i32 = cin_parse!(single_input, i32);
assert_eq!(number, 12);
示例:使用 cout!
let name: &str = "Alice";
let age: i32 = 30;
cout!("Name: {}, Age: {}\n", name, age);
示例:使用 endl!
endl!();
示例:使用 cout_endl!
let name: &str = "Alice";
let age: i32 = 30;
cout_endl!("Name: {}, Age: {}\n", name, age);
示例:使用 execute!
fn sum(data: &[i32]) -> i32 {
    data.iter().sum()
}
fn add_offset(data: &[i32], offset: i32) -> i32 {
    data.iter().map(|x| x + offset).sum()
}
let nums: Vec= vec![1, 2, 3];
let total: i32 = execute!(sum, &nums);
assert_eq!(total, 6);
let total_with_offset: i32 = execute!(add_offset, &nums, 10);
assert_eq!(total_with_offset, 36);
示例:使用 execute_async!
let data: Vec= vec![1, 2, 3];
async fn async_func(data: &[i32], offset: i32) -> i32 {
    data.iter().map(|x| x + offset).sum()
}
let res: i32 = execute_async!(async_func, &data, 1).await;
assert_eq!(res, 9);
可用的宏
- arc!:创建一个- Arc。
- vector!:创建一个- Vec。
- map!:创建一个- HashMap。
- set!:创建一个- HashSet。
- b_tree_map!:创建一个- BTreeMap。
- b_tree_set!:创建一个- BTreeSet。
- list!:创建一个- LinkedList。
- heap!:创建一个- BinaryHeap。
- string!:创建一个- String。
- boxed!:创建一个- Box。
- rc!:创建一个- Rc。
- arc!:创建一个- Arc。
- mutex!:创建一个- Mutex。
- rw_lock!:创建一个- RwLock。
- cell!:创建一个- Cell。
- ref_cell!:创建一个- RefCell。
- vector_deque!: Creates a- VecDeque。
- join_paths!: 将多个路径组合成一个有效的路径,并处理重叠的斜杠。
- cin!: 从标准输入读取一行字符串输入。
- cin_parse!: 将输入解析为指定的类型或类型数组。
- cout!: 将格式化输出打印到标准输出(不换行)。
- endl!: 打印一个换行符到标准输出。
- cout_endl!: 打印格式化输出并追加一个换行符到标准输出,同时刷新缓冲区。
- execute!: 使用提供的参数调用并执行指定函数。
- execute_async!: 使用提供的参数调用并异步执行指定函数。
TCP 请求库 (tcp-request/README.md)
一个 Rust 库,用于发送原始 TCP 请求,支持处理响应、管理重定向以及在 TCP 连接中处理压缩数据。
安装
通过以下命令安装该 crate:
cargo add tcp-request
使用方法
接收文本数据
use tcp_request::*;
let mut request_builder = RequestBuilder::new()
    .host("127.0.0.1")
    .port(80)
    .build();
request_builder
    .send("tcp send".as_bytes())
    .and_then(|response| {
        println!("ResponseTrait => {:?}", response.text());
        Ok(())
    })
    .unwrap_or_else(|e| println!("Error => {:?}", e));
接收二进制数据
use tcp_request::*;
let mut request_builder = RequestBuilder::new()
    .host("127.0.0.1")
    .port(80)
    .build();
request_builder
    .send("tcp send".as_bytes())
    .and_then(|response| {
        println!("ResponseTrait => {:?}", response.binary());
        Ok(())
    })
    .unwrap_or_else(|e| println!("Error => {:?}", e));
注意事项
确保系统中已安装 CMake。
TCP 后端框架 (tcplane/README.md)
tcplane 是一个轻量级且高性能的 Rust TCP 服务器库,旨在简化网络服务开发。它支持 TCP 通信、数据流管理和连接处理,专注于提供高效的底层网络连接和数据传输能力,非常适合构建现代网络服务。
安装
可以通过以下命令安装该库:
cargo add tcplane
使用示例
use tcplane::*;
async fn test_func(ctx: Context) {
    ctx.send("tcplane: 1").await.unwrap();
}
fn error_handle(error: String) {
    eprintln!("{}", error);
    let _ = std::io::Write::flush(&mut std::io::stderr());
}
#[tokio::main]
async fn main() {
    let mut server: Server = Server::new();
    server.host("0.0.0.0").await;
    server.port(60000).await;
    server.buffer(100_024_000).await;
    server.error_handle(error_handle).await;
    server.func(test_func).await;
    server
        .func(|ctx: Context| async move {
            ctx.send("tcplane: 2").await.unwrap();
        })
        .await;
    server.run().await;
}
UDP 请求库 (udp-request/README.md)
一个简单的 UDP 请求库,用于在 Rust 应用程序中发送和接收 UDP 数据包,设计用于处理网络通信。
安装
要使用这个库,你可以运行以下命令:
cargo add udp-request
使用
接收文本
use udp_request::*;
let mut request_builder = RequestBuilder::new()
    .host("127.0.0.1")
    .port(80)
    .build();
request_builder
    .send("udp send".as_bytes())
    .and_then(|response| {
        println!("ResponseTrait => {:?}", response.text());
        Ok(())
    })
    .unwrap_or_else(|e| println!("Error => {:?}", e));
接收二进制
use udp_request::*;
let mut request_builder = RequestBuilder::new()
    .host("127.0.0.1")
    .port(80)
    .build();
request_builder
    .send("udp send".as_bytes())
    .and_then(|response| {
        println!("ResponseTrait => {:?}", response.binary());
        Ok(())
    })
    .unwrap_or_else(|e| println!("Error => {:?}", e));
UDP 后端框架 (udp/README.md)
一个轻量高效的 Rust 库,用于构建支持请求-响应处理的 UDP 服务器
安装
要使用此 crate,你可以运行以下命令:
cargo add udp
使用方法
use udp::*;
async fn test_func(ctx: Context) {
    ctx.send("udp: 1").await.unwrap();
}
fn error_handle(error: String) {
    eprintln!("{}", error);
    let _ = std::io::Write::flush(&mut std::io::stderr());
}
#[tokio::main]
async fn main() {
    let mut server: Server = Server::new();
    server.host("0.0.0.0").await;
    server.port(60000).await;
    server.buffer(100_024_000).await;
    server.error_handle(error_handle).await;
    server.func(test_func).await;
    server
        .func(|ctx: Context| async move {
            ctx.send("udp: 2").await.unwrap();
        })
        .await;
    server.run().await;
}
 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号