I made a small Genie App, and I created a Goose Attack with Rust that tests it. I used Julia 1.6.1.
The app is essentially (which is routes.jl)
using Genie.Router, Genie.Renderer, Genie.Exceptions, JSON3
route("/") do
serve_static_file("welcome.html")
end
route("/greeting", named = :greeting) do
"Welcome to Genie! This site is served by Julia $(Base.VERSION) with $(Threads.nthreads()) threads."
end
route("/random", named = :random) do
num = 1
if haskey(@params, :num)
num = parse(Int64, @params(:num))
end
JSON3.write(rand(Int, num))
end
I’m getting many errors like this in Genie
┌ Error: 2021-06-25 13:22:17 Base.IOError("stream is closed or unusable", 0)
â”” @ Genie.AppServer ~/.julia/packages/Genie/Uvtzb/src/AppServer.jl:93
The biggest difference is disabling logging of those “stream is closed or unusable” errors with e.g.
Logging.global_logger(NullLogger())
As a comparison: “normal” average request time in Genie is 47ms on my machine, whereas disabling the global logger gets it down to 21ms. The HTTP.jl server with logging took something like 35ms on average, IIRC. Not sure where the disparity comes from, tbh, but maybe Genie has some internal log handling.
HTTP server
using HTTP, Sockets, JSON, Logging
using HTTP.URIs: queryparams, URI
const ROUTER = HTTP.Router()
function serveWelcome(req::HTTP.Request)
return HTTP.Response(200, read(joinpath(@__DIR__, "welcome.html"), String))
end
function serveRandom(req::HTTP.Request)
num = parse(Int, get(queryparams(URI(req.target)), "num", "0"))
return HTTP.Response(200, JSON.json(rand(Int, num)))
end
HTTP.@register(ROUTER, "GET", "/", serveWelcome)
HTTP.@register(ROUTER, "GET", "/random", serveRandom)
Logging.global_logger(NullLogger())
HTTP.serve(ROUTER, Sockets.localhost, 8000)
I tried to put this HTTP server code in the root of my repo, and serve content from GenieApp/public(/welcome.html). I can get it to serve that html page, but all content it tries to load is broken.
using HTTP, Sockets, JSON3, Logging
using HTTP.URIs: queryparams, URI
const ROUTER = HTTP.Router()
function serveWelcome(req::HTTP.Request)
return HTTP.Response(200, "Welcome to Genie! This site is served by Julia $(Base.VERSION) with $(Threads.nthreads()) threads.")
end
function serveRandom(req::HTTP.Request)
num = parse(Int, get(queryparams(URI(req.target)), "num", "1"))
return HTTP.Response(200, JSON3.write(rand(Int, num)))
end
HTTP.@register(ROUTER, "GET", "/", serveWelcome)
HTTP.@register(ROUTER, "GET", "/random", serveRandom)
Logging.global_logger(NullLogger())
HTTP.serve(ROUTER, Sockets.localhost, 8000)
It’s still not working properly, though. I’m running on a Mac, which might affect things.
It’s in git.
Ok, that looks much better, no? If you disregard the 99th and 100th percentile (or compile your code by hitting each route manually before starting the benchmark) then you get pretty good times.
The normal speed is better, but I still get a lot of errors during execution.
I added a Python Flask app to the git repo. It also produces connection errors on the Rust side. This makes me think that this is a problem with my Mac.
Most curious. The Python Flask app stopped producing any errors when I totally turned off the Mullvad VPN client. This didn’t help the two Julia apps, which still produce errors.
With Genie 1.18 configured for production env (no Revise, no logging, etc) the timing on my machine is about identical with the timing for HTTP.jl (but both much worse than the ones on your server, I’m running on a Windows laptop):
Can you please try my Genie fork configured for prod on your server?
I’m not sure about the errors, seem to be coming from Rust, Goose outputs a lot of:
21:58:29 [WARN] "/": error sending request for url (http://127.0.0.1:8000/): error trying to connect: tcp connect error: Only one usage of each socket address (protocol/network address/port) is normally permitted. (os error 10048)