Как использовать Rust shiplift с гипер-сервера - PullRequest
0 голосов
/ 10 мая 2019

Я пытаюсь написать простую программу на Rust, которая читает статистику Docker с использованием shiplift и представляет их как метрики Prometheus с использованием rust-prometheus .

Пример shiplift stats работает правильно сам по себе, и я пытаюсь интегрировать его в сервер как

fn handle(_req: Request<Body>) -> Response<Body> {
    let docker = Docker::new();
    let containers = docker.containers();
    let id = "my-id";
    let stats = containers
        .get(&id)
        .stats().take(1).wait();
    for s in stats {
        println!("{:?}", s);
    }
    // ...
}

// in main
let make_service = || {
    service_fn_ok(handle)
};

let server = Server::bind(&addr)
    .serve(make_service);

но похоже, что поток зависает навсегда (я не могу выдать сообщение об ошибке).

Я также пробовал тот же рефакторинг (используя take и wait вместо tokio::run) в примере shiplift , но в этом случае я получаю ошибку executor failed to spawn task: tokio::spawn failed (is a tokio runtime running this future?). tokio так или иначе требуется shiplift?

EDIT: Если я правильно понял, моя попытка не работает, потому что wait заблокирует tokio исполнителя, а stats никогда не даст результатов.

Ответы [ 2 ]

0 голосов
/ 12 мая 2019

В моем понимании того, как работает hyper, было довольно много ошибок.В основном:

  • если служба должна обрабатывать фьючерсы, не используйте service_fn_ok для ее создания (она предназначена для синхронных служб): используйте service_fn;
  • не используйтеwait: все фьючерсы используют одного и того же исполнителя, исполнение будет зависать вечно (в документах есть предупреждение, ну да ладно ...);
  • в качестве уведомлений ecstaticm0rse, hyper::rt::spawn может использоваться длячитать статистику асинхронно, вместо того, чтобы делать это в службе

Требуется ли Токио для Shiplift?

Да.Он использует hyper, который выдает executor failed to spawn task, если по умолчанию tokio исполнитель недоступен (работа с фьючерсами почти всегда требует исполнителя).

Вот минимальная версия того, с чем я закончил(Токио 0,1.20 и гипер 0,12):

use std::net::SocketAddr;
use std::time::{Duration, Instant};

use tokio::prelude::*;
use tokio::timer::Interval;

use hyper::{
    Body, Response, service::service_fn_ok,
    Server, rt::{spawn, run}
};

fn init_background_task(swarm_name: String) -> impl Future<Item = (), Error = ()> {
    Interval::new(Instant::now(), Duration::from_secs(1))
        .map_err(|e| panic!(e))
        .for_each(move |_instant| {
            futures::future::ok(())  // unimplemented: call shiplift here
        })
}

fn init_server(address: SocketAddr) -> impl Future<Item = (), Error = ()> {
    let service = move || {
        service_fn_ok(|_request| Response::new(Body::from("unimplemented")))
    };
    Server::bind(&address)
        .serve(service)
        .map_err(|e| panic!("Server error: {}", e))
}


fn main() {
    let background_task = init_background_task("swarm_name".to_string());
    let server = init_server(([127, 0, 0, 1], 9898).into());

    run(hyper::rt::lazy(move || {
        spawn(background_task);
        spawn(server);
        Ok(())
    }));
}
0 голосов
/ 10 мая 2019
API

shiplift является асинхронным, что означает wait(), а другие функции возвращают Future вместо блокировки основного потока до тех пор, пока результат не будет готов. Future на самом деле не будет выполнять ввод-вывод, пока не будет передан исполнителю. Вам нужно передать Future на tokio::run, как в примере, на который вы ссылались. Вы должны прочитать tokio docs , чтобы лучше понять, как писать асинхронный код в ржавчине.

...