Introducing Immutant

79
Jim Crossley [email protected] Creative Commons BY SA 3.0 Monday, March 19, 2012

description

At Clojure/West 2012

Transcript of Introducing Immutant

Page 1: Introducing Immutant

Jim [email protected]

Creative Commons BY-SA 3.0

Monday, March 19, 2012

Page 2: Introducing Immutant

Agenda

• Who, what, WHY?• A fake, real-life application• How?• API deep-dive• Clustering, etc

Monday, March 19, 2012

Page 3: Introducing Immutant

Who?

• Jim Crossley - @jcrossley3• Toby Crawley - @tcrawley

• Bob McWhirter - Director of Polyglot• JBoss by Red Hat

Monday, March 19, 2012

Page 4: Introducing Immutant

What?

• An app server for Clojure• Inspired by TorqueBox• Powered by JBoss AS7• Started in September 2011

Monday, March 19, 2012

Page 5: Introducing Immutant

App Server?

• Multiple apps running in same JVM• Isolation via classloaders• Shared common services

Monday, March 19, 2012

Page 6: Introducing Immutant

Inspired by TorqueBox

• Make Ring, not War• Clojure interfaces to JBoss middleware

• No XML• Adapt AS7 to the Clojure development common practices

Monday, March 19, 2012

Page 7: Introducing Immutant

• Tomcat for web• HornetQ for messaging• Infinispan for caching• Quartz for scheduling• Distributed Transactions (XA)• Clustering• Management/Monitoring

AS7 is teh awesome!

Monday, March 19, 2012

Page 8: Introducing Immutant

Certified, fwiw

Java EE 6 Full Profile

Monday, March 19, 2012

Page 9: Introducing Immutant

Why?

• Reduce incidental complexity• Simplify deployment of non-trivial applications

• Simplify clustering to achieve high-availability

• Encapsulate JBoss services• Exploit the JVM

Monday, March 19, 2012

Page 10: Introducing Immutant

Polyglot

• Leverage the best tool on a single platform, regardless of language

• “ruby in the front, java in the middle, clojure in the rear”

• Red Hat is committed to it

Monday, March 19, 2012

Page 11: Introducing Immutant

Maybe a fit?

• If you’re already using Jetty, Memcached, RabbitMQ, and/or Cron

• If you have a polyglot organization, especially Ruby and Java

• If you must integrate with legacy Java EE

Monday, March 19, 2012

Page 12: Introducing Immutant

Let’s Get RealCRUD can only take you so far

Monday, March 19, 2012

Page 13: Introducing Immutant

Apps often require...

• More than one web interface, e.g. administration

• Background processing• Periodic maintenance• Dynamic data sources, e.g. REST• Services

Monday, March 19, 2012

Page 14: Introducing Immutant

But...

We’d still like to treat the integration of those components as a single, versioned application.

Monday, March 19, 2012

Page 15: Introducing Immutant

Consider...

• A tweet-analysis app• Filters the Twitter streaming API for brand keywords

• Scrapes URL’s from the tweets• Stores weighted results of sentiment analysis

Monday, March 19, 2012

Page 16: Introducing Immutant

Possible Components...

• A web app for displaying impressions• An admin app for defining users• The Twitter streaming client• The URL scraper• A housekeeper to purge old data

Monday, March 19, 2012

Page 17: Introducing Immutant

;;;; Mount the ring handlers(web/start “/” brandy.web/app)(web/start “/admin” brandy.web/admin)

The Web Apps

Monday, March 19, 2012

Page 18: Introducing Immutant

;;;; Where scrapers pick up their work(msg/start “/queue/tweets”)

;;;; The scrapers(msg/listen “/queue/tweets” brandy.msg/scrape)

The Message Queue

Monday, March 19, 2012

Page 19: Introducing Immutant

;;;; The work producer(daemon/start “streamer” brandy.stream/start brandy.stream/stop)

The Twitter Service

Monday, March 19, 2012

Page 20: Introducing Immutant

;;;; The housekeeper(job/schedule “housekeeper” “0 0 0 * * ?” brandy.job/purge)

