rust代码无法在部分机器上正常启动?

rust代码无法在部分机器上正常启动
部署的机器都是Debian12 x86架构

main.rs

use std::sync::atomic::AtomicBool;
use std::sync::Arc;

mod watchdog;
mod web;

static mut NORMAL_LIST: Vec<String> = Vec::new();
static mut REALITY_LIST: Vec<String> = Vec::new();

#[tokio::main]
pub async fn main() {
    // 处理 Ctrl-C 信号
    let running = Arc::new(AtomicBool::new(true));

    ctrlc::set_handler(move || {
        std::process::exit(0);
    })
    .expect("Error setting Ctrl-C handler");

    tokio::spawn(async {
        watchdog::start_watch().await;
    });

    tokio::spawn(async {
        web::start_web().await;
    });

    while running.load(std::sync::atomic::Ordering::Relaxed) {
        tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
    }
}

web.rs

use rand::seq::SliceRandom;
use salvo::http::HeaderValue;
use salvo::http::StatusCode;
use salvo::logging::Logger;
use salvo::prelude::*;

use crate::*;

#[handler]
pub async fn avatar(res: &mut Response, req: &Request) {
    // 获取 Host
    let host = match req.headers().get("Host") {
        Some(t) => t.to_str().unwrap(),
        _ => {
            res.status_code(StatusCode::BAD_REQUEST);
            return;
        }
    };

    // 拼接字符串,转成url编码
    let host = host.splitn(2, '.').nth(1).unwrap_or("");
    let host = format!("resources.{host}");

    // 获取 协议类型
    let protocol = match req
        .headers()
        .get("scheme")
        .unwrap_or(&HeaderValue::from_static("https"))
        .to_str()
        .unwrap()
    {
        "http" => "http",
        _ => "https",
    };

    // 拼接字符串,转成url编码
    let host = host.splitn(2, '.').nth(1).unwrap_or("");
    let host = format!("resources.{host}");

    // 随机从 NORMAL_LIST 中选择一个文件名
    unsafe {
        let file_name = urlencoding::encode(NORMAL_LIST.choose(&mut rand::thread_rng()).unwrap());
        res.render(Redirect::found(format!(
            "{protocol}://{host}/avatar/{file_name}"
        )));
    }
}

#[handler]
pub async fn random(res: &mut Response, req: &Request) {
    // 获取查询参数
    let img_type = match req.query::<&str>("type") {
        Some("reality") => "reality",
        _ => "normal",
    };

    // 获取 Host
    let host = match req.headers().get("Host") {
        Some(t) => t.to_str().unwrap(),
        _ => {
            res.status_code(StatusCode::BAD_REQUEST);
            return;
        }
    };

    // 拼接字符串,转成url编码
    let host = host.splitn(2, '.').nth(1).unwrap_or("");
    let host = format!("resources.{host}");

    // 获取 协议类型
    let protocol = match req
        .headers()
        .get("scheme")
        .unwrap_or(&HeaderValue::from_static("https"))
        .to_str()
        .unwrap()
    {
        "http" => "http",
        _ => "https",
    };

    // 随机从中选择一个文件名
    unsafe {
        let file_name = match img_type {
            "reality" => urlencoding::encode(REALITY_LIST.choose(&mut rand::thread_rng()).unwrap()),
            _ => urlencoding::encode(NORMAL_LIST.choose(&mut rand::thread_rng()).unwrap()),
            
        };
        res.render(Redirect::found(format!(
            "{protocol}://{host}/{img_type}/{file_name}"
        )));
    }
}

pub async fn start_web() {
    tracing_subscriber::fmt().try_init().unwrap();
    let router = Router::with_path("yuri")
        .push(Router::with_path("random").get(random))
        .push(Router::with_path("avatar.jpg").get(avatar));
    let service = Service::new(router).hoop(Logger::new());
    let acceptor = TcpListener::new("127.0.0.1:8081").bind().await;
    Server::new(acceptor).serve(service).await;
}

watchdog.rs

use hotwatch::{Event, Hotwatch};
use std::fs;
use std::thread::sleep;
use std::time::Duration;
use crate::*;

