如何通过RESTAPI实现一个长时间运行的进程,并在Rust中取得进展?

如何通过RESTAPI实现一个长时间运行的进程,并在Rust中取得进展?,rust,Rust,我是铁锈的初学者 我有一个长时间运行的IO绑定进程,我希望通过RESTAPI生成和监视该进程。我选择了这个,遵循这个。监测意味着取得进展和最终结果 当我生成它时,我给它一个id,并将该id映射到一个我可以获得进度的资源。我不必对进度一丝不苟;我可以报告5秒前的进度 我的第一次尝试是通过一个通道发送进度请求并接收状态。我被困在哪里存储接收器,因为在我的理解中它只属于一个线程。我想把它放在请求的上下文中,但这行不通,因为有不同的线程处理后续请求 在Rust中这样做的惯用方法是什么 我有一本书 以后编

我是铁锈的初学者

我有一个长时间运行的IO绑定进程,我希望通过RESTAPI生成和监视该进程。我选择了这个,遵循这个。监测意味着取得进展和最终结果

当我生成它时,我给它一个id,并将该id映射到一个我可以获得进度的资源。我不必对进度一丝不苟;我可以报告5秒前的进度

我的第一次尝试是通过一个通道发送进度请求并接收状态。我被困在哪里存储接收器,因为在我的理解中它只属于一个线程。我想把它放在请求的上下文中,但这行不通,因为有不同的线程处理后续请求

在Rust中这样做的惯用方法是什么

我有一本书

以后编辑

下面是一个自包含的示例,它遵循示例原则作为答案,即每个线程更新其进度的映射:

extern crate iron;
extern crate router;
extern crate rustc_serialize;

use iron::prelude::*;
use iron::status;
use router::Router;
use rustc_serialize::json;
use std::io::Read;
use std::sync::{Mutex, Arc};

use std::thread;
use std::time::Duration;
use std::collections::HashMap;

#[derive(Debug, Clone, RustcEncodable, RustcDecodable)]
pub struct Status {
    pub progress: u64,
    pub context: String
}

#[derive(RustcEncodable, RustcDecodable)]
struct StartTask {
    id: u64
}

fn start_process(status: Arc<Mutex<HashMap<u64, Status>>>, task_id: u64) {
    let c = status.clone();
    thread::spawn(move || {
        for i in 1..100 {
            {
                let m = &mut c.lock().unwrap();
                m.insert(task_id, Status{ progress: i, context: "in progress".to_string()});
            }
            thread::sleep(Duration::from_secs(1));
        }
        let m = &mut c.lock().unwrap();
        m.insert(task_id, Status{ progress: 100, context: "done".to_string()});
    });
}

