自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Rust中的信號處理:Unix信號 vs 信號服務(wù)器

開發(fā) 前端
如果你的程序不需要HTTP或網(wǎng)絡(luò),那么引入一個完整的HTTP框架來監(jiān)聽信號可能有點多余。因此,根據(jù)程序的大小,以及系統(tǒng)管理員的需求或SRE團隊的大小,來決定是否添加HTTP服務(wù)器,因為這對于管理流程的人員和軟件來說,它有更好的用戶體驗。

如果你正在運行一個服務(wù)器,假設(shè)服務(wù)器需要從磁盤讀取一些文件,比如證書或密鑰。證書經(jīng)常會發(fā)生變化,因此你的服務(wù)器必須重新加載它們。如何告訴服務(wù)器重新加載這些文件?

傳統(tǒng)的方法是使用Unix信號,你的服務(wù)器偵聽特定的信號,如SIGUSR1(用戶定義的信號#1)或SIGHUP(掛起信號),并且可以在接收到信號時執(zhí)行你編寫的任何代碼。因此,你的服務(wù)器等待適當?shù)男盘?,接收它,然后重新加載證書。

這種方法工作得很好,但是在實際應(yīng)用中出現(xiàn)了一些可用性的問題。使用單獨的一個http服務(wù)器來處理信號會更好。

下面我們先來看一下使用Unix信號的例子,然后我們使用服務(wù)器處理信號來改進這個例子。

首先,我們先創(chuàng)建一個Rust項目:

cargo new signals-servers

在Cargo.toml文件中加入以下依賴項:

[dependencies]
axum = "0.7.2"
tokio = { version = "1.25.0", features = ["macros", "rt-multi-thread", "signal"] }

在項目根目錄下創(chuàng)建一個cert.pem文件,內(nèi)容隨便寫,只是為了演示。

Unix信號處理

我們看一個完整的服務(wù)器偵聽信號的示例,當你啟動你的服務(wù)器時,也啟動一個異步任務(wù)(或進程,或線程)來監(jiān)聽這個信號,當接收到信號時,重新加載證書。

創(chuàng)建一個src/bin/unix_signal.rs文件,代碼如下:

use axum::{routing::get, Router};
use std::process;
use tokio::signal::unix::{signal, SignalKind};

#[tokio::main]
async fn main() {
    let _cert = std::fs::read_to_string("cert.pem");
    println!("已加載證書,正在啟動web服務(wù)器");
    println!("PID: {}", process::id());
    tokio::select! {
        _ = start_normal_server(8080) => {
            println!("Web服務(wù)器關(guān)閉")
        }
        _ = listen_for_reload(SignalKind::hangup()) => {
            println!("信號監(jiān)聽器停止")
        }
    }
}

async fn start_normal_server(port: u32) {
    // 構(gòu)建我們的應(yīng)用程序
    let app = Router::new().route("/hello", get(|| async { "Hello, world!" }));

    let addr = format!("127.0.0.1:{port}");
    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn listen_for_reload(signal_kind: SignalKind) -> Result<(), std::io::Error> {
    // 監(jiān)聽信號
    let mut stream = signal(signal_kind)?;

    loop {
        stream.recv().await;

        match std::fs::read_to_string("cert.pem") {
            Ok(_) => eprintln!("重新加載證書成功"),
            Err(e) => eprintln!("無法重新加載證書: {e}"),
        }
    }
}

運行如下命令啟動服務(wù)器:

cargo run --bin unix_signal

已加載證書,正在啟動web服務(wù)器
PID: 41945

然后打開一個新的終端,輸入以下命令:

kill -s sighup 41945

這是服務(wù)器的日志如下:

已加載證書,正在啟動web服務(wù)器
PID: 41945
重新加載證書成功

這是可行的,但對于發(fā)送信號的人來說,這不是一個很好的用戶體驗。假設(shè)你是SRE或系統(tǒng)管理員,當需要重新加載服務(wù)器證書時,首先查找進程的PID,并使用kill -s sighup pid發(fā)送信號。

服務(wù)器可能重新加載了,但也許它沒有,可能出現(xiàn)了錯誤,例如新證書無效,或者服務(wù)器沒有讀取新證書的權(quán)限。系統(tǒng)管理員如何知道是否發(fā)生了這種情況?他們應(yīng)該檢查一下服務(wù)器的日志,但這需要切換窗口,或者打開一個不同的程序。

這不是一個很好的用戶體驗。通常,當你運行命令時,希望得到一些反饋。但是當你發(fā)送Unix信號時,終端不會給你任何響應(yīng)。你必須查找服務(wù)器的日志并檢查它們,以確保重新加載成功完成。閱讀一個不熟悉的程序日志是很困難的,特別是當日志中有很多其他錯誤時。

Unix信號的主要問題是,它們讓用戶向進程發(fā)出信號,但程序不向用戶發(fā)送響應(yīng)。

更好的方法:信號服務(wù)器

因此,我們希望進程接受請求(“重新加載您的證書”),并響應(yīng)(“是的,它成功了”或“它失敗了,原因如下”)。這聽起來很熟悉——它只是一個普通的請求-響應(yīng)協(xié)議。沒有必要重新發(fā)明輪子——我們可以在這個過程中啟動第二個小HTTP服務(wù)器。

創(chuàng)建一個src/bin/http_signal.rs文件,代碼如下:

use axum::{
    http::StatusCode,
    response::IntoResponse,
    routing::{get, post},
    Router,
};

#[tokio::main]
async fn main() {
    let _cert = std::fs::read_to_string("cert.pem");
    println!("已加載證書,正在啟動web服務(wù)器");

    tokio::select! {
        _ = start_normal_server(8080) => {
            println!("Web服務(wù)器關(guān)閉")
        }
        _ = start_control_server(3000) => {
            println!("信號服務(wù)器關(guān)閉")
        }
    }
}

async fn start_normal_server(port: u32) {
    // 構(gòu)建我們的應(yīng)用程序
    let app = Router::new().route("/hello", get(|| async { "Hello, world!" }));

    let addr = format!("127.0.0.1:{port}");
    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn start_control_server(port: u32) {
    // 構(gòu)建信號控制服務(wù)器
    let app: Router = Router::new().route(
        "/reload_certs",
        post(|| async {
            println!("重新加載證書");
            match std::fs::read_to_string("cert.pem") {
                Ok(_) => "重新加載證書成功".into_response(),
                Err(e) => {
                    let error = format!("無法重新加載證書: {e}");
                    eprintln!("{error}");
                    let resp = (StatusCode::INTERNAL_SERVER_ERROR, error);
                    resp.into_response()
                }
            }
        }),
    );

    let addr = format!("127.0.0.1:{port}");
    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

對于SRE或系統(tǒng)管理員來說,這是一個更好的用戶體驗。使用如下命令重新加載證書:

$ curl -X POST 0.0.0.0:3000/reload_certs
重新加載證書成功%

如果沒有找到證書,會立即得到有關(guān)錯誤的反饋:

$ curl -X POST 0.0.0.0:3000/reload_certs
無法重新加載證書: No such file or directory (os error 2)

總結(jié)

如果你的程序不需要HTTP或網(wǎng)絡(luò),那么引入一個完整的HTTP框架來監(jiān)聽信號可能有點多余。因此,根據(jù)程序的大小,以及系統(tǒng)管理員的需求或SRE團隊的大小,來決定是否添加HTTP服務(wù)器,因為這對于管理流程的人員和軟件來說,它有更好的用戶體驗。

責(zé)任編輯:武曉燕 來源: coding到燈火闌珊
相關(guān)推薦

2010-04-21 16:25:13

Unix信號量

2010-04-21 16:42:48

Unix信號量

2010-04-21 16:50:31

Unix信號量

2010-04-21 17:10:25

Unix信號量

2010-04-21 15:37:38

Unix信號量

2019-02-22 14:40:35

信號壓縮領(lǐng)域

2010-07-30 14:22:47

RIP協(xié)議

2017-03-20 13:26:35

英特爾服務(wù)器閃存

2015-07-23 15:00:41

DSP

2024-01-03 10:17:51

Linux通信

2019-01-04 12:46:03

程序員技能溝通

2010-04-09 15:29:10

無線信號故障

2012-11-07 10:57:46

無線路由器飛魚星

2020-02-04 09:18:28

PythonRedis數(shù)據(jù)

2011-06-30 17:31:32

Qt 多線程 信號

2010-10-08 15:55:38

無線路由信號

2011-06-09 09:45:35

Linux QT 信號

2022-03-29 10:56:46

Pythonblinker信號庫

2017-12-06 13:24:57

服務(wù)器計算信號

2025-04-16 08:50:00

信號量隔離線程池隔離并發(fā)控制
點贊
收藏

51CTO技術(shù)棧公眾號