pub async fn start_watch() {

    // 获取目录下所有文件名
    for path in fs::read_dir("../data/output/normal").unwrap() {
        unsafe {
            NORMAL_LIST.push(path.unwrap().file_name().to_str().unwrap().to_string());
        }
    }
    for path in fs::read_dir("../data/output/reality").unwrap() {
        unsafe {
            REALITY_LIST.push(path.unwrap().file_name().to_str().unwrap().to_string());
        }
    }

    let mut hotwatch = Hotwatch::new().expect("hotwatch failed to initialize!");
    hotwatch
        .watch("../data/output/normal", |event: Event| match event.kind {
            _ => {
                // 清空 NORMAL_LIST
                unsafe {
                    NORMAL_LIST.clear();
                }
                // 获取目录下所有文件名
                for path in fs::read_dir("../data/output/normal").unwrap() {
                    unsafe {
                        NORMAL_LIST.push(path.unwrap().file_name().to_str().unwrap().to_string());
                    }
                }
            }
        })
        .expect("failed to watch file!");
    hotwatch
        .watch("../data/output/reality", |event: Event| match event.kind {
            _ => {
                // 清空 NORMAL_LIST
                unsafe {
                    REALITY_LIST.clear();
                }
                // 获取目录下所有文件名
                for path in fs::read_dir("../data/output/reality").unwrap() {
                    unsafe {
                        REALITY_LIST.push(path.unwrap().file_name().to_str().unwrap().to_string());
                    }
                }
            }
        })
        .expect("failed to watch file!");
    loop {
        sleep(Duration::from_secs(1));
    }
}

部分成功启动机器截图,请忽略找不到路径的报错
image.png

启动失败机器截图
image.png

尝试1
更改x86_64-unknown-linux-gnu为x86_64-unknown-linux-musl 无效

尝试2
在watchdog.rs中添加输出代码可以正常输出,在web.rs中进行同样操作没有输出

求大佬解答

阅读 968
avatarAI BotBETA

你的问题可能涉及几个不同的方面,我将逐一进行分析。

  1. 并发安全:你的代码中,NORMAL_LISTREALITY_LIST 是全局的 Vec<String>,并且使用 unsafe 块在不加锁的情况下进行读写操作。这在多线程环境下可能会导致数据竞争(race condition),这可能是导致部分机器启动失败的原因。你可以考虑使用线程安全的数据结构,如 std::sync::Mutexstd::collections::VecDeque(如果你只需要在末尾添加和删除元素)来确保并发安全。
  2. 文件系统监控:你使用了 hotwatch 库来监控文件系统的变化。这个库在某些系统或文件系统上可能表现不稳定。你可以考虑更换为其他更稳定的文件系统监控库,如 fsnotify
  3. Rust版本和依赖库版本:确保所有机器上的Rust版本和依赖库版本都是一致的。不同版本的库可能有不同的行为或bug,这可能会导致程序在某些机器上无法正常运行。
  4. 调试信息:尝试在代码中添加更多的调试信息,如日志输出,以帮助确定问题发生的具体位置。特别是在 watchdog.rsweb.rs 中,你可以添加日志输出以检查代码的执行流程和数据状态。
  5. 系统资源限制:检查启动失败的机器是否有足够的系统资源(如内存、CPU等)来运行程序。如果资源不足,可能会导致程序无法启动或崩溃。
  6. 环境差异:虽然你提到所有机器都是Debian 12 x86架构,但可能存在一些细微的环境差异,如内核版本、系统配置等。这些差异可能会导致程序在某些机器上无法正常运行。你可以尝试使用容器技术(如Docker)来确保所有机器上的环境完全一致。

综上所述,你需要对代码进行一些修改,以确保其在并发环境下的正确性,并考虑更换文件系统监控库、统一Rust和依赖库版本、添加更多调试信息、检查系统资源限制以及确保环境一致性等方面的问题。希望这些建议能对你有所帮助!

1 个回答
✓ 已被采纳

原因为出现问题的机器只有单线程,代码中有两处使用到了tokio::spawn需要两个线程执行,所以web服务会等待watchdog结束才执行