完全にノンブロッキングのClojureバックエンドWebアプリケーションをhttp-kitでまとめることができるかどうか疑問に思っています。
(実際には、リング互換のhttpサーバーであれば問題ありません。イベント駆動型の非ブロッキングモデルを 主張 しているので、http-kitについて言及しています)。
この質問は、非ブロッキング/非同期/イベント駆動型システムの性質について私が持っていたいくつかの誤解の兆候です。あなたが私と同じ場所にいる場合のために、ここにいくつかの説明があります。
(Node.jsのように)非ブロッキングであるというパフォーマンス上の利点を備えたイベント駆動型システムを作成できるのは、のすべて(たとえばほとんど)の場合のみです。あなたのIOはゼロからノンブロッキングの方法で処理されます。これは、すべてのDBドライバー、HTTPサーバー、およびクライアントを意味します、Webサービスなどはそもそも非同期インターフェースを提供する必要があります。特に:
今、具体的には:
私がそれを正しく理解した場合(そして私は専門家ではないので、間違った仮定に取り組んでいる場合は教えてください)、Webアプリケーションのそのようなノンブロッキングモデルの原則は次のとおりです。
私が見たところ、このモデルは Play Framework (Scala)および Node.js (JavaScript)プラットフォームでデフォルトでサポートされています。プログラムで非同期を管理するためのpromiseベースのユーティリティを使用します。
Compojureルーティングを使用して、Ringベースのclojureアプリでこれを実行してみましょう。 my-handle
関数を呼び出して応答を作成するルートがあります。
(defroutes my-routes
(GET "/my/url" req (my-handle req))
)
(def my-app (noir.util.middleware/app-handler [my-routes]))
(defn start-my-server! []
(http-kit/run-server my-app))
Clojureアプリケーションで非同期を管理する一般的に受け入れられている方法は、 core.async ライブラリを使用したCSPベースのようですが、これで問題ありません。したがって、上記の非ブロッキングの原則を採用したい場合は、次のようにmy-handle
を実装します。
(require '[clojure.core.async :as a])
(defn my-handle [req]
(a/<!!
(a/go ; `go` makes channels calls asynchronous, so I'm not really waiting here
(let [my-db-resource (a/thread (fetch-my-db-resource)) ; `thread` will delegate the waiting to "weaker" threads
my-web-resource (a/thread (fetch-my-web-resource))]
(construct-my-response (a/<! my-db-resource)
(a/<! my-web-resource)))
)))
のTimBaldridgeが示唆しているように、CPUを集中的に使用するconstruct-my-response
タスクはgo
-ブロックで実行されますが、外部リソースの待機はthread
-ブロックで実行されます。 /] core.async (38'55 '')のこのビデオ
しかし、それだけでは私のアプリケーションを非ブロッキングにするのに十分ではありません。ルートを通過してmy-handle
関数を呼び出すスレッドは、応答が作成されるのを待機しますか?
このHTTP処理を非ブロッキングにすることも(私が信じているように)有益でしょうか?もしそうなら、どうすればそれを達成できますか?
[〜#〜]編集[〜#〜]
Codemomentumが指摘しているように、リクエストをノンブロッキングで処理するために欠けている要素は、http-kitチャネルを使用することです。 core.asyncと組み合わせると、上記のコードは次のようになります。
(defn my-handle! [req]
(http-kit/with-channel req channel
(a/go
(let [my-db-resource (a/thread (fetch-my-db-resource))
my-web-resource (a/thread (fetch-my-web-resource))
response (construct-my-response (a/<! my-db-resource)
(a/<! my-web-resource))]
(send! channel response)
(close channel))
)))
これにより、非同期モデルを実際に採用できます。
これの問題は、リングミドルウェアとほとんど互換性がないことです。リングミドルウェアは、応答を取得するために関数呼び出しを使用します。これにより、本質的に同期されます。より一般的に言えば、イベントのトリガーは副作用があることを意味するため、イベント駆動型の処理は純粋な関数型プログラミングインターフェイスと互換性がないようです。
これに対処するClojureライブラリがあるかどうかを知ってうれしいです。
非同期アプローチを使用すると、準備中はスレッドをブロックするのではなく、準備ができたときにデータをクライアントに送信できます。
Http-kitの場合、ドキュメントに記載されている非同期ハンドラーを使用する必要があります。リクエストを適切な方法で非同期ハンドラーに委任した後、core.asyncなどを使用して好きなように実装できます。
非同期ハンドラのドキュメントはこちらです: http://http-kit.org/server.html#channel