当前位置: 首页 > news >正文

Hyperlane框架最全教学(5693)

二进制加解密库 (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_hookpost_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,该选项基于 TokioTcpStream::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,该选项基于 TokioTcpStream::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,该选项基于 TokioTcpStream::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 框架,使用 redisserver-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 实体 UserEntityArticleEntity,保存于数据库 persistent 持久化,供 domain 使用
business 业务对象 封装核心业务逻辑(BO) UserBO::register 内部逻辑完整,不依赖框架 application 调用
data 数据对象 数据结构本身,不带行为(值对象、常量等) GenderEnumIdVODateRange domaindto 等层使用
data_access 数据访问对象 封装数据库交互(DAO、Repository) UserRepository::find_by_email() 操作 beanpersistent
data_transfer 数据传输对象 接口中传输的数据载体,常用于请求响应、分页、统一结构 ApiResponsePageUserDto 被 controller、OpenAPI 文档广泛使用
param 参数对象 接口入参、查询条件、分页等 LoginParamSearchQueryParam 传入 application
persistent 持久化对象 ORM 映射专用结构体,有时带属性注解 UserPersistent 映射数据库字段 bean 相似,偏向实现层
domain 领域对象 领域模型(实体和值对象),封装行为 OrderAggregate,可带行为如 Order::cancel() business 聚合使用
view 视图对象 接口输出结果的表现结构,适配前端需求 UserProfileViewArticleDetailView dtobean 转换而来

app/exception(异常处理层)

  • 被调用:

    • controller
    • service
    • mapper

app/filter(过滤器层)

  • 被调用:

    • controller 请求前过滤。

app/middleware(中间件层)

  • 被调用:

    • controller 请求或响应阶段增强,如权限校验、Header 注入等。

app/aspect(切面编程层)

  • 被调用:

    • 自动织入 controllerservice 等层处理日志、安全等横切关注点。

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/htmlimg:被 view 层或浏览器直接访问。
    • templates/html:被 controllerview 用于渲染页面。

快速开始 (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 + Clonetrait 的类型。

设置某个临时上下文属性

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 + Synctrait,否则无法跨线程调用。
不推荐 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)

中间件洋葱模型

graph TDA[Client] -->|Request| B[Request Middleware 1]B --> C[Request Middleware 2]C --> D[Request Middleware 3]D --> E[Controller]E --> F[Response Middleware 1]F --> G[Response Middleware 2]G --> H[Response Middleware 3]H -->|Response| I[Client]

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 生命周期变化

graph TDA[Client] -->|Request| Z[pre_ws_upgrade]Z --> Y[ws_upgrade]Y --> X[on_ws_connected]X --> B[Request Middleware 1]B --> C[Request Middleware 2]C --> D[Request Middleware 3]D --> E[Controller]E --> F[Response Middleware 1]F --> G[Response Middleware 2]G --> H[Response Middleware 3]H -->|Response| I[Client]

多服务 (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 额外封装了子字段的方法,可以直接调用大部分子字段的 getset 方法名称。
例如:调用 request 上的 get_method 方法,
一般需要从 ctx 解出 request,再调用request.get_method()
可以简化成直接调用 ctx.get_request_method().await

调用规律

  • requestget 方法的 get 名称后加 request 名称,中间使用_拼接。
  • requestset 方法的 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 框架没有发送响应前通过 ctxget_response 获取的只是响应的初始化实例,里面其实没有数据,
只有当用户发送响应时才会构建出完整 http 响应,此后再次 get_response 才能获取到响应内容。

[!tip]

hyperlane 框架对 ctx 额外封装了子字段的方法,可以直接调用大部分子字段的 getset 方法名称,
例如:调用 response 上的 get_status_code 方法。

调用规律

  • responseget 方法的 get 名称后加 response 名称,中间使用_拼接。
  • responseset 方法的 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 发送。此方法内部兼容了SSEWebsocket等协议,常用于响应中间件用于统一发送。

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 会成功,filevaluea/b/c/d。如果路径的最后不是正则动态路由,则仅使用正则匹配当前段的路由,例如 /test/{file:^.*$}/b 匹配 /test/a/b 会成功,filevaluea

朴素动态路由

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]

需要阻塞住当前处理函数,将后续所有请求在处理函数中处理。
这里使用 tokioselect 来处理多个请求,使用 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 库,支持 httphttps(需额外安装和导入),
使用参考 官方文档。

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 标准库数据结构提供的宏扩展集合,简化了常见集合(如 HashMapVec 等)的创建和操作。

特性

  • 简化初始化:使用宏轻松创建常见数据结构的实例。
  • 支持多种数据结构:包括 VecHashMapArc 等的宏。
  • 易于使用:直观的语法使数据结构的创建更加迅速。

安装

要安装 std-macro-extensions,请运行以下命令:

cargo add std-macro-extensions

用法

以下是如何使用该 crate 提供的宏的一些示例:

示例:使用 arc!

use std_macro_extensions::*;fn main() {let value = arc!(5);
}

示例:使用 b_tree_map!

use std_macro_extensions::*;fn main() {let empty_map: BTreeMap= b_tree_map!();let b_tree_map_a: BTreeMap= b_tree_map!("a" => "a","b" => "b");
}

示例:使用 b_tree_map!

use std_macro_extensions::*;fn main() {let empty_set: BTreeSet= b_tree_set!();let number_set: BTreeSet= b_tree_set!(1, 2, 3);
}

示例:使用 boxed!

use std_macro_extensions::*;fn main() {let boxed_value = boxed!(10);
}

示例:使用 cell!

use std_macro_extensions::*;fn main() {let cell_value: Cell= cell!(5);
}

示例:使用 hash_map!

use std_macro_extensions::*;fn main() {let my_map: HashMap= hash_map!();let my_map: HashMap= hash_map!("a" => 1, "b" => 2);
}

示例:使用 hash_set!

use std_macro_extensions::*;fn main() {let my_set: HashSet= hash_set!();let my_set: HashSet= hash_set!(1, 2, 3);
}

示例:使用 linked_list!

use std_macro_extensions::*;fn main() {let my_list: LinkedList= linked_list!();let my_list: LinkedList= linked_list!(1, 2, 3);
}

示例:使用 mutex!

use std_macro_extensions::*;fn main() {let my_mutex: Mutex= mutex!(5);let lock: MutexGuard= my_mutex.lock().unwrap();
}

示例:使用 rc!

use std_macro_extensions::*;fn main() {let my_rc = rc!(5);
}

示例:使用 refcell!

use std_macro_extensions::*;fn main() {let my_refcell = refcell!(5);
}

示例:使用 rw_lock!

use std_macro_extensions::*;fn main() {let my_rwlock = rw_lock!(5);
}

示例:使用 string!

use std_macro_extensions::*;fn main() {let empty_string = string!();let hello_string = string!("Hello");
}

示例:使用 vector!

use std_macro_extensions::*;fn main() {let empty_vector: Vec= vector!();let numbers = vector!(1, 2, 3);
}

示例:使用 vector_deque!

use std_macro_extensions::*;fn main() {let empty_deque: VecDeque= vector_deque!();let numbers = vector_deque!(1, 2, 3);
}

示例:使用 join_paths!

let combined_path: String = join_paths!("/home/", "/user/", "/documents", "file.txt");
let another_path: String = join_paths!("C:/", "/Program Files", "App");

示例:使用 cin!

let input: String = cin!();
println!("You typed: {}", input);

示例:使用 cin_parse!

let input: &str = "1 2 3";
let numbers: Vec= cin_parse!(input, Vec);
assert_eq!(numbers, vec![1, 2, 3]);
let single_input: &str = "12";
let number: i32 = cin_parse!(single_input, i32);
assert_eq!(number, 12);

示例:使用 cout!

let name: &str = "Alice";
let age: i32 = 30;
cout!("Name: {}, Age: {}\n", name, age);

示例:使用 endl!

endl!();

示例:使用 cout_endl!

let name: &str = "Alice";
let age: i32 = 30;
cout_endl!("Name: {}, Age: {}\n", name, age);

示例:使用 execute!

fn sum(data: &[i32]) -> i32 {data.iter().sum()
}
fn add_offset(data: &[i32], offset: i32) -> i32 {data.iter().map(|x| x + offset).sum()
}
let nums: Vec= vec![1, 2, 3];
let total: i32 = execute!(sum, &nums);
assert_eq!(total, 6);
let total_with_offset: i32 = execute!(add_offset, &nums, 10);
assert_eq!(total_with_offset, 36);

示例:使用 execute_async!

let data: Vec= vec![1, 2, 3];
async fn async_func(data: &[i32], offset: i32) -> i32 {data.iter().map(|x| x + offset).sum()
}
let res: i32 = execute_async!(async_func, &data, 1).await;
assert_eq!(res, 9);

可用的宏

  • arc!:创建一个 Arc
  • vector!:创建一个 Vec
  • map!:创建一个 HashMap
  • set!:创建一个 HashSet
  • b_tree_map!:创建一个 BTreeMap
  • b_tree_set!:创建一个 BTreeSet
  • list!:创建一个 LinkedList
  • heap!:创建一个 BinaryHeap
  • string!:创建一个 String
  • boxed!:创建一个 Box
  • rc!:创建一个 Rc
  • arc!:创建一个 Arc
  • mutex!:创建一个 Mutex
  • rw_lock!:创建一个 RwLock
  • cell!:创建一个 Cell
  • ref_cell!:创建一个 RefCell
  • vector_deque!: Creates a VecDeque
  • join_paths!: 将多个路径组合成一个有效的路径,并处理重叠的斜杠。
  • cin!: 从标准输入读取一行字符串输入。
  • cin_parse!: 将输入解析为指定的类型或类型数组。
  • cout!: 将格式化输出打印到标准输出(不换行)。
  • endl!: 打印一个换行符到标准输出。
  • cout_endl!: 打印格式化输出并追加一个换行符到标准输出,同时刷新缓冲区。
  • execute!: 使用提供的参数调用并执行指定函数。
  • execute_async!: 使用提供的参数调用并异步执行指定函数。

TCP 请求库 (tcp-request/README.md)

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;
}
http://www.sczhlp.com/news/591.html

相关文章:

  • Web服务器性能大比拼:谁才是真正的速度之王(3996)
  • 高性能路由系统的设计与实现(8404)
  • 零依赖Web框架的设计哲学(2930)
  • 实战项目:全栈在线群聊系统(7371)
  • Hello-javasec靶场Java代码审计
  • Hyperlane性能调优秘籍:从毫秒级响应到百万QPS的优化之路(1356)
  • 轻量级服务器架构的极致优化(5633)
  • 延迟优化的极致追求:毫秒级响应的秘密(0202)
  • 跨平台Web服务开发的新选择(4862)
  • 现代Web服务器性能革命:我的Rust框架探索之旅(9477)
  • 并发处理能力的巅峰对决:异步编程的艺术(4095)
  • 带宽是什么?
  • 内存安全的Web服务器实现(6253)
  • 实时通信技术深度对比:WebSocket与SSE的最佳实践(1018)
  • 微服务架构的轻量级解决方案(6064)
  • WebSocket服务端的高效处理(1104)
  • 服务端推送技术的现代实现(6185)
  • 异步编程在Web开发中的应用(1191)
  • sequence的启动
  • L. Dynamic Convex Hull 题解
  • 最左前缀原则和覆盖索引相关问题
  • 【LeetCode 142】算法:环形链表 II
  • Gin框架介绍
  • 正则表达式中的元字符
  • 7/27
  • I2C
  • 小新Pad2022刷机记录
  • 每日随笔
  • 01API语法与路由配置详解
  • 图 - -刘-j-x