繁体   English   中英

我怎样才能从另一个线程使用hyper :: client?

[英]How can I use hyper::client from another thread?

我有多个线程执行一些繁重的操作,我需要在工作中使用客户端。 我使用Hyper v0.11作为HTTP客户端,我想重用连接,所以我需要共享相同的hyper::Client以保持打开连接(在keep-alive模式下)。

客户端在线程之间不可共享(它不实现SyncSend )。 这是一个包含我试过的代码的小片段:

let mut core = Core::new().expect("Create Client Event Loop");
let handle = core.handle();

let remote = core.remote();

let client = Client::new(&handle.clone());

thread::spawn(move || {

    // intensive operations...

    let response = &client.get("http://google.com".parse().unwrap()).and_then(|res| {
        println!("Response: {}", res.status());
        Ok(())
    });

    remote.clone().spawn(|_| {
        response.map(|_| { () }).map_err(|_| { () })
    });

    // more intensive operations...
});
core.run(futures::future::empty::<(), ()>()).unwrap();

此代码无法编译:

thread::spawn(move || {
^^^^^^^^^^^^^ within `[closure@src/load-balancer.rs:46:19: 56:6 client:hyper::Client<hyper::client::HttpConnector>, remote:std::sync::Arc<tokio_core::reactor::Remote>]`, the trait `std::marker::Send` is not implemented for `std::rc::Weak<std::cell::RefCell<tokio_core::reactor::Inner>>`

thread::spawn(move || {
^^^^^^^^^^^^^ within `[closure@src/load-balancer.rs:46:19: 56:6 client:hyper::Client<hyper::client::HttpConnector>, remote:std::sync::Arc<tokio_core::reactor::Remote>]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<std::cell::RefCell<hyper::client::pool::PoolInner<tokio_proto::util::client_proxy::ClientProxy<tokio_proto::streaming::message::Message<hyper::http::MessageHead<hyper::http::RequestLine>, hyper::Body>, tokio_proto::streaming::message::Message<hyper::http::MessageHead<hyper::http::RawStatus>, tokio_proto::streaming::body::Body<hyper::Chunk, hyper::Error>>, hyper::Error>>>>`
...
remote.clone().spawn(|_| {
               ^^^^^ the trait `std::marker::Sync` is not implemented for `futures::Future<Error=hyper::Error, Item=hyper::Response> + 'static`

有没有办法从不同的线程或其他方法重用相同的客户端?

简短的回答是否定的,但这样做更好。

每个Client对象都包含一个连接池。 以下是版本0.11.0中定义Hyper的Pool方法:

pub struct Pool<T> {
    inner: Rc<RefCell<PoolInner<T>>>,
}

由于inner是使用Rc引用计数并在运行时使用RefCell进行借用检查,因此该池肯定不是线程安全的。 当您尝试将该Client移动到新线程时,该对象将持有一个存在于另一个线程中的池,该线程本来就是数据争用的来源。

这种实现是可以理解的。 尝试跨多个线程重用HTTP连接并不常见,因为它需要同步访问主要是I / O密集型的资源。 这与Tokio的异步性质非常吻合。 在同一个线程中执行多个请求实际上更合理,让Tokio的核心负责发送消息并异步接收它们,而无需按顺序等待每个响应。 此外,计算密集型任务可以由futures_cpupool的CPU池执行。 考虑到这一点,下面的代码工作正常:

extern crate tokio_core;
extern crate hyper;
extern crate futures;
extern crate futures_cpupool;

use tokio_core::reactor::Core;
use hyper::client::Client;
use futures::Future;
use futures_cpupool::CpuPool;

fn main() {

    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let client = Client::new(&handle.clone());
    let pool = CpuPool::new(1);

    println!("Begin!");
    let req = client.get("http://google.com".parse().unwrap())
        .and_then(|res| {
            println!("Response: {}", res.status());
            Ok(())
        });
    let intensive = pool.spawn_fn(|| {
        println!("I'm working hard!!!");
        std::thread::sleep(std::time::Duration::from_secs(1));
        println!("Phew!");
        Ok(())
    });

    let task = req.join(intensive)
        .map(|_|{
            println!("End!");
        });
    core.run(task).unwrap();
}

如果没有收到响应太晚,输出将是:

Begin!
I'm working hard!!!
Response: 302 Found
Phew!
End!

如果您在单独的线程中运行多个任务,则问题变得开放,因为有多个架构可行。 其中之一是将所有通信委托给单个actor,因此要求所有其他工作线程将其数据发送给它。 或者,您可以为每个工作者提供一个客户端对象,因此也具有单独的连接池。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM