Description
I'm writing a reverse proxy and use the HTTP client to connect to any upstream server. Code is in https://github.com/klausi/rustnish/blob/goal-06/src/lib.rs#L150 . The server is leaking memory so I must be doing something wrong.
Steps to reproduce:
-
Make sure you have any upstream HTTP service running locally on port 80, for example the default Apache install on Ubuntu which will give you a dummy page at http://localhost/
sudo apt install apache2
-
Run rustnish reverse proxy on port 9090:
git clone --branch goal-06 [email protected]:klausi/rustnish.git cd rustnish cargo run --release
-
Get PID of rustnish process and memory usage:
ps aux | grep rustnish
The 6th column is the memory usage of the process, something like 20124 (~20MB)
-
Fire 1 million requests at the reverse proxy with Apache Bench:
ab -c 4 -n 1000000 http://localhost:9090/
(This takes ~130 seconds on my computer)
-
Check memory usage again:
ps aux | grep rustnish
The 6th column should show something like 284920, which is ~280MB!
So although we do not cache any requests or keep them around otherwise memory usage is growing without freeing up anymore.
One solution to the problem is this patch:
diff --git a/src/lib.rs b/src/lib.rs
index 520dd24..a591b99 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -170,7 +170,6 @@ pub fn start_server_background(
let http = Http::new();
let listener = TcpListener::bind(&address, &handle)
.chain_err(|| format!("Failed to bind server to address {}", address))?;
- let client = Client::new(&handle);
let server = listener.incoming().for_each(move |(sock, addr)| {
http.bind_connection(
@@ -180,7 +179,7 @@ pub fn start_server_background(
Proxy {
port: port,
upstream_port: upstream_port,
- client: client.clone(),
+ client: Client::new(&handle),
},
);
Ok(())
Avoiding the client.clone() call fixes the memory leak but degrades the runtime performance. Apache Bench with 1 million requests took 220 seconds instead of 130 seconds before. So the client_clone() call seems to be the right thing to do, but the clones are not dropped correctly when one request handling is done?