Hello Everyone,
I'm currently developing a basic reverse-proxy that should be running in multi-threaded mode for maximum performance. Yet I'm struggling a bit to make this work. I did simplify the code to make it more readable and shorter..
Can you point me to an example using jester + threads or have a tip for me how to solve it efficiently? Please see the compiler errors in the bottom of this post.
import jester, strtabs, asyncdispatch, os, strutils, json, threadpool
var cpucores = 4
let x: string = "test"
let settings = newSettings(port=Port(5000), staticDir=".", reusePort=true)
proc processLine(line: string) =
routes:
get "/":
resp("Hello World!")
get "/json":
resp("Please use POST-method on this path.")
get "/redirect":
redirect "http://192.168.0.1:80"
post "/json":
var push = parseJson($request.body)
resp ("You have just sent this JSON: " & $push)
post "/redirect":
var push = parseJson($request.body)
redirect "http://192.168.0.1:80"
status = Http200
runForever()
var i = 1
while i <= cpucores:
spawn processLine(x)
inc(i)
sync()
On compile i get this:
INFO Jester is making jokes at http://localhost:5000
INFO Jester is making jokes at http://localhost:5000
INFO Jester is making jokes at http://localhost:5000
INFO Jester is making jokes at http://localhost:5000
Error: unhandled exception: Address already in use [OSError]
Error: execution of an external program failed: '/root/src2/jester/jester_multi '
I would be very happy to get some help from you experienced nim coders! :)
Cheers, JohnnyC
It's probably best to implement multi-threading at the asynchttpserver-level (for which a PR is forthcoming) instead of jester-level.
It is odd that this doesn't work though. Perhaps reusePort has no effect in Jester.
try this, please make shure you use the freshest jester!
import jester # ./jester/jester # i had to clone the freshest jester
import strtabs, asyncdispatch, os, strutils, json, threadpool
var cpucores = 4
let x: string = "test"
# var settings = newSettings(port=Port(5000), staticDir=".") #,#reusePort = true)
proc processLine(line: string) =
settings:
port = Port(5000)
reusePort = true
appName = "/foo"
bindAddr = "127.0.0.1"
routes:
get "/":
resp("Hello World!")
get "/json":
resp("Please use POST-method on this path.")
get "/redirect":
redirect "http://192.168.0.1:80"
post "/json":
var push = parseJson($request.body)
resp ("You have just sent this JSON: " & $push)
post "/redirect":
var push = parseJson($request.body)
redirect "http://192.168.0.1:80"
status = Http200
runForever()
var i = 1
while i <= cpucores:
spawn processLine(x)
inc(i)
sync()
Wow enthus1ast - This has compiled flawless and is using all cores.
Thanks a lot for the heads up! Will post some performance benchmarks once finished :)
enthus1ast: Very interesting indeed. That really sounds like a massive idea! Will hit you up once I'm done with my application and we can see how to merge :)
Dom69: I'm trying to redirect a HTTP POST (json data) with jester, is that even possible? Or do I need to use httpclient or libcurl to pass the POST-data to the remotehost?
To redirect a POST, you could use HTTP code 307 (temporary redirect; preserves same http method).
See https://softwareengineering.stackexchange.com/questions/99894/why-doesnt-http-have-post-redirect and https://stackoverflow.com/questions/2068418/whats-the-difference-between-a-302-and-a-307-redirect
post "/..."
resp Http307, @[("Location", "http://example.com/redirected")], ""
Hey Everyone and thanks for your help boia01 and dom96!
@ boia01: I've read about the HTTP 307 meanwhile and also have implemented it. While it works good on HTTP Clients that support 307-code, some tools like curl or other APIs need special flags or are incompatible with this temporary redirect.
Thats why I've decided to read the post-data with jester, like dom96 has suggested and then pass it on to the httpclient library to HTTP-POST the $request.body to the remote ip.
discard client.postContent(remotehost, body=request.body)
Unfortunately, when benchmarking the application with wrk -t4 -c500 -d10s -s post.lua http://192.168.0.10:5000 and sending a json package located in post.lua, I get many of these errors:
Connection was closed before full request has been made
I've located the error msg coming from the httpclient
https://github.com/sheerun/nimjs/blob/master/lib/pure/httpclient.nim from line 250
I assume that some threads take longer than others and the client-pool of 4 clients gets exhausted and causes connections to drop while they are not finished sending post-data yet. I've tried to raise the threads altogether which does not give a performance boost but instead decreases the overall performance.
So the solution would be to either:
My current problem is that I seem not able to create 2 threadpools (one threadpool for jester, one threadpool for httpclient with a different amount of threads each). I would go for 4 jester-threads and maybe 1000 httpclient-threads but Im failing to do so...
Any suggestions concerning 1) or 2) ?
Yes, the client should wait for the response. The default timeout is "-1" (infinite).
I think that error message ("Connection was closed before full request has been made") is misleading because the error happens while the client is attempting to read the response from the server.
A lot of things can cause this:
First thing I would try to assess (before blaming the client) is whether the server is correctly sending a response. One way to do this could be to use a proxy or logging on the server-side.
Another thing -- without seeing your code it's hard to know -- make sure you are not sharing your HttpClient (across threads). You're mentioning a client-pool, but I'm not aware of a client pool in the standard library. Are you instantiating your client with var client = newHttpClient() ?