The Recurring Job

Monday, March 19, 2012

Page 21: Introducing Immutant

;; The ring handlers(web/start “/” brandy.web/app)(web/start “/admin” brandy.web/admin)

;; Where scrapers pick up their work(msg/start “/queue/tweets”)

;; The scrapers(msg/listen “/queue/tweets” brandy.msg/scrape)

;; The work producer(daemon/start “streamer” brandy.stream/start brandy.stream/stop);; The housekeeper(job/schedule “housekeeper” “0 0 0 * * ?” brandy.job/purge)

immutant.clj

Monday, March 19, 2012

Page 22: Introducing Immutant

;; The ring handlers(web/start “/” brandy.web/app)(web/start “/admin” brandy.web/admin)

;; Where scrapers pick up their work(msg/start “/queue/tweets”)

;; The scrapers(msg/listen “/queue/tweets” brandy.msg/scrape)

;; The work producer(daemon/start “streamer” brandy.stream/start brandy.stream/stop);; The housekeeper(job/schedule “housekeeper” “0 0 0 * * ?” brandy.job/purge)

immutant.clj

Monday, March 19, 2012

Page 23: Introducing Immutant

;; The ring handlers(web/start “/” brandy.web/app)(web/start “/admin” brandy.web/admin)

;; Where scrapers pick up their work(msg/start “/queue/tweets”)

;; The scrapers(msg/listen “/queue/tweets” brandy.msg/scrape)

;; The work producer(daemon/start “streamer” brandy.stream/start brandy.stream/stop);; The housekeeper(job/schedule “housekeeper” “0 0 0 * * ?” brandy.job/purge)

immutant.clj

Monday, March 19, 2012

Page 24: Introducing Immutant

Immutanthow?

Monday, March 19, 2012

Page 25: Introducing Immutant

Leiningen Plugin

$ lein plugin install lein-immutant 0.6.0

Monday, March 19, 2012

Page 26: Introducing Immutant

Install

$ lein immutant install [version]

Monday, March 19, 2012

Page 27: Introducing Immutant

Install

By default, the plugin installs Immutant beneath

~/.lein/immutant/

Monday, March 19, 2012

Page 28: Introducing Immutant

Run

$ lein immutant run

Monday, March 19, 2012

Page 29: Introducing Immutant

Deploy

$ lein new myapp$ cd myapp

$ lein immutant init$ $EDITOR immutant.clj

$ lein immutant deploy

Monday, March 19, 2012

Page 30: Introducing Immutant

Deploy

A “deployment descriptor” is a glorified symlink written to jboss/standalone/deployments

Its contents::root "/the/path/to/your/app"

Monday, March 19, 2012

Page 31: Introducing Immutant

Deploy

Each app gets a dedicated Clojure runtime and classpath containing resolved dependencies, src, lib, and resources.

Monday, March 19, 2012

Page 32: Introducing Immutant

project.clj

(defproject myapp "1.2.3" ...

:immutant :init myapp.core/initialize :resolve-dependencies true :context-path "/" :swank-port 4111 :nrepl-port 4112)

Monday, March 19, 2012

Page 33: Introducing Immutant

Overlay

$ lein immutant overlay torquebox

$ export TORQUEBOX_HOME=$IMMUTANT_HOME

Monday, March 19, 2012

Page 34: Introducing Immutant

immutant namespaces

The API

Monday, March 19, 2012

Page 35: Introducing Immutant

The API• Dynamic by design

• No static xml files or annotations• Modules

• Web• Messaging• Caching • Scheduling• Daemons

Monday, March 19, 2012

Page 36: Introducing Immutant

ring

immutant.web

Monday, March 19, 2012

Page 37: Introducing Immutant

Web

• Supports any Ring-based handler• Ring middleware to expose the Servlet session

• Multiple web apps may share the same lifecycle via “subcontexts”

Monday, March 19, 2012

Page 38: Introducing Immutant

Web

(require ‘[immutant.web :as web])

