Open camsaul opened 4 years ago
Did you ever get to the bottom of this?
I'm not too familiar with the async workflow, however from a cursory glance, I have a hunch (unproven) that perhaps the connections in the async pool are not cleaning up properly (one per request).
One suggestion I have to try and uncover this is to create a single async connection manager and bind it in the request to see if that changes the # of active threads.
I think I'm seeing the same problem.
[clj-http "3.10.1"]
Here's the code I'm using:
; get number of threads and file descriptors
{:threads (Thread/activeCount)
:files (.getOpenFileDescriptorCount (ManagementFactory/getOperatingSystemMXBean))}
(def n 1)
; Sync
(dotimes [i n]
(clojure.core.async/thread
(println "starting request" i)
(try
(http/get "http://localhost:8080/delay")
(println "request" i "responded")
(catch Throwable e
(println "request" i "threw" e)))))
; Async
(dotimes [i n]
(println "starting request" i)
(http/get "http://localhost:8080/delay"
{:async? true}
(fn [r] (println "request" i "responded"))
(fn [e] (println "request" i "threw" e))))
When I first start the repl with lein repl
:
{:threads 11, :files 95}
With n=1 while the sync request is running:
{:threads 12, :files 98}
Then it goes back to:
{:threads 12, :files 97}
With n=1 while the async request is running:
{:threads 20, :files 126}
Then back to:
{:threads 11, :files 98}
With n=100, sync requests:
{:threads 111, :files 200}
then
{:threads 111, :files 100}
With n=100, async requests, only the first 33 get started. Then:
Execution error (IOException) at sun.nio.ch.IOUtil/makePipe (IOUtil.java:-2).
Too many open files
{:threads 308, :files 1024}
Ok, creating an async connection manager seems to solve it.
; get number of threads and file descriptors
{:threads (Thread/activeCount)
:files (.getOpenFileDescriptorCount (ManagementFactory/getOperatingSystemMXBean))}
(def n 100)
(def acm (clj-http.conn-mgr/make-reusable-async-conn-manager
{:threads n
:default-per-route n}))
(dotimes [i n]
(println "starting request" i)
(http/get "http://localhost:8080/delay"
{:async? true
:connection-manager acm}
(fn [r] (println "request" i "responded"))
(fn [e] (println "request" i "threw " e))))
After startup:
{:threads 11, :files 95}
After creating the async connection manager:
{:threads 20, :files 122}
While requests are running:
{:threads 20, :files 228}
After requests completed (and a few more seconds to wait for the cached connections to close):
{:threads 20, :files 128}
With n=800 it goes up to:
{:threads 20, :files 925}
From experimenting it looks like both :threads
and :default-per-route
are required to get all of the requests to run in parallel but :threads
doesn't seem to control the actual number of threads created.
but :threads doesn't seem to control the actual number of threads created.
For the purposes of testing something I've tried to issue 60 simultaneous long-running HTTP requests to our web server using the
async
functionality of this lib. However when issuing 30 or so requests they start throwing this Exception:I've also seen
When logging the number of active threads with
(Thread/activeCount)
it looks like each async request is opening a truly insane number of threads:Compare the same loop, using
So I guess the fact that it's opening so many threads is causing me to hit the
ulimit
for myjava
process. Any ideas why this might be happening?