Closed lsk569937453 closed 7 months ago
Is there any more to the usage? I don't see log4rs
being used in the Rust code. Also, it is quite surprising that adding the logger would make any difference, since with hyper v1 we made tracing
an unstable feature, so it won't even emit log events unless you enabled that unstable feature (which is not easy to enable accidentally, by design).
Yeah,you are right.I have never used the log4rs in the code .But the adding of the log4rs in the Cargo.toml influence the performance.It is weird.I just suspect the dependencies of log4rs influence the other crates.
I have tried many times for the project.I found that the problem is that the anyhow of default feature decrease the performance of the hyper. code sample that causes the bug: Cargo.toml
[package]
name = "hyper_1"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "silverwind"
path = "src/main.rs"
[dependencies]
hyper = { version = "1.2.0", features = ["full"] }
tokio = { version = "1.36.0", features = ["full"] }
hyper-util = { version = "0.1.3", features = ["full"] }
bytes = "1"
http-body-util = { version = "0.1.0"}
anyhow = { version = "1.0.80" }
main.rs
use anyhow::anyhow;
use hyper::{server::conn::http1, service::service_fn};
use std::net::SocketAddr;
use tokio::net::TcpListener;
use bytes::Bytes;
use http_body_util::{combinators::BoxBody, BodyExt, Full};
use hyper::body::Incoming;
use hyper::{Request, Response, Uri};
use hyper_util::client::legacy::{connect::HttpConnector, Client};
use hyper_util::rt::TokioIo;
use std::convert::Infallible;
use tokio::runtime;
#[tokio::main]
async fn main() {
if let Err(err) = start_with_error().await {
println!("Failed to serve the connection: {:?}", err);
}
}
async fn check(uri: Uri) -> Result<(), anyhow::Error> {
let backend_path = uri.path_and_query().ok_or(anyhow!(""))?.as_str();
Ok(())
}
async fn do_req(
client: Client<HttpConnector, BoxBody<Bytes, Infallible>>,
req: Request<BoxBody<Bytes, Infallible>>,
) -> Result<Response<BoxBody<Bytes, Infallible>>, Infallible> {
let uri = req.uri().clone();
check(uri)
.await
.map_err(|_| -> Infallible { unreachable!() })?;
let response_incoming = client
.request(req)
.await
.map_err(|_| -> Infallible { unreachable!() })?;
let res = response_incoming
.map(|b| b.boxed())
.map(|item| item.map_err(|_| -> Infallible { unreachable!() }).boxed());
Ok(res)
}
async fn start_with_error() -> Result<(), Box<dyn std::error::Error>> {
let in_addr: SocketAddr = ([0, 0, 0, 0], 6667).into();
let out_addr_clone = "http://backend:8080";
let listener = TcpListener::bind(in_addr).await?;
println!("Listening on http://{}", in_addr);
println!("Proxying on http://{}", out_addr_clone);
let client = Client::builder(hyper_util::rt::TokioExecutor::new()).build(HttpConnector::new());
loop {
let (stream, _) = listener.accept().await?;
let io = TokioIo::new(stream);
let client_clone = client.clone();
let service = service_fn(move |mut req: Request<Incoming>| {
let client_clone1 = client_clone.clone();
let uri_now: hyper::http::uri::Uri = out_addr_clone.parse().unwrap();
*req.uri_mut() = uri_now.clone();
let req = req.map(|item| item.map_err(|_| -> Infallible { unreachable!() }).boxed());
do_req(client_clone1, req)
});
tokio::task::spawn(async move {
if let Err(err) = http1::Builder::new()
.preserve_header_case(true)
.title_case_headers(true)
.serve_connection(io, service)
.await
{
println!("Failed to serve the connection: {:?}", err);
}
});
}
}
If I change the anyhow = { version = "1.0.80"}
to anyhow = { version = "1.0.80",default-features = false}
.Everything will run well.
Another point is that the project will not use any other creates which use the anyhow of default feature,like log4rs,delay-timer,etc.Otherwise,it will decrease the performance of hyper.(In my docker container of 4Core-8GB RAM,the TPS decrease from 80000 to 40000)
Enabling or disabling the feature also shouldn't be causing such a problem. One possible case: when you add anyhow
, are you using that as the error type? Usage of Infallible
allows the compiler to remove a bunch of error-related code, which might cause a performance difference. There's no way it should be double.
And also, the unreachable!()
lines are not actually unreachable, errors can happen in plenty of places during networking/HTTP.
All in all, this doesn't seem like it's an issue with hyper itself. So, I'm going to close this issue.
I dont think so.If I change the Infallible to anyhow::Error like following,it will cause the same problem.
use anyhow::anyhow;
use hyper::{server::conn::http1, service::service_fn};
use std::net::SocketAddr;
use tokio::net::TcpListener;
use bytes::Bytes;
use http_body_util::{combinators::BoxBody, BodyExt, Full};
use hyper::body::Incoming;
use hyper::{Request, Response, Uri};
use hyper_util::client::legacy::{connect::HttpConnector, Client};
use hyper_util::rt::TokioIo;
use std::convert::Infallible;
use tokio::runtime;
#[tokio::main]
async fn main() {
if let Err(err) = start_with_error().await {
println!("Failed to serve the connection: {:?}", err);
}
}
async fn check(uri: Uri) -> Result<(), anyhow::Error> {
let backend_path = uri.path_and_query().ok_or(anyhow!(""))?.as_str();
Ok(())
}
async fn do_req(
client: Client<HttpConnector, BoxBody<Bytes, anyhow::Error>>,
req: Request<BoxBody<Bytes, anyhow::Error>>,
) -> Result<Response<BoxBody<Bytes, anyhow::Error>>, anyhow::Error> {
let uri = req.uri().clone();
check(uri).await.map_err(|err| anyhow!(err))?;
let response_incoming = client.request(req).await.map_err(|err| anyhow!(err))?;
let res = response_incoming
.map(|b| b.boxed())
.map(|item| item.map_err(|err| anyhow!(err)).boxed());
Ok(res)
}
async fn start_with_error() -> Result<(), Box<dyn std::error::Error>> {
let in_addr: SocketAddr = ([0, 0, 0, 0], 6667).into();
let out_addr_clone = "http://backend:8080";
let listener = TcpListener::bind(in_addr).await?;
println!("Listening on http://{}", in_addr);
println!("Proxying on http://{}", out_addr_clone);
let client = Client::builder(hyper_util::rt::TokioExecutor::new()).build(HttpConnector::new());
loop {
let (stream, _) = listener.accept().await?;
let io = TokioIo::new(stream);
let client_clone = client.clone();
let service = service_fn(move |mut req: Request<Incoming>| {
let client_clone1 = client_clone.clone();
let uri_now: hyper::http::uri::Uri = out_addr_clone.parse().unwrap();
*req.uri_mut() = uri_now.clone();
let req = req.map(|item| item.map_err(|err| anyhow!(err)).boxed());
do_req(client_clone1, req)
});
tokio::task::spawn(async move {
if let Err(err) = http1::Builder::new()
.preserve_header_case(true)
.title_case_headers(true)
.serve_connection(io, service)
.await
{
println!("Failed to serve the connection: {:?}", err);
}
});
}
}
Version hyper = { version = "1.2.0", features = ["full"] } tokio = { version = "1.36.0", features = ["full"] } hyper-util = { version = "0.1.3", features = ["full"] }
Platform Linux
code sample that causes the bug The demo source code is https://gist.github.com/lsk569937453/b42a8cfce21bd20c5da8737db1f5a1b1.
Description I use the hyper v1 as the http1 gateway.And I found the above code will cause performance problem.If I delete the crates of log4rs from the Cargo.toml.The QPS of the gateway could be 67000.But If I add the log4rs in the Cargo.toml like the above code ,the QPS of the gateway could be 40000.
[short summary of the bug]
I tried the following actions:
let backend_path = uri.path_and_query().ok_or(anyhow!(""))?.as_str();
.Everything runs good.let backend_path = uri.path_and_query().ok_or(anyhow!(""))?.as_str();
.Everything runs good.let backend_path = uri.path_and_query().ok_or(anyhow!(""))?.as_str();
.The gateway will have the lower performance.let backend_path = uri.path_and_query().ok_or(anyhow!(""))?.as_str();
in the hyper 0.14.xx.Everything runs good.The code is show here(https://gist.github.com/lsk569937453/6f8ac1db1cee9132b891c2263f8220fd).I expected to see this happen: The v1 version of hyper gateway should keep the same behaviour as the 0.14.xx.
Instead, this happened: The v1 version of hyper gatway has the lower QPS than the 0.14.xx version of hyper.