diff --git a/Cargo.toml b/Cargo.toml index bc279fe5..c97328cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ hyper = { version = "0.14", features = ["full"] } reqwest = { version = "0.11", features = ["json", "stream"] } serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.6.1", features = ["macros", "rt", "rt-multi-thread"] } -tower = { version = "0.4", features = ["util", "make", "timeout", "limit", "load-shed"] } +tower = { version = "0.4", features = ["util", "make", "timeout", "limit", "load-shed", "steer"] } tracing = "0.1" tracing-subscriber = "0.2" uuid = "0.8" diff --git a/src/service/mod.rs b/src/service/mod.rs index 891c11cc..997e4a76 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -24,7 +24,56 @@ //! # }; //! ``` //! +//! # Regarding backpressure and `Service::poll_ready` +//! +//! Generally routing to one of multiple services and backpressure doesn't mix +//! well. Ideally you would want ensure a service is ready to receive a request +//! before calling the service. However in order to know which service to call +//! you need the request... +//! +//! One approach is to not consider the router service itself ready until all +//! destination services are ready. That is the approach used by +//! [`tower::steer::Steer`]. +//! +//! Another approach is to always consider all services ready (always return +//! `Poll::Ready(Ok(()))`) from `Service::poll_ready` and then actually drive +//! readiness inside the response future. This works well when your services +//! don't care about backpressure and are always ready anyway. +//! +//! tower-web expects that most services used in your app wont care about +//! backpressure and so it uses the latter strategy. However that means you +//! should avoid routing to a service (or using a middleware) that _does_ care +//! about backpressure. At the very least you should [load shed] so requests are +//! dropped quickly and don't keep piling up. +//! +//! One possible approach is to only apply backpressure sensitive middleware +//! around your entire app. This is possible because tower-web applications are +//! themselves services: +//! +//! ```rust +//! use tower_web::prelude::*; +//! use tower::ServiceBuilder; +//! # let some_backpressure_sensitive_middleware = +//! # tower::layer::util::Identity::new(); +//! +//! async fn handler(request: Request) { /* ... */ } +//! +//! let app = route("/", get(handler)); +//! +//! let app = ServiceBuilder::new() +//! .layer(some_backpressure_sensitive_middleware) +//! .service(app); +//! ``` +//! +//! However when applying middleware around your whole application in this way +//! you have to take care that errors are still being dealt with appropriately. +//! +//! Also note that handlers created from async functions don't care about +//! backpressure and are always ready. So if you're not using any Tower +//! middleware you don't have to worry about backpressure. +//! //! [`Redirect`]: tower_http::services::Redirect +//! [load shed]: tower::load_shed use crate::{ body::{Body, BoxBody},