简体   繁体   English

Clojure消息处理/异步,多线程

[英]Clojure message handling / async, multithreaded

I have a small Clojure consumer/publisher receiving messages, processing them and sending them off to other consumers, all via RabbitMQ. 我有一个小的Clojure消费者/发布者接收消息,处理它们并将它们发送给其他消费者,所有这些都通过RabbitMQ。

I've defined a message-handler that handles messages in a separate thread (separate from the main thread that is). 我已经定义了一个消息处理程序,它在一个单独的线程中处理消息(与主线程分开)。 As can be seen in the code below, the thread synchronously receives and sends messages, all happening in an event loop started by the lcm/subscribe function. 从下面的代码中可以看出,线程同步接收和发送消息,所有这些都发生在由lcm / subscribe函数启动的事件循环中。

So, the question is, what would be the "Clojure way" to create a N-sized thread pool of these synchronous message-handlers? 那么,问题是,创建这些同步消息处理程序的N大小线程池的“Clojure方法”是什么? I guess the non-Clojure way would be to manually spawn a number of threads via Java interop. 我想非Clojure方法是通过Java interop手动生成许多线程。

Also, would that speed up message processing things at all, considering that the processing isn't very CPU intensive? 另外,考虑到处理不是CPU密集型的,那会加速消息处理吗? Would it be better to make these message-handlers async - again, considering that more time is spent publishing than processing? 将这些消息处理程序设置为异步会不会更好 - 考虑到发布时间比处理时间更长?

And finally, how would I go about measuring the performance of these contending approaches (I come from the Ruby/Javascript world, and there isn't any multithreading there)? 最后,我将如何衡量这些竞争方法的性能(我来自Ruby / Javascript世界,那里没有任何多线程)?

NOTE : I know all this could be avoided by just scaling horizontally and spawning more JVM processes listening to the message bus, but since the app is going to be deployed on Heroku, I'd like to use as much resources as possible in each dyno/process. 注意 :我知道所有这一切都可以通过横向扩展并产生更多JVM进程来监听消息总线来避免,但由于应用程序将部署在Heroku上,我想在每个dyno中使用尽可能多的资源/处理。

(defn message-handler
  [ch metadata ^bytes payload]
  (let [msg (json/parse-string (String. payload "UTF-8"))
        processed-message (process msg)] 
    (lb/publish ch "e.events" "" processed-message)))

(defn -main
  [& args]
  (let [conn          (rmq/connect {:uri (System/getenv "MSGQ")})
        ch            (lch/open conn)
        q-name        "q.events.tagger"
        e-sub-name    "e.events.preproc"
        e-pub-name    "e.events"
        routing-key   "tasks.taggify"]
    (lq/declare ch q-name :exclusive false :auto-delete false)
    (le/declare ch e-pub-name "fanout" :durable false)
    (lq/bind ch q-name e-sub-name :routing-key routing-key)
    (.start (Thread. (fn []
                       (lcm/subscribe ch q-name message-handler :auto-ack true))))))

On a more basic note... how would I go about refactoring this code to support registering the message-handler callback with an additional argument, like this: 更基本的说明......我将如何重构此代码以支持使用附加参数注册消息处理程序回调,如下所示:

    (.start (Thread. (fn []
                       (lcm/subscribe ch q-name (message-handler pub-name) :auto-ack true))))))

and then publish with a reference: 然后发布参考:

    (lb/publish ch pub-name "" processed-message)))

instead of a literal: 而不是文字:

    (lb/publish ch "e.events" "" processed-message)))

For the second part of the question, you can use partial application as shown below: 对于问题的第二部分,您可以使用部分应用程序,如下所示:

(defn message-handler
  [pub-name ch metadata ^bytes payload]
  (let [msg (json/parse-string (String. payload "UTF-8"))
        processed-message (process msg)] 
    (lb/publish ch pub-name "" processed-message)))



(.start 
  (Thread. 
     (fn []
       (lcm/subscribe ch q-name (partial message-handler e-pub-name) :auto-ack true))))))

这是一个非常大的主题,您可以考虑将此问题分解为几个不同的问题,但简明的答案是: 使用agents

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM