In case people need it, I finally managed to make it work:
- the HTTP requests are handled by the CPU cores,
- I can see it in the logs,
- I can measure the improvement in how fast concurrent HTTP requests get answered.
A few notes first:
When I am talking about CPU cores I refer to what is displayed either
From what I see in Julia’s documentation a “process” is more or less the same thing as a “CPU core” (see Distributed.myid
). I use them indistinctively here.
The important functions/macros :
-
Sys.CPU_THREADS
tells us how many CPU cores the machine has (same as in /proc/cpuinfo). We can use this number to know how many processes we can add.
-
Distributed.addprocs
to add processes
-
@everywhere
to load all packages and project code (including the web APIs!..that was my mistake) in all the processes
-
@spawnat
to start the HTTP server on a given process
-
Distributed.myid()
allows us to check on which CPU core a piece of code is being executed.
I use julia 1.3.1 but it probably also works with older version because I only use functions from the Distributed package (not Base.Threads).
I use Mux.serve but that would also work with HTTP.serve
This being said here is a simplfied version of my main file
using Pkg
Pkg.activate(".")
using Revise # NOTE: In order for the web APIs to take into account changes
# you execute `@everywhere include("src/web-api-definition.jl")` (see below)
using Distributed
addprocs(Sys.CPU_THREADS - 1) # Add the number of CPU cores minus one (for the
# current process)
@everywhere using Distributed # Add Distributed to all processes because we
# want to use Distributed.myid() in the processes
@everywhere using Lib1, Lib2, LibX # Add packages
@everywhere push!(LOAD_PATH, "/home/myuser/CODE/MyLib.jl/") # Add your packages
using Mux, HTTP
@everywhere include("src/web-api-definition.jl") # This file contains the definition of the variable
# web_api used below (eg. `@app web_api = (...)`) see documentation
# of Mux. I executed
# Start a HTTP server on every available process. All HTTP servers listen
# on the same port, hence `reuseaddr = true`
for i in 1:nprocs()
@spawnat i Mux.serve(web_api, Mux.localhost, 8082
;reuseaddr = true)
end
Now, if you want to check that the HTTP requests are handled by the different processes, you can use
@info "Running on process[$(Distributed.myid()]"
To check what improvement it brings to the caller, I used curl calls. Side note, at first it seemed to me that there was no difference between having 1 CPU core and 8 CPU cores handling the requests but that was because the julia function called by the API was misconceived: I was using sleep(numOfSeconds)
in the function in order to simulate a function that takes some time to execute but sleep()
actually let the process go take care of another request. In order to simulate a long running function you need to keep the process busy, for example:
function doSomething(numOfSeconds::Float64)
@info "Running on process[$(Distributed.myid()]"
startTime = Dates.now()
diffRes = 0.0
counter = 0
while diffRes < numOfSeconds
counter+=1
diffRes = Dates.now() - startTime
diffRes = convert(Float64,diffRes / Millisecond(1000))
end
return string(counter)
end