Rust优雅关闭应用插件shutdown-handler的使用,实现安全可靠的进程终止和资源清理

Rust优雅关闭应用插件shutdown-handler的使用,实现安全可靠的进程终止和资源清理

shutdown-handler是一个允许应用程序各部分触发关闭的处理器。

安装

在Cargo.toml中添加依赖:

[dependencies]
shutdown-handler = "0.1.0"

使用示例

下面是一个完整的示例,展示如何使用shutdown-handler实现优雅关闭:

use shutdown_handler::ShutdownHandler;
use std::thread;
use std::time::Duration;

fn main() {
    // 创建ShutdownHandler实例
    let shutdown = ShutdownHandler::new();
    
    // 获取关闭信号接收器
    let mut receiver = shutdown.subscribe();
    
    // 模拟工作线程
    let worker = thread::spawn(move || {
        loop {
            // 检查是否收到关闭信号
            if receiver.try_recv().is_ok() {
                println!("Worker: Received shutdown signal, cleaning up...");
                // 执行清理工作
                thread::sleep(Duration::from_secs(1)); // 模拟清理
                println!("Worker: Cleanup complete, exiting");
                break;
            }
            
            // 模拟工作
            println!("Worker: Doing some work...");
            thread::sleep(Duration::from_secs(1));
        }
    });
    
    // 模拟主线程工作
    for i in 0..5 {
        println!("Main: Working {}...", i);
        thread::sleep(Duration::from_secs(1));
    }
    
    // 触发关闭
    println!("Main: Triggering shutdown...");
    shutdown.shutdown();
    
    // 等待工作线程完成清理
    worker.join().unwrap();
    println!("Main: Application shutdown complete");
}

完整示例代码

下面是一个更完整的示例,展示如何在多线程环境中使用shutdown-handler:

use shutdown_handler::ShutdownHandler;
use std::sync::Arc;
use std::thread;
use std::time::Duration;

struct Resource {
    name: String,
    // 其他资源字段...
}

impl Resource {
    fn cleanup(&self) {
        println!("Resource {}: Cleaning up...", self.name);
        thread::sleep(Duration::from_millis(500)); // 模拟清理耗时
        println!("Resource {}: Cleanup completed", self.name);
    }
}

fn main() {
    // 创建共享的ShutdownHandler
    let shutdown = Arc::new(ShutdownHandler::new());
    
    // 创建两个资源
    let resource1 = Arc::new(Resource { name: "DB Connection".to_string() });
    let resource2 = Arc::new(Resource { name: "File Handler".to_string() });
    
    // 创建工作线程1 - 处理资源1
    let shutdown_clone1 = shutdown.clone();
    let resource1_clone = resource1.clone();
    let worker1 = thread::spawn(move || {
        let mut receiver = shutdown_clone1.subscribe();
        loop {
            if receiver.try_recv().is_ok() {
                resource1_clone.cleanup();
                break;
            }
            
            println!("Worker1: Processing {}...", resource1_clone.name);
            thread::sleep(Duration::from_secs(1));
        }
    });
    
    // 创建工作线程2 - 处理资源2
    let shutdown_clone2 = shutdown.clone();
    let resource2_clone = resource2.clone();
    let worker2 = thread::spawn(move || {
        let mut receiver = shutdown_clone2.subscribe();
        loop {
            if receiver.try_recv().is_ok() {
                resource2_clone.cleanup();
                break;
            }
            
            println!("Worker2: Processing {}...", resource2_clone.name);
            thread::sleep(Duration::from_secs(1));
        }
    });
    
    // 主线程工作
    for i in 1..=3 {
        println!("Main: Executing task {}...", i);
        thread::sleep(Duration::from_secs(2));
    }
    
    // 触发优雅关闭
    println!("Main: Initiating graceful shutdown...");
    shutdown.shutdown();
    
    // 等待所有工作线程完成清理
    worker1.join().unwrap();
    worker2.join().unwrap();
    
    println!("Main: All resources cleaned up, application exiting");
}

工作原理

  1. 创建ShutdownHandler实例
  2. 通过subscribe()方法获取关闭信号接收器
  3. 工作线程定期检查是否收到关闭信号
  4. 主线程调用shutdown()方法触发关闭
  5. 工作线程收到信号后执行清理工作并退出

特点

  • 线程安全:可以在多线程环境中使用
  • 非阻塞:try_recv()方法不会阻塞线程
  • 轻量级:实现简洁高效
  • 可扩展:支持多个接收器同时监听关闭信号

许可证

可选择以下任一许可证:

  • Apache License, Version 2.0
  • MIT license

1 回复

Rust优雅关闭应用插件shutdown-handler使用指南

shutdown-handler 是一个 Rust 库,用于实现应用程序的优雅关闭,确保在进程终止时能够安全地清理资源。

功能特点

  • 捕获操作系统信号(如 SIGINT, SIGTERM)
  • 提供优雅关闭的钩子函数
  • 支持异步和同步操作
  • 允许设置关闭超时时间
  • 线程安全的实现

安装

Cargo.toml 中添加依赖:

