在对结构的引用上实现 hyper::Service
Implementing hyper::Service on a reference to a struct
我正在尝试为一个巨大的数据结构制作一个问答服务器。用户向服务器发送 JSON 个问题,服务器将使用庞大的数据结构来回答。
我正在尝试通过为我的 Oracle
结构实现 hyper::server::Service
特征来做到这一点。
我有这样的东西:
use self::hyper::server::{Http, Service, Request, Response};
// ...other imports
struct Oracle { /* Tons of stuff */}
impl<'a> Service for &'a Oracle {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, req: Request) -> Self::Future {
match (req.method(), req.path()) {
// could be lots of question types
(&hyper::Method::Post, "/query") => {
Box::new(req.body().concat2().map(|b| {
let query: Query = deserialize_req(&b.as_ref());
let ans = get_answer(&self, &query);
Response::new()
.with_header(ContentLength(ans.len() as u64))
.with_body(ans)
}))
},
_ => {
let response = Response::new()
.with_status(hyper::StatusCode::NotFound);
Box::new(futures::future::ok(response))
},
}
}
}
当我尝试将 &self
放在未来时,这会导致生命周期问题 (cannot infer an appropriate lifetime due to conflicting requirements
)。
我认为这是解决这个问题的完全错误的方法,但我很难找出最好的方法。
请注意,这些 futures 将是计算密集型的,运行 在 CPU pool 上使用它们并避免在异步单线程上 运行ning 它们是有意义的Tokio 核心堆栈。
call
中的 &self
是对调用站点管理的内存的引用。调用站点可能会在调用后立即释放该内存,或者在我们无法控制的其他时间释放该内存,因此将引用保存在闭包 ("closing over the reference") 中供以后使用是错误的。
为了以更利于共享的方式管理内存,您通常会使用引用计数指针。 Oracle
内存将由引用计数指针而不是调用站点拥有,允许您与闭包和线程自由共享 Oracle
。
如果你想并行处理这些未来,你需要一个线程安全的引用计数指针,例如Arc
。
要使用 Arc
,您可以将 call
变成一个免费函数:
fn call(oracle: Arc<Oracle>, req: Request) -> OracleFuture
或者用一个trait在指针上实现call
:
struct Oracle { /* Tons of stuff */}
type OraclePt = Arc<Oracle>;
trait OracleIf {
fn call(&self, req: Request) -> Self::Future
}
impl OracleIf for OraclePt {
fn call(&self, req: Request) -> Self::Future {
...
let oracle: OraclePt = self.clone();
Box::new(req.body().concat2().map(move |b| { // Close over `oracle`.
let query: Query = deserialize_req(&b.as_ref());
let ans = get_answer(&*oracle, &query);
Response::new()
.with_header(ContentLength(ans.len() as u64))
.with_body(ans)
}))
}
}
我们在这里关闭引用计数指针的副本。
如果您不喜欢使用引用计数指针的想法,那么另一种选择是使用 "scoped thread pool",一个保证子线程在父线程之前终止的线程池,使与子线程安全地共享 Oracle
引用成为可能。
如果不将计算包装在 Future
.
中,则可能更容易执行后者
我正在尝试为一个巨大的数据结构制作一个问答服务器。用户向服务器发送 JSON 个问题,服务器将使用庞大的数据结构来回答。
我正在尝试通过为我的 Oracle
结构实现 hyper::server::Service
特征来做到这一点。
我有这样的东西:
use self::hyper::server::{Http, Service, Request, Response};
// ...other imports
struct Oracle { /* Tons of stuff */}
impl<'a> Service for &'a Oracle {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, req: Request) -> Self::Future {
match (req.method(), req.path()) {
// could be lots of question types
(&hyper::Method::Post, "/query") => {
Box::new(req.body().concat2().map(|b| {
let query: Query = deserialize_req(&b.as_ref());
let ans = get_answer(&self, &query);
Response::new()
.with_header(ContentLength(ans.len() as u64))
.with_body(ans)
}))
},
_ => {
let response = Response::new()
.with_status(hyper::StatusCode::NotFound);
Box::new(futures::future::ok(response))
},
}
}
}
当我尝试将 &self
放在未来时,这会导致生命周期问题 (cannot infer an appropriate lifetime due to conflicting requirements
)。
我认为这是解决这个问题的完全错误的方法,但我很难找出最好的方法。
请注意,这些 futures 将是计算密集型的,运行 在 CPU pool 上使用它们并避免在异步单线程上 运行ning 它们是有意义的Tokio 核心堆栈。
call
中的 &self
是对调用站点管理的内存的引用。调用站点可能会在调用后立即释放该内存,或者在我们无法控制的其他时间释放该内存,因此将引用保存在闭包 ("closing over the reference") 中供以后使用是错误的。
为了以更利于共享的方式管理内存,您通常会使用引用计数指针。 Oracle
内存将由引用计数指针而不是调用站点拥有,允许您与闭包和线程自由共享 Oracle
。
如果你想并行处理这些未来,你需要一个线程安全的引用计数指针,例如Arc
。
要使用 Arc
,您可以将 call
变成一个免费函数:
fn call(oracle: Arc<Oracle>, req: Request) -> OracleFuture
或者用一个trait在指针上实现call
:
struct Oracle { /* Tons of stuff */}
type OraclePt = Arc<Oracle>;
trait OracleIf {
fn call(&self, req: Request) -> Self::Future
}
impl OracleIf for OraclePt {
fn call(&self, req: Request) -> Self::Future {
...
let oracle: OraclePt = self.clone();
Box::new(req.body().concat2().map(move |b| { // Close over `oracle`.
let query: Query = deserialize_req(&b.as_ref());
let ans = get_answer(&*oracle, &query);
Response::new()
.with_header(ContentLength(ans.len() as u64))
.with_body(ans)
}))
}
}
我们在这里关闭引用计数指针的副本。
如果您不喜欢使用引用计数指针的想法,那么另一种选择是使用 "scoped thread pool",一个保证子线程在父线程之前终止的线程池,使与子线程安全地共享 Oracle
引用成为可能。
如果不将计算包装在 Future
.