创建和运行服务器
简介
服务器是任何 Web 应用的核心。在 Hyperlane 中,创建和运行服务器被设计为简单直接,同时为复杂的部署场景提供所需的灵活性。本文涵盖了创建服务器的所有方式、如何启动和停止服务器,以及如何并发运行多个服务器。
服务器创建方式
Hyperlane 提供了三种不同的创建服务器方式,每种适用于不同的用例。
方式一:默认服务器
创建服务器的最简单方式是使用 Server::default()。这会创建一个具有合理默认设置的服务器:
let mut server: Server = Server::default();
let server_control_hook: ServerControlHook = server.run().await.unwrap_or_default();
server_control_hook.wait().await;
默认服务器非常适合开发、测试和简单的应用。它使用预定义的地址、请求限制和连接处理设置,因此你可以专注于构建应用逻辑,而不是配置。
方式二:从 ServerConfig 创建
当需要自定义服务器级别的设置(如绑定地址、nodelay 或 TTL)时,可以从 ServerConfig 创建服务器:
let server_config: ServerConfig = ServerConfig::default();
let mut server: Server = Server::from(server_config);
这种方法让你可以完全控制服务器的行为。你可以在服务器启动前配置地址、TCP 选项和其他设置。Server::from() trait 实现会自动处理转换。
方式三:从 RequestConfig 创建
如果你主要关心请求级别的设置(缓冲区大小、体大小限制、超时时间),可以从 RequestConfig 创建服务器:
let request_config: RequestConfig = RequestConfig::default();
let mut server: Server = Server::from(request_config);
当你想微调服务器如何处理各个请求,但不改变服务器级别设置时,这种方式很有用。
启动服务器
创建服务器实例后,需要启动它。run() 方法是关键:
let server_control_hook: ServerControlHook = server.run().await.unwrap_or_default();
run() 方法是异步的,返回 Result<ServerControlHook, ...>。ServerControlHook 是一个控制句柄,让你在服务器启动后对其进行管理。
ServerControlHook
ServerControlHook 提供了两个基本操作:
- wait():保持服务器运行,直到显式关闭。
- shutdown():优雅地停止服务器。
// 启动并保持运行
let server_control_hook: ServerControlHook = server.run().await.unwrap_or_default();
server_control_hook.wait().await;
保持服务器存活
调用 run() 后,需要保持服务器进程运行。ServerControlHook 上的 wait() 方法会阻塞当前任务,直到服务器关闭:
server_control_hook.wait().await;
这通常是 main 函数中的最后一条语句。没有它,程序会在 run() 返回后立即退出。
优雅停机
Hyperlane 通过 ServerControlHook 支持优雅停机。当你调用 shutdown() 时,服务器会停止接受新连接,并在停止前完成对现有连接的处理:
let server_control_hook: ServerControlHook = server.run().await.unwrap_or_default();
server_control_hook.shutdown().await;
这对于生产环境部署非常重要,因为在这些环境中需要重启或停止服务器而不丢失正在处理的请求。停机过程确保:
- 不再接受新连接
- 现有连接有足够的时间完成
- 资源被正确清理
属性宏风格
Hyperlane 的属性宏系统提供了一种更声明式的方式来设置服务器:
use hyperlane::*;
use hyperlane_macros::*;
#[hyperlane(server: Server)]
#[hyperlane(server_config: ServerConfig)]
#[tokio::main]
async fn main() {
server_config.set_nodelay(Some(false));
server.server_config(server_config);
let server_control_hook: ServerControlHook = server.run().await.unwrap_or_default();
server_control_hook.wait().await;
}
在这种风格中:
#[hyperlane(server: Server)]将server变量注入到函数作用域#[hyperlane(server_config: ServerConfig)]将server_config变量注入到函数作用域- 你可以在调用
run()之前直接配置服务器
这种方法在与用于路由、中间件和请求处理的其他属性宏结合使用时特别有用。
运行多个服务器
Hyperlane 使得使用 Tokio 的 spawn 和 join! 宏并发运行多个服务器实例变得容易。这对于在不同端口上服务于不同应用程序很有用:
let app1 = tokio::spawn(async move {
let mut server_config: ServerConfig = ServerConfig::default();
server_config.set_address(Server::format_bind_address(DEFAULT_HOST, 80));
let mut server: Server = Server::default();
server.server_config(server_config);
let server_control_hook: ServerControlHook = server.run().await.unwrap_or_default();
server_control_hook.wait().await;
});
let app2 = tokio::spawn(async move {
let mut server_config: ServerConfig = ServerConfig::default();
server_config.set_address(Server::format_bind_address(DEFAULT_HOST, 81));
let mut server: Server = Server::default();
server.server_config(server_config);
let server_control_hook: ServerControlHook = server.run().await.unwrap_or_default();
server_control_hook.wait().await;
});
let _ = tokio::join!(app1, app2);
在这个示例中:
app1在 80 端口运行app2在 81 端口运行- 两个服务器作为独立的 Tokio 任务并发运行
tokio::join!等待两个任务完成
Server::format_bind_address(DEFAULT_HOST, port) 辅助方法使得以编程方式构建绑定地址变得容易。
多服务器用例
运行多个服务器在以下场景中很有用:
- HTTP 和 HTTPS:在 80 端口运行 HTTP 服务器,在 443 端口运行 HTTPS 服务器。
- API 版本控制:在不同端口提供不同版本的 API。
- 微服务:在同一进程中运行多个小型服务。
- 健康检查:在一个端口运行主应用,在另一个端口运行健康检查端点。
完整示例
以下是一个完整的示例,展示了创建、配置、运行和关闭服务器的完整生命周期:
use hyperlane::*;
#[tokio::main]
async fn main() {
// 使用自定义配置创建服务器
let mut config: ServerConfig = ServerConfig::default();
config.set_address("0.0.0.0:8080");
config.set_nodelay(Some(true));
config.set_ttl(Some(128));
let mut server: Server = Server::from(config);
// 启动服务器
let server_control_hook: ServerControlHook = server.run().await.unwrap_or_default();
// 服务器现在正在运行并接受连接
// 在实际应用中,你可以在这里注册路由和中间件
// 保持服务器存活
server_control_hook.wait().await;
}
错误处理
启动服务器时,run() 方法返回一个 Result。unwrap_or_default() 调用在服务器启动失败时提供默认的 ServerControlHook:
let server_control_hook: ServerControlHook = server.run().await.unwrap_or_default();
在生产代码中,你应该显式处理错误:
match server.run().await {
Ok(hook) => {
println!("Server started successfully");
hook.wait().await;
}
Err(e) => {
eprintln!("Failed to start server: {:?}", e);
}
}
服务器启动失败的常见原因包括:
- 端口已被使用
- 没有足够的权限绑定到地址
- 配置值无效
总结
在 Hyperlane 中创建和运行服务器被设计为简单而灵活。无论你需要用于开发的快速默认服务器,还是用于生产环境的多服务器设置,Hyperlane 都提供了所需的工具。ServerControlHook 为服务器的生命周期提供了干净的控制,属性宏系统则为喜欢声明式风格的开发者提供了替代方案。
在下一篇文章中,我们将探讨 Hyperlane 的中间件系统,这对于实现身份验证、日志记录和跨域等横切关注点至关重要。