(defn my-handler [request] :status 200 :headers "Content-Type" "text/html" :body "Hello from Immutant!")

(web/start "/hi" my-handler)

Monday, March 19, 2012

Page 39: Introducing Immutant

Context pathsproject.clj

(defproject myapp "1.2.3" ...)

immutant.clj

(web/start "/" main);;;; http://localhost:8080/myapp/

(web/start "/admin" admin);;;; http://localhost:8080/myapp/admin

Monday, March 19, 2012

Page 40: Introducing Immutant

Context pathsproject.clj

(defproject myapp "1.2.3” :immutant :context-path "/")

immutant.clj

(web/start "/" main);;;; http://localhost:8080/

(web/start "/admin" admin);;;; http://localhost:8080/admin

Monday, March 19, 2012

Page 41: Introducing Immutant

Session Replication

(require ‘[immutant.web :as web] ‘[immutant.web.session :as iws])

(web/start "/" (ring-session/wrap-session handler :store (iws/servlet-store)))

Monday, March 19, 2012

Page 42: Introducing Immutant

hornetq

immutant.messaging

Monday, March 19, 2012

Page 43: Introducing Immutant

Messagingpublish-subscribe

point-to-point

Monday, March 19, 2012

Page 44: Introducing Immutant

Messaging

(require ‘[immutant.messaging :as msg])

(msg/start "/queue/work")(msg/start "/topic/news")

Monday, March 19, 2012

Page 45: Introducing Immutant

Producers

;;;; Content may be any serializable;;;; data structure

(msg/publish “/queue/work” anything)

Monday, March 19, 2012

Page 46: Introducing Immutant

Producers

;;;; May be as complex as necessary

(msg/publish "/queue/work" :a "b" :c [1 2 3 :foo 42])

Monday, March 19, 2012

Page 47: Introducing Immutant

Producers

;;;; Support priority and time-to-live

(msg/publish “/queue/work” a-message :priority :high :ttl 1000)

Monday, March 19, 2012

Page 48: Introducing Immutant

Producers

;;;; Three message encodings:;;;; :clojure :json :text

(msg/publish "/queue/work" :a "b" :c [1 2 3 :foo 42] :encoding :json)

Monday, March 19, 2012

Page 49: Introducing Immutant

Messaging

With TorqueBox, messages comprised of standard collections and types are transparently interoperable between Clojure and Ruby in either direction.

Monday, March 19, 2012

Page 50: Introducing Immutant

Consumers

;;;; Block until message arrives

(let [task (msg/receive "/queue/work")] (perform task))

Monday, March 19, 2012

Page 51: Introducing Immutant

Consumers

;;;; Create a lazy sequence of messages

(take 4 (msg/message-seq “/queue/foo”))

Monday, March 19, 2012

Page 52: Introducing Immutant

Consumers

(defn upper-caser [s] (msg/publish "/topic/upper" (.toUpperCase s)))

;;;; Register a listener(msg/listen "/queue/lower" upper-caser)

Monday, March 19, 2012

Page 53: Introducing Immutant

Messaging

Automatic retries, failover and load-balancing when clustered.

Monday, March 19, 2012

Page 54: Introducing Immutant

infinispan

immutant.cache

Monday, March 19, 2012

Page 55: Introducing Immutant

Distributed Cache

• Backed by Infinispan data grid• Implements core Clojure interfaces•core.cache/CacheProtocol• core.memoize store

Monday, March 19, 2012

Page 56: Introducing Immutant

Infinispan

• Eviction: FIFO, LRU• Expiration: time-to-live, idle time• Persistence: write-through/behind• Transactional: JTA/XA• MVCC-based concurrency• Flexible clustering

Monday, March 19, 2012

Page 57: Introducing Immutant

Cache Modes

• :local - non-clustered• Clustered

• :invalidated - no data shared• :replicated - all data shared• :distributed - linear scalability

Monday, March 19, 2012

Page 58: Introducing Immutant

:invalidated

Monday, March 19, 2012

Page 59: Introducing Immutant

:replicated

Monday, March 19, 2012

Page 60: Introducing Immutant

:distributed

Monday, March 19, 2012

Page 61: Introducing Immutant

immutant.cache

• All caches have a name and a mode (:local, :invalidated, :replicated, :distributed)

• Like-named Immutant caches are backed by the same Infinispan cache

Monday, March 19, 2012

Page 62: Introducing Immutant

immutant.cache

(require ‘[immutant.cache :as ic])

(def a (ic/cache “foo” :replicated))(def b (ic/cache “bar”))(def c (ic/cache “foo”))

Monday, March 19, 2012

Page 63: Introducing Immutant

immutant.cache

(def c (ic/cache “foo”))(assoc c :a 1)(merge c :b 2 :c 3)(dissoc c :c)

c => :a 1, :b 2

Monday, March 19, 2012

Page 64: Introducing Immutant

Memoization

(require ‘[immutant.cache :as ic])

(def memoized-fn (ic/memo slow-fn “foo”))(memoized-fn 1 2 3)(def c (ic/cache “foo”))

c => [1 2 3] 42

Monday, March 19, 2012

Page 65: Introducing Immutant

Explicit writes

(require ‘[immutant.cache :as ic])

(def opts :ttl 2 :idle 1 :units :days)(def c (ic/cache “foo”))

(ic/put c key value opts)(ic/put-if-absent c key value opts)(ic/put-if-present c key value opts)(ic/put-if-replace c key old new opts)(ic/delete c key)

Monday, March 19, 2012

Page 66: Introducing Immutant

quartz

immutant.jobs

Monday, March 19, 2012

Page 67: Introducing Immutant

Scheduling

(require ‘[immutant.jobs :as job])

;;;; Once a month(job/schedule "newsletter" "0 0 0 1 * ?" news/monthly :singleton true)

Monday, March 19, 2012

Page 68: Introducing Immutant

Scheduling

Seconds Minutes Hours DOM Month DOW Year

0-59 0-59 0-23 1-31? L W

1-12JAN-DEC

1-7SUN-SAT? L #

1970-2099empty

0 */30 10-13 ? * FRI#3

“Fire every half hour from 10am until 1pm on the third Friday of each month”

Monday, March 19, 2012

Page 69: Introducing Immutant

Scheduling

(require ‘[immutant.jobs :as job])

(job/at “name” “3m” #(println “I fire after 3 minutes”))

(job/every “name” “1d” #(println “I fire once a day”))

Monday, March 19, 2012

Page 70: Introducing Immutant

long-running services

immutant.daemons

Monday, March 19, 2012

Page 71: Introducing Immutant

Daemons

(require ‘[immutant.daemons :as daemon])

(daemon/start "name" start-fn stop-fn)

Monday, March 19, 2012

Page 72: Introducing Immutant

Daemons(def response (atom nil))

(defn start [] (reset! response (twitter-statuses-filter (terms) url-scraper)))

(defn stop [] ((:cancel (meta @response))))

(daemon/start "tweets" start stop :singleton true)

Monday, March 19, 2012

Page 73: Introducing Immutant

Clusteringsimple horizontal scaling

Monday, March 19, 2012

Page 74: Introducing Immutant

Clustering

$ lein immutant run --clustered

Monday, March 19, 2012

Page 75: Introducing Immutant

Clustering

• Session replication• Robust, load-balanced message distribution

• HA Singleton• HA Jobs• Infinispan

Monday, March 19, 2012

Page 76: Introducing Immutant

mod_cluster

• An Apache module aware of JBoss• Reverse proxy via AJP or HTTP[S]• Uses load statistics and deployment availability for intelligent routing

• Optional session affinity• Failover

Monday, March 19, 2012

Page 77: Introducing Immutant

mod_cluster

Monday, March 19, 2012

Page 78: Introducing Immutant

Coming soon

• Distributed Transactions (XA)• OpenShift integration• Websockets• Better remote support• Domain Mode (cluster management)

Monday, March 19, 2012

Page 79: Introducing Immutant

http://immutant.org#immutant

@immutants

Monday, March 19, 2012