fn main() {
    let status: Arc<Mutex<HashMap<u64, Status>>> = Arc::new(Mutex::new(HashMap::new()));
    let status_clone: Arc<Mutex<HashMap<u64, Status>>> = status.clone();

    let mut router = Router::new();

    router.get("/:taskId", move |r: &mut Request| task_status(r, &status.lock().unwrap()));
    router.post("/start", move |r: &mut Request|
        start_task(r, status_clone.clone()));

    fn task_status(req: &mut Request, statuses: & HashMap<u64,Status>) -> IronResult<Response> {
        let ref task_id = req.extensions.get::<Router>().unwrap().find("taskId").unwrap_or("/").parse::<u64>().unwrap();
        let payload = json::encode(&statuses.get(&task_id)).unwrap();
        Ok(Response::with((status::Ok, payload)))
    }

    // Receive a message by POST and play it back.
    fn start_task(request: &mut Request, statuses: Arc<Mutex<HashMap<u64, Status>>>) -> IronResult<Response> {
        let mut payload = String::new();
        request.body.read_to_string(&mut payload).unwrap();
        let task_start_request: StartTask = json::decode(&payload).unwrap();
        start_process(statuses, task_start_request.id);
        Ok(Response::with((status::Ok, json::encode(&task_start_request).unwrap())))
    }

    Iron::new(router).http("localhost:3000").unwrap();
}
外部板条箱铁;
外部板条箱路由器;
外部板条箱锈迹丛生;
使用铁:前奏曲::*;
使用铁:状态;
使用路由器::路由器;
使用rustc_serialize::json;
使用std::io::Read;
使用std::sync::{Mutex,Arc};
使用std::线程;
使用std::time::Duration;
使用std::collections::HashMap;
#[派生(调试、克隆、可信任、可信任)]
发布结构状态{
发布进度:u64,
发布上下文:字符串
}
#[衍生(可生锈、可生锈)]
结构StartTask{
id:u64
}
fn启动流程(状态:Arc,任务id:u64){
设c=status.clone();
线程::生成(移动| |{
因为我在1..100{
{
设m=&mut c.lock().unwrap();
m、 插入(任务id,状态{progress:i,上下文:“in progress.”到字符串();
}
线程::睡眠(持续时间::从秒(1));
}
设m=&mut c.lock().unwrap();
m、 插入(任务id,状态{progress:100,上下文:“done.”to_string()});
});
}
fn main(){
let状态:Arc=Arc::new(Mutex::new(HashMap::new());
让状态_克隆:Arc=status.clone();
让mut router=router::new();
router.get(“/:taskId”,move | r:&mut Request | task|u status(r,&status.lock().unwrap());
路由器.post(“/start”,move | r:&mut请求|
启动_任务(r,状态_clone.clone());
fn任务状态(请求:&mut请求,状态:&HashMap)->IronResult{
让ref task_id=req.extensions.get::().unwrap().find(“taskId”).unwrap_或(“/”).parse::().unwrap();
让payload=json::encode(&statuses.get(&task_id)).unwrap();
正常(响应::带有((状态::正常,有效负载)))
}
//通过POST接收消息并播放。
fn启动任务(请求:&mut请求,状态:Arc)->IronResult{
让mut payload=String::new();
request.body.read_to_string(&mut payload).unwrap();
让任务启动请求:StartTask=json::decode(&payload).unwrap();
启动流程(状态、任务启动请求.id);
Ok(响应::with((状态::Ok,json::编码(&task\u start\u request).unwrap()))
}
Iron::new(router).http(“localhost:3000”).unwrap();
}

您需要为每个请求线程注册一个通道,因为如果克隆
接收器是可能的,那么如果两个请求同时运行,响应可能/将以错误的线程结束


不要让线程创建一个应答请求的通道,而是使用一个。
future
允许您拥有对象的句柄,而对象还不存在。您可以更改输入通道以接收一个
承诺
,然后实现该承诺,无需输出通道。

一种可能性是使用全局
哈希映射
,将每个工作人员id与进度(和结果)相关联。下面是一个简单的例子(不包括其他内容)

#[宏使用]
外部板条箱(静态);
使用std::sync::Mutex;
使用std::collections::HashMap;
使用std::线程;
使用std::time::Duration;
懒惰的人!{
静态引用进度:Mutex=Mutex::new(HashMap::new());
}
fn设置进度(id:usize,进度:usize){
//如果存在旧值,“插入”将替换旧值。
PROGRESS.lock().unwrap().insert(id,PROGRESS);
}
fn获取进度(id:usize)->选项{
PROGRESS.lock().unwrap().get(&id).cloned()
}
fn工作(id:usize){
println!(“创建{}”,id);
设置进度(id,0);
因为我在0..100{
设置进度(id,i+1);
//模拟工作
线程::睡眠(持续时间::新建(0,50_000_000));
}
}
fn监视器(id:usize){
环路{
如果让一些人(p)=取得进展(id){
如果p==100{
println!(“完成{}”,id);
//为避免泄漏,请从进程中删除id。
//也许可以将任务结束保存在数据库中。
返回
}否则{
println!(“进度{}:{}”,id,p);
}
}
线程::睡眠(持续时间::新建(1,0));
}
}
fn main(){
让w=thread::spawn(| | work(1));
让m=thread::spawn(| |监视器(1));
w、 join().unwrap();
m、 join().unwrap();
}

谢谢您的回答。正如你们在我更新的问题中所看到的,这就是我最后所做的。我最初想到的是类似于演员的东西。我在Scala中使用actors完成了类似的事情,在Scala中,启动一个长时间运行的流程意味着生成一个actor(实际上是2个,一个做工作,另一个保持进度),我在后续的进度报告请求中查询该actor。我不知道我如何使用未来,因为我不需要一个结果。长时间运行的流程必须在请求时提供其进度,而不仅仅是最终结果。
#[macro_use]
extern crate lazy_static;

use std::sync::Mutex;
use std::collections::HashMap;
use std::thread;
use std::time::Duration;

lazy_static! {
    static ref PROGRESS: Mutex<HashMap<usize, usize>> = Mutex::new(HashMap::new());
}

fn set_progress(id: usize, progress: usize) {
    // insert replaces the old value if there was one.
    PROGRESS.lock().unwrap().insert(id, progress);
}

fn get_progress(id: usize) -> Option<usize> {
    PROGRESS.lock().unwrap().get(&id).cloned()
}

fn work(id: usize) {
    println!("Creating {}", id);
    set_progress(id, 0);
    for i in 0..100 {
        set_progress(id, i + 1);
        // simulates work
        thread::sleep(Duration::new(0, 50_000_000));
    }
}

fn monitor(id: usize) {
    loop {
        if let Some(p) = get_progress(id) {
            if p == 100 {
                println!("Done {}", id);
                // to avoid leaks, remove id from PROGRESS.
                // maybe save that the task ends in a data base.
                return
            } else {
                println!("Progress {}: {}", id, p);
            }
        }
        thread::sleep(Duration::new(1, 0));
    }
}

fn main() {
    let w = thread::spawn(|| work(1));
    let m = thread::spawn(|| monitor(1));
    w.join().unwrap();
    m.join().unwrap();
}