Как уже знают многие клоюристы, минималистичная лаконичность и ясность Клоюра могут принести пользу производительности и обслуживания для повседневных задач разработки, а также эстетическое блаженство и даже радость для нашей работы; Благодаря Pulsar вы также можете наслаждаться высокой производительностью волокон и улучшенной абстракцией параллелизма в традиционных приложениях Clojure. Недавно был выпущен Comsat 0.3.0, который также предоставляет возможности волокон для веб-разработки Ring: давайте кратко рассмотрим, что возможно.
Кольцо, в ближайшее время
В то время как рассекает API сервлетов мы уже имели освежающий взгляд на кольцо от моделирования и API точки зрения дизайна. Кольцо давно стало основой выбора для большинства веб-приложений и фреймворков Clojure.
На самом низком уровне веб-приложение Ring — это просто обработчик , то есть функция, преобразующая карту, представляющую запрос HTTP, в карту, представляющую ответ HTTP.
Поскольку необходимость выполнения предварительной и последующей обработки запросов является очень распространенной, Ring предлагает использовать промежуточное программное обеспечение , то есть функции высшего порядка, превращающие обработчик в новый обработчик с дополнительной логикой. Ring уже предоставляет некоторые промежуточные программы, например, для обслуживания файлов и ресурсов classpath или для обогащения карты запросов информацией из нескольких частей. Конечно, промежуточное программное обеспечение может быть связано в определенном порядке посредством обычной функциональной композиции, потому что то, что мы получаем, применяя промежуточное программное обеспечение к обработчику, является другим обработчиком, к которому затем может быть применено еще несколько промежуточных программ.
Наконец, Ring предоставляет на основе Jetty HTTP- адаптер для обработчиков. Помимо Ring есть несколько других адаптеров, но обычно любой из них будет функцией, получающей обработчик в качестве основного входа и карту дополнительных опций (например, для прослушивающих интерфейсов и портов, размеров пула потоков и т. Д.), Которые будут запускать бесконечное обслуживание HTTP. петля.
Основы: вставка волоконно-оптического адаптера Comsat
Оптоволоконный адаптер Comsat Ring основан на Jetty 9 и опирается на поддержку Servlet Async (доступно с Servlet 3.0). Для каждого запроса он порождает новое волокно и немедленно возвращается, так что дорогие серверные потоки освобождаются как можно скорее; волокно выполнит обработчик и передаст ответ позже, когда обработка и построение ответа будут завершены.
Волокна Comsat Ring порождается через Pulsar, поэтому обработчик и все вокруг применяются промежуточное программной она должна быть суспендируемыми : это можно сделать легко с помощью либо пульсар sfn
/ defsfn
макросов или suspendable!
функции. В качестве вежливости адаптер сделает это за вас в последнем обработчике, который передается.
Давайте перенесем следующее простое веб-приложение «Hello World» Ring на адаптер блокировки волокна:
(ns myapp
(:use ring.adapter.jetty))
(defn- hello-world [request]
(Thread/sleep 100)
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello World"})
(defn run [] (run-jetty hello-world {:port 8080}))`
Окончательный файл проекта Leiningen project.clj
будет выглядеть примерно так: мы включаем его co.paralleluniverse/comsat-ring-jetty9
в качестве зависимости, ring/ring-jetty-adapter
а не настраиваем инструмент инструментария Quasar для запуска:
(defproject myapp "0.1.0-SNAPSHOT"
:description "Comsat Ring Hello World example."
:min-lein-version "2.4.3"
:dependencies
[[org.clojure/clojure "1.6.0"]
[co.paralleluniverse/comsat-ring-jetty9 "0.3.0"]]
:main myapp.core/run
:java-agents [[co.paralleluniverse/quasar-core "0.6.2"]])
Прежде всего, немного измените ваши предложения use / require для использования адаптера блокировки волокна и объявите обработчик приостановленным; затем измените ваш режим блокировки потока на блокирующий волокно, просто чтобы убедиться, что обработчик действительно работает внутри волокна:
(ns myapp.core
(:use co.paralleluniverse.fiber.ring.jetty9)
(:require [co.paralleluniverse.pulsar.core :refer [sfn defsfn suspendable!]])
(:import (co.paralleluniverse.fibers Fiber)))
(defsfn hello-world [request]
(Fiber/sleep 1000)
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello World"})
(defn run [] (run-jetty hello-world {:port 8080}))
Это так просто: обработчик вашего существующего приложения теперь работает внутри эффективных волокон, а не потребляет дорогие потоки. lein run
будет обслуживать наш Hello World через порт 8080.
Применение промежуточного программного обеспечения
Давайте сделаем небольшой шаг вперед: что, если мы хотим применить промежуточное ПО? Это очень просто: мы собираемся использовать обычный многопоточный макрос ->
с дополнительной заботой о том, чтобы сделать приостановленным результат каждого приложения промежуточного программного обеспечения, чтобы run-jetty
запускать обогащенный обработчик, а не основной. Вот пример:
(ns myapp.core
(:use co.paralleluniverse.fiber.ring.jetty9
ring.middleware.file)
(:require [co.paralleluniverse.pulsar.core :refer [sfn defsfn suspendable!]])
(:import (co.paralleluniverse.fibers Fiber)))
(defn- fiber-sleep-middleware [h] #(do (Fiber/sleep 1000) ((suspendable! h) %)))
(defsfn hello-world [request]
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello World"})
(defn run [] (run-jetty
(-> hello-world
fiber-sleep-middleware
(wrap-file "public")
fiber-sleep-middleware)
{:port 8080}))
На самом деле есть еще несколько изменений, чем строго необходимо:
Fiber/sleep
Вызов теперь часть промежуточного слоя , который будет выполнять его перед вызовом обработчика, поэтому мы можем применить его вокруг любого из них. Кроме того, это сделает обработчик приостановленным, если это еще не сделано.run-jetty
оборачивает обработчик нашимfiber-sleep-middleware
, затем промежуточным программным обеспечением Ring, которое будет пытаться обслуживать статические файлы изpublic
каталога в нашем проекте (и делегировать внутреннему обработчику, если он не сможет их найти), затем снова сfiber-sleep-middleware
.
Давайте теперь добавим любой файл в наш public
каталог, например, некоторые testPage.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
</head>
<body>
<h1>Static test page!</h1>
</body>
</html>
Когда мы просматриваем http: // localhost: 8080, через пару секунд мы получаем наш красивый, блокирующий волокно, динамически генерируемый текст «Hello World», поскольку в этом случае все слои промежуточного программного обеспечения пересекаются; если вместо этого нажать http: // localhost: 8080 / testPage.html , промежуточное ПО для обслуживания файлов возьмет на себя и мы получим нашу «Статическую тестовую страницу!» title через одну секунду, так как внутренние обработчики больше не будут вызываться.
Использование сохраняющих кольцо структур маршрутизации
Некоторые микро-фреймворки предоставляют удобные средства, такие как маршрутизация, но при этом полностью используют концепции Ring. Например, Mustache предоставляет только один макрос, app
который будет использоваться в качестве препроцессора маршрутизации с существующими обработчиками Ring.
Этот шаблон Leinigen предоставляет отправную точку на основе Comsat-Ring и Mustache на стороне сервера, в то время как сторона браузера использует ClojureScript , core.async и Om .
Давайте посмотрим на основной серверный модуль:
(ns ring-sample.core
(:use co.paralleluniverse.fiber.ring.jetty9)
(:require
[co.paralleluniverse.pulsar.core :refer [sfn defsfn suspendable!]]
[ring.middleware.json :as midjson]
[ring.middleware.resource :as midres]
[net.cgrand.moustache :as moustache]
[ring.util.response :as ringres])
(:import (co.paralleluniverse.fibers Fiber)))
(def ^:private app-routes
(moustache/app
[] (sfn [_] (Fiber/sleep 100) (ringres/resource-response "index.html" {:root "public"}))
["widgets"] (sfn [_] (Fiber/sleep 100) (ringres/response [{:name "Widget 1"} {:name "Widget 2"}]))))
(def app
(-> app-routes
suspendable!
(midres/wrap-resource "/public")
suspendable!
(midjson/wrap-json-body)
suspendable!
(midjson/wrap-json-response)))
(defn run [] (run-jetty app {:port 8080}))
moustache/app
Макрос строит новый обработчик Ring , который будет маршрут тэ запрос на другие функции , основываясь на его URL, в этом случае дифференцирующего между корнем и «виджетами» путем. Мы просто определяем анонимные однопутевые обработчики как приостанавливаемые, а затем гарантируем, что те, которые созданы Mustache и обогащены промежуточным программным обеспечением, также являются приостановленными.
Интеграция других веб-фреймворков Clojure
Другие структуры на стороне сервера Clojure часто в конечном итоге создание Ring-совместимых обработчиков , но обеспечивают разработчику абстракций, которые отличаются от кольца, таких как Compojure «s маршрутов .
Using these frameworks in fiber-blocking mode may require some more work, as they can stack additional calls on top of the user-provided logic, all of which need to be made suspendable too. This can be as easy as adding a few suspendable!
statements but in other cases some more tweaking is needed as these calls can be inaccessible (e.g. anonymous functions). Compojure itself is a very popular choice but at present it is not supported out-of-the-box.
Pulsar’s automatic suspendables in the works
We have just realized that explicitly declaring suspendables can be as easy as adding a few statements or it can a bit trickier, especially when dealing with third-party libraries.
Luckily automatic instrumentation is in the works as part of Pulsar: this basically means that the only needed change will be replacing the call to your previous Ring adapter with a call to Comsat’s and your web application will then fly on high-performance fibers rather than running on threads. It will no longer be necessary to declare your functions (or protocols) as suspendable – any Clojure code will work seemlessly with fibers. If this sounds intriguing, stay tuned.
Enjoying both bliss and top-notch performance
To me, Parallel Universe’s stack together with Clojure is a developer’s dream coming true. Beauty and expression power finally don’t fight anymore with performance and scalability; on the contrary Quasar, Pulsar and Comsat bring unprecedented efficiency and best-of-breed scalability abstractions to the JVM at large and specifically to Clojure.
I hope you enjoyed having a quick taste of what’s possible; soon we’ll be publishing deeper explorations and more tutorials. In the meanwhile enjoy a highly maintainable, efficient, scalable and, last but not least, joyful Clojure coding experience with the Parallel Universe stack.