Does Clojure Ring create a thread for each request?
Asked Answered
V

2

7

I am making a Messenger bot and I am using Ring as my http framework.

Sometime I want to apply delays between messages sent by the bot. My expectation would be that it is safe to use Thread/sleep because this will make the active thread sleep and not the entire server. Is that so, or should I resort to clojure/core.async?

This is the code I would be writing without async:

  (match [reply]

    ; The bot wants to send a message (text, images, videos etc.) after n milliseconds
    [{:message message :delay delay}] 
    (do
      (Thread/sleep interval delay)
      (facebook/send-message sender-id message))
    ; More code would follow...

A link to Ring code where its behaviour in this sense is clear would be appreciated, as well as any other with explanation on the matter.

Vassalize answered 5/7, 2017 at 18:34 Comment(0)
A
16

Ring is the wrong thing to ask this question about: ring is not an http server, but rather an abstraction over http servers. Ring itself does not have a fixed threading model: all it really cares about is that you have a function from request to response.

What really makes this decision is which ring adapter you use. By far the most common is ring-jetty-adapter, which is a jetty http handler that delegates to your function through ring. And jetty does indeed have a single thread for each request, so that you can sleep in one thread without impacting others (but as noted in another answer, threads are not free, so you don't want to do a ton of this regularly).

But there are other ring handlers with different threading models. For example, aleph includes a ring adapter based on netty, which uses java.nio for non-blocking IO in a small, limited threadpool; in that case, sleeping on a "request thread" is very disruptive.

Anurag answered 5/7, 2017 at 21:52 Comment(3)
Good explanation!Agripinaagrippa
I would add that using async handler allows to implement a delay without occupying a thread for the whole duration of the delay by scheduling response rendering for example in ScheduledExecutorService.Ame
Indeed, that is a big advantage of async. But you have to make sure to actually do it, rather than sleeping.Anurag
N
1

Assuming you're talking about code in a handler, Thread/sleep in Ring does make the thread for the request sleep. If you have multiple requests you are burning up expensive server threads.

The reason why Ring blocks is because the (non-async) model is based on function composition, where the result of one function is the output for another. So they have to wait, where exactly I can pinpoint this in the code I don't know.

Putting it in a go-block is better, because then you are not blocking server threads. It can return the response while you send the message. Do note that you cannot use results from the go block.

If you also want a response asynchronously (without blocking a server thread) you can for example use Pedestal.

For most servers synchronous handlers are sufficient, but if you are using Thread/sleeps AND want a response I would recommend using asynchronous Ring handlers or Pedestal or another framework.

Neel answered 5/7, 2017 at 19:27 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.