[dependencies]
shutdown-handler = "0.3"

基本使用

同步版本

use shutdown_handler::ShutdownHandler;

fn main() {
    // 创建关闭处理器
    let shutdown = ShutdownHandler::new().unwrap();
    
    // 注册关闭钩子
    shutdown.register(Box::new(|| {
        println!("执行清理操作...");
        // 这里放置资源清理代码
    }));
    
    // 模拟应用运行
    println!("应用运行中,按Ctrl+C终止...");
    while !shutdown.is_shutdown() {
        std::thread::sleep(std::time::Duration::from_secs(1));
        println!("运行中...");
    }
    
    println!("应用已关闭");
}

异步版本 (tokio)

use shutdown_handler::tokio::ShutdownHandler;
use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() {
    let shutdown = ShutdownHandler::new().unwrap();
    
    // 注册异步关闭钩子
    shutdown.register(Box::new(|| {
        Box::pin(async {
            println!("开始异步清理...");
            sleep(Duration::from_secs(1)).await;
            println!("异步清理完成");
        })
    }));
    
    println!("应用运行中,按Ctrl+C终止...");
    while !shutdown.is_shutdown() {
        sleep(Duration::from_secs(1)).await;
        println!("运行中...");
    }
    
    println!("应用已关闭");
}

高级用法

设置超时时间

use shutdown_handler::ShutdownHandler;
use std::time::Duration;

fn main() {
    // 设置5秒超时
    let shutdown = ShutdownHandler::builder()
        .timeout(Duration::from_secs(5))
        .build()
        .unwrap();
    
    shutdown.register(Box::new(|| {
        println!("长时间运行的清理操作...");
        std::thread::sleep(Duration::from_secs(10));
        println!("清理完成(可能不会执行到这里)");
    }));
    
    println!("尝试按Ctrl+C,清理操作将在5秒后强制终止");
    while !shutdown.is_shutdown() {
        std::thread::sleep(Duration::from_secs(1));
    }
}

多个关闭钩子

use shutdown_handler::ShutdownHandler;

fn main() {
    let shutdown = ShutdownHandler::new().unwrap();
    
    // 注册多个关闭钩子(按注册顺序逆序执行)
    shutdown.register(Box::new(|| println!("钩子3")));
    shutdown.register(Box::new(|| println!("钩子2")));
    shutdown.register(Box::new(|| println!("钩子1")));
    
    println!("按Ctrl+C查看钩子执行顺序");
    while !shutdown.is_shutdown() {
        std::thread::sleep(std::time::Duration::from_secs(1));
    }
}

信号处理

默认情况下,shutdown-handler 会处理以下信号:

  • SIGINT (Ctrl+C)
  • SIGTERM (kill 命令)
  • SIGHUP (终端关闭)

你也可以自定义处理的信号:

use shutdown_handler::ShutdownHandler;
use nix::sys::signal::Signal;

fn main() {
    let shutdown = ShutdownHandler::builder()
        .signals(&[Signal::SIGINT, Signal::SIGUSR1])  // 只处理SIGINT和SIGUSR1
        .build()
        .unwrap();
    
    // ...其余代码
}

完整示例

下面是一个结合了多种功能的完整示例:

use shutdown_handler::ShutdownHandler;
use std::time::Duration;

fn main() {
    // 创建带超时和自定义信号的关闭处理器
    let shutdown = ShutdownHandler::builder()
        .timeout(Duration::from_secs(3))  // 3秒超时
        .build()
        .unwrap();

    // 注册多个关闭钩子
    shutdown.register(Box::new(|| {
        println!("[钩子1] 开始关闭数据库连接...");
        std::thread::sleep(Duration::from_secs(1));
        println!("[钩子1] 数据库连接已关闭");
    }));

    shutdown.register(Box::new(|| {
        println!("[钩子2] 开始保存缓存数据...");
        std::thread::sleep(Duration::from_secs(2));
        println!("[钩子2] 缓存数据已保存");
    }));

    // 模拟应用主循环
    println!("服务器启动成功,等待请求中...");
    println!("按Ctrl+C可优雅关闭服务器");

    let mut request_count = 0;
    while !shutdown.is_shutdown() {
        // 模拟处理请求
        std::thread::sleep(Duration::from_millis(500));
        request_count += 1;
        println!("处理请求 #{}", request_count);
        
        // 检查关闭信号
        if shutdown.is_shutdown() {
            println!("收到关闭信号,开始优雅关闭...");
            break;
        }
    }

    // 等待所有钩子执行完成
    shutdown.wait();
    println!("服务器已完全关闭");
}

最佳实践

  1. ShutdownHandler 实例存储在应用程序的全局或主要结构中
  2. 在启动后台任务前注册关闭钩子
  3. 在长时间运行的任务中定期检查 is_shutdown()
  4. 为关键资源清理设置合理的超时时间
  5. 避免在关闭钩子中执行可能阻塞的操作

shutdown-handler 为 Rust 应用提供了简单可靠的优雅关闭解决方案,特别适合需要确保资源正确释放的服务器应用和长时间运行的后台服务。

回到顶部