二进制加解密库 (bin-encode-decode/README.md)
GITHUB 地址
高性能二进制编码和解码库:支持超越 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)
GITHUB 地址
说明
[!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)
GITHUB 地址
API 文档
一个简单高效的 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)
GITHUB 地址
API 文档
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)
GITHUB 地址
基于 Rust hyperlane 框架的云文件存储服务器,支持多种文件类型的上传。
使用现有地址(服务器不在大陆且经过多个服务器中转,接口会比较慢)
接口地址:https://file.ltpp.vip
本地部署
克隆
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)
GITHUB 地址
说明
[!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)
GITHUB 地址
说明
[!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)
GITHUB 地址
文件操作
API 文档
一个 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)
GITHUB 地址
API 文档
一个 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)
GITHUB 地址
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)
GITHUB 地址
API 文档
一个热重启的 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)
GITHUB 地址
API 文档
一个支持 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)
GITHUB 地址
API 文档
一个提供常见 HTTP 常量的全面库,包括头部名称、版本、MIME 类型和协议标识符。
安装
要使用此 crate,可以运行以下命令:
cargo add http-constant
使用示例
use http_constant::*;
HTTP 请求库 (http-request/README.md)
GITHUB 地址
API 文档
http-request 是一个轻量级、高效的库,用于在 Rust 应用程序中构建、发送和处理 HTTP/HTTPS 请求。它提供了简单直观的 API,允许开发者轻松与 Web 服务交互,无论使用的是 "HTTP" 还是 "HTTPS" 协议。该库支持各种 HTTP 方法、自定义头部、请求体、超时、自动处理重定向(包括检测重定向循环)以及增强的响应体解码(自动和手动),实现快速安全的通信。无论是处理安全的 "HTTPS" 连接还是标准的 "HTTP" 请求,该库都针对性能、最小资源使用和易于集成到 Rust 项目进行了优化。
特性
- 支持 HTTP/HTTPS:支持 HTTP 和 HTTPS 协议。
- WebSocket 支持:完整的 WebSocket 支持,提供同步和异步 API 用于实时通信。
- 轻量级设计:
http_request
crate 提供简单高效的 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)
GITHUB 地址
API 文档
一个提供 HTTP 基本类型的库,包括请求体、响应头和其他核心 HTTP 抽象。
安装
要使用此 crate,可以运行以下命令:
cargo add http-type
使用示例
use http_type::*;
Hyperlane 广播库 (hyperlane-broadcast/README.md)
GITHUB 地址
API 文档
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)
GITHUB 地址
API 文档
一个支持异步和同步日志的 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)
GITHUB 地址
API 文档
安装
要使用这个 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)
GITHUB 地址
API 文档
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)
GITHUB 地址
API 文档
一个根据系统的本地设置获取当前时间的库。
安装
要使用这个库,你可以运行以下命令:
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)
GITHUB 地址
API 文档
一个为 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)
GITHUB 地址
[!tip]
基于
hyperlane
框架,使用chunkify
(官方文档) 开发的的文件分块项目
(点击此处在线体验)。
克隆项目
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)
GITHUB 地址
[!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)
GITHUB 地址
[!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)
GITHUB 地址
[!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)
GITHUB 地址
API 文档
Hyperlane 是一个轻量级且高性能的 Rust HTTP 服务器库,旨在简化网络服务开发。它支持 HTTP 请求解析、响应构建和 TCP 通信,非常适合构建现代 Web 服务。此外,它还支持请求和响应中间件、WebSocket 和 Server-Sent Events (SSE),从而实现灵活高效的实时通信。Hyperlane 使用纯 Rust 和标准库构建,提供跨 Windows、Linux 和 macOS 的真正跨平台兼容性,且所有平台上的 API 体验一致,依托 Tokio 的异步运行时实现无缝网络通信,无需特定于平台的依赖。
安装
要使用此 crate,可以运行以下命令:
cargo add hyperlane
快速开始
- hyperlane-quick-start git
- hyperlane-quick-start docs
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_back(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)
GITHUB 地址
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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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 readSocket 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: 60000Document Path: /
Document Length: 5 bytesConcurrency 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] receivedConnection 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 1085Percentage of the requests served within a certain time (ms)50% 1966% 2275% 2480% 2590% 2995% 3398% 3799% 41100% 1085 (longest request)
Rust 标准库
Server Hostname: 127.0.0.1
Server Port: 60000Document Path: /
Document Length: 5 bytesConcurrency 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] receivedConnection 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 7441Percentage of the requests served within a certain time (ms)50% 466% 575% 580% 690% 795% 898% 42699% 1050100% 7441 (longest request)
Tokio 框架
Server Hostname: 127.0.0.1
Server Port: 60000Document Path: /
Document Length: 5 bytesConcurrency 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] receivedConnection 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 1107Percentage of the requests served within a certain time (ms)50% 1866% 2175% 2380% 2590% 2995% 3398% 3899% 42100% 1107 (longest request)
Rocket 框架
Server Software: Rocket
Server Hostname: 127.0.0.1
Server Port: 60000Document Path: /
Document Length: 13 bytesConcurrency 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] receivedConnection 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 1107Percentage of the requests served within a certain time (ms)50% 1966% 2275% 2580% 2690% 3095% 3498% 3999% 43100% 1107 (longest request)
Gin 框架
Server Hostname: 127.0.0.1
Server Port: 60000Document Path: /
Document Length: 5 bytesConcurrency 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] receivedConnection 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 1114Percentage of the requests served within a certain time (ms)50% 2066% 2375% 2680% 2790% 3295% 3598% 4099% 44100% 1114 (longest request)
Go 标准库
Server Hostname: 127.0.0.1
Server Port: 60000Document Path: /
Document Length: 13 bytesConcurrency 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] receivedConnection 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 1120Percentage of the requests served within a certain time (ms)50% 1966% 2375% 2580% 2790% 3195% 3598% 4199% 46100% 1120 (longest request)
Node 标准库
Server Hostname: 127.0.0.1
Server Port: 60000Document Path: /
Document Length: 13 bytesConcurrency 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] receivedConnection 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 1108Percentage of the requests served within a certain time (ms)50% 2066% 2275% 2380% 2490% 2795% 2998% 3399% 37100% 1108 (longest request)
环境信息 (hyperlane/speed/env.md)
GITHUB 地址
环境信息
- 系统:
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)
GITHUB 地址
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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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 connectionsThread Stats Avg Stdev Max +/- StdevLatency 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: 60000Document Path: /
Document Length: 5 bytesConcurrency 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] receivedConnection 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 16Percentage of the requests served within a certain time (ms)50% 366% 475% 480% 490% 595% 698% 799% 7100% 16 (longest request)
Rust 标准库
Server Hostname: 127.0.0.1
Server Port: 60000Document Path: /
Document Length: 5 bytesConcurrency 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] receivedConnection 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 1286Percentage of the requests served within a certain time (ms)50% 366% 475% 480% 490% 595% 698% 899% 8100% 1286 (longest request)
Tokio 框架
Server Hostname: 127.0.0.1
Server Port: 60000Document Path: /
Document Length: 5 bytesConcurrency 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] receivedConnection 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 16Percentage of the requests served within a certain time (ms)50% 366% 475% 480% 490% 595% 698% 799% 7100% 16 (longest request)
Rocket 框架
Server Software: Rocket
Server Hostname: 127.0.0.1
Server Port: 60000Document Path: /
Document Length: 13 bytesConcurrency 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] receivedConnection 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 21Percentage of the requests served within a certain time (ms)50% 466% 475% 580% 590% 695% 698% 799% 8100% 21 (longest request)
Gin 框架
Server Hostname: 127.0.0.1
Server Port: 60000Document Path: /
Document Length: 5 bytesConcurrency 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] receivedConnection 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 184Percentage of the requests served within a certain time (ms)50% 466% 575% 580% 690% 895% 1098% 1299% 13100% 184 (longest request)
Go 标准库
Server Hostname: 127.0.0.1
Server Port: 60000Document Path: /
Document Length: 13 bytesConcurrency 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] receivedConnection 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 176Percentage of the requests served within a certain time (ms)50% 466% 475% 580% 590% 795% 898% 899% 9100% 176 (longest request)
Node 标准库
Server Hostname: 127.0.0.1
Server Port: 60000Document Path: /
Document Length: 13 bytesConcurrency 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] receivedConnection 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 1102Percentage of the requests served within a certain time (ms)50% 966% 1575% 1780% 1890% 2195% 2398% 2799% 30100% 1102 (longest request)
性能测试 (hyperlane/speed/README.md)
响应时间测试 (hyperlane/speed/request-time.md)
GITHUB 地址
[!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 ContinueContinue,/// 101 Switching ProtocolsSwitchingProtocols,/// 102 Processing (WebDAV)Processing,/// 103 Early HintsEarlyHints,/// 200 OKOk,/// 201 CreatedCreated,/// 202 AcceptedAccepted,/// 203 Non-Authoritative InformationNonAuthoritativeInformation,/// 204 No ContentNoContent,/// 205 Reset ContentResetContent,/// 206 Partial ContentPartialContent,/// 207 Multi-Status (WebDAV)MultiStatus,/// 208 Already Reported (WebDAV)AlreadyReported,/// 226 IM UsedIMUsed,/// 300 Multiple ChoicesMultipleChoices,/// 301 Moved PermanentlyMovedPermanently,/// 302 FoundFound,/// 303 See OtherSeeOther,/// 304 Not ModifiedNotModified,/// 305 Use ProxyUseProxy,/// 307 Temporary RedirectTemporaryRedirect,/// 308 Permanent RedirectPermanentRedirect,/// 400 Bad RequestBadRequest,/// 401 UnauthorizedUnauthorized,/// 402 Payment RequiredPaymentRequired,/// 403 ForbiddenForbidden,/// 404 Not FoundNotFound,/// 405 Method Not AllowedMethodNotAllowed,/// 406 Not AcceptableNotAcceptable,/// 407 Proxy Authentication RequiredProxyAuthenticationRequired,/// 408 Request TimeoutRequestTimeout,/// 409 ConflictConflict,/// 410 GoneGone,/// 411 Length RequiredLengthRequired,/// 412 Precondition FailedPreconditionFailed,/// 413 Payload Too LargePayloadTooLarge,/// 414 URI Too LongURITooLong,/// 415 Unsupported Media TypeUnsupportedMediaType,/// 416 Range Not SatisfiableRangeNotSatisfiable,/// 417 Expectation FailedExpectationFailed,/// 418 I'm a teapotImATeapot,/// 421 Misdirected RequestMisdirectedRequest,/// 422 Unprocessable Entity (WebDAV)UnprocessableEntity,/// 423 Locked (WebDAV)Locked,/// 424 Failed Dependency (WebDAV)FailedDependency,/// 425 Too EarlyTooEarly,/// 426 Upgrade RequiredUpgradeRequired,/// 428 Precondition RequiredPreconditionRequired,/// 429 Too Many RequestsTooManyRequests,/// 431 Request Header Fields Too LargeRequestHeaderFieldsTooLarge,/// 451 Unavailable For Legal ReasonsUnavailableForLegalReasons,/// 500 Internal Server ErrorInternalServerError,/// 501 Not ImplementedNotImplemented,/// 502 Bad GatewayBadGateway,/// 503 Service UnavailableServiceUnavailable,/// 504 Gateway TimeoutGatewayTimeout,/// 505 HTTP Version Not SupportedHTTPVersionNotSupported,/// 506 Variant Also NegotiatesVariantAlsoNegotiates,/// 507 Insufficient Storage (WebDAV)InsufficientStorage,/// 508 Loop Detected (WebDAV)LoopDetected,/// 510 Not ExtendedNotExtended,/// 511 Network Authentication RequiredNetworkAuthenticationRequired,/// Unknown status codeUnknown,
}
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.9HTTP0_9,/// HTTP version 1.0HTTP1_0,/// HTTP version 1.1HTTP1_1,/// HTTP version 2.0HTTP2,/// HTTP version 3.0HTTP3,/// Unknown versionUnknown(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,/// UnknownUNKNOWN(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,/// UnknownUnknown(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 upgradeWebSocket,/// HTTP/2 cleartext upgrade (h2c)H2c,/// TLS upgrade (rare, experimental)Tls(String),/// Other custom or unknown upgrade protocolsUnknown(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_back("key").await;
获取 headers
let headers: RequestHeaders = ctx.get_request_header_backs().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)
GITHUB 地址
[!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)
GITHUB 地址
API 文档
一组提供 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)
GITHUB 地址
API 文档
一个库,用于在 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)
GITHUB 地址
API 文档
一个支持自动从恐慌中恢复的线程池,允许线程在发生恐慌后重新启动。适用于网络和 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)
GITHUB 地址
Api Docs
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)
GITHUB 地址
说明
[!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 aVecDeque
。join_paths!
: 将多个路径组合成一个有效的路径,并处理重叠的斜杠。cin!
: 从标准输入读取一行字符串输入。cin_parse!
: 将输入解析为指定的类型或类型数组。cout!
: 将格式化输出打印到标准输出(不换行)。endl!
: 打印一个换行符到标准输出。cout_endl!
: 打印格式化输出并追加一个换行符到标准输出,同时刷新缓冲区。execute!
: 使用提供的参数调用并执行指定函数。execute_async!
: 使用提供的参数调用并异步执行指定函数。
TCP 请求库 (tcp-request/README.md)
GITHUB 地址
API 文档
一个 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)
GITHUB 地址
API 文档
tcplane 是一个轻量级且高性能的 Rust TCP 服务器库,旨在简化网络服务开发。它支持 TCP 通信、数据流管理和连接处理,专注于提供高效的底层网络连接和数据传输能力,非常适合构建现代网络服务。
安装
可以通过以下命令安装该库:
cargo add tcplane
使用示例
use tcplane::*;async fn test_func(ctx: Context) {ctx.send("tcplane: 1").await.unwrap();
}fn panic_hook(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.panic_hook(panic_hook).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)
GITHUB 地址
API 文档
一个简单的 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)
GITHUB 地址
API 文档
一个轻量高效的 Rust 库,用于构建支持请求-响应处理的 UDP 服务器
安装
要使用此 crate,你可以运行以下命令:
cargo add udp
使用方法
use udp::*;async fn test_func(ctx: Context) {ctx.send("udp: 1").await.unwrap();
}fn panic_hook(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.panic_hook(panic_hook).await;server.func(test_func).await;server.func(|ctx: Context| async move {ctx.send("udp: 2").await.unwrap();}).await;server.run().await;
}