Download - Reactor grails realtime web devoxx 2013
Guillaume Laforge @glaforge !
Groovy, Reactor, Grails & the realtime web
Stéphane Maldini@smaldini !
Stéphane Maldini
Consultant and Reactor committer at .
!
@smaldini
Reactor
Part 1
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Housekeeping
!
• Tweet questions during the presentation – @ProjectReactor
!
• Stop us anywhere if you have a question – There are no stupid questions, only stupid answers!
!4
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Reactive Architecture ?
!5
EVENT DRIVENAGILE
REACTIVE
SCALABLE
AVAILABLE
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Reactive Architecture ?
!6
• A good description is available on: http://www.reactivemanifesto.org/ !
• Functional Programming helps as it is stimulus based by nature !
• Groovy is a perfect candidate: Closures and DSL are first class citizen !
• Reactor completes the picture by providing abstractions
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Threading model matters
!7
• Context switching hurts performances
• Locking hurts performances
• Message passing hurts performances
• Blocking for a thread hurts performances
• Creating Threads needs memory
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Dealing with performances
!8
• Actors solve the locking and context switching issues by becoming state boxes • One thread assigned per consumer • One thread will ever access a property
• Non Blocking Programming solves thread creation and waiting issues by delaying logic • Callback will be executed when possible (Lazy) • Reallocate the blocking time to process something else
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — LMAX Disruptor and Ring Buffer
!9
• LMAX Disruptor deals with message passing issues • Based on a Ring Buffer structure • “Mechanical Sympathy” in Disruptor
!
• http://lmax-exchange.github.com/disruptor/files/Disruptor-1.0.pdf
• http://mechanitis.blogspot.co.uk/2011/06/dissecting-disruptor-whats-so-special.html
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Disruptor performances
!10
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Trisha’s pretty picture of Disruptor
!11
http://mechanitis.blogspot.co.uk/2011/07/dissecting-disruptor-writing-to-ring.html
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — In love with Disruptor
!12
• Reactor best performances are derived from LMAX Disruptor !
• LMAX Disruptor can be considered as an evolution of the Actor Model: !
• Still avoid locking and deals with context switching • Producer/Consumer decoupled • Add Pipelining, Batching and more
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — A foundation part of Spring IO
!13
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — What is it?
!
• Reactor is a distillation of other libraries and best-practices – Elements of other patterns and libraries
surface throughout Reactor's abstractions !
• http://stackoverflow.com/questions/16595393/akka-or-reactor
!14
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — What can I build with it?
• Reactor applications are reactive – Reactive Extensions in .NET – Netflix RxJava – Observer pattern
!
• Reactor applications route events based on a Selector – Like a routing topic, but can be any object – Regex, URI template, Class.isAssingableFrom, custom logic
!15
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Landscape
!16
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — What does it look like?
!17
def env = new Environment() !def reactor = Reactors.reactor().env(env).dispatcher(RING_BUFFER).get() !reactor.on($('topic')){ Event<String> ev -‐> println "Hello $ev.data" } !reactor.notify('topic', Event.wrap('John Doe'))
Create a reactor context
Trigger reactor ‘topic’ key
React on ‘topic’ events
Build a reactor parameter
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Selectors
• Selectors are the left-hand side of an equality comparison !
– A Selector can be created from any object using $(obj) (or the long form: Selectors.object(obj)) !
– A Selector can extract data from the matched key !
– Predicate<T> Selectors can be created to match on domain-specific criteria like header values
!18
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — RegexSelector
• A RegexSelector will match a String by executing the regex over it – R(“some.(.*)”) – Selectors.regex(“some.(.*)”)
!19
@glaforge — @smaldini / #DV13-rtweb
Reactor — RegexSelector
!
• A RegexSelector will match a String by executing the regex over it !
– R(“some.(.*)”) !
– Selectors.regex(“some.(.*)”)
!20
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — RegexSelector
!21
reactor.on(R('some.(.+)')){ Event<String> ev -‐> def s = ev.headers.get('group1') } !
reactor.notify('some.topic', Event.wrap('John Doe'))
Use a Regex Selector and capture group
Notify a simple String key to be matched
s will be ‘topic’
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — UriSelector
• A UriTemplateSelector will match a String by extracting bits from a URI !
– U(“/some/{path}”) – Selectors.uri(“/some/{path}”)
!22
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — UriSelector
!23
reactor.on(U('/some/**/{topic}')){ Event<String> ev -‐> def s = ev.headers['topic'] } !
reactor.notify('/some/to/topic', Event.wrap('John Doe'))
Use a URI Selector and capture fragment
Notify a simple String URI key to be matched
s will be ‘topic’
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Consumer, Function, Supplier, Predicate
!24
public interface Consumer<T> { void accept(T t); } !
public interface Supplier<T> { T get(); } !
public interface Function<T, V> { V apply(T t); } !
public abstract class Predicate<T> { boolean test(T t); }
Generic Callback
Object Factory
Map Operation
Filter Operation
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Streams
● Streams allow for composition of functions on data − Callback++ − Netflix RxJava Observable, JDK 8 Stream
!25
Stream<String> str = obtainStream() !
str.map{ it.toUpperCase() } .filter{ someCondition() } .consume{ s -‐> log.info "consumed string $s" }
Coerces to Predicate
Coerces to Function
Coerces to Consumer
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Promises
● Promises supersedes Java Future for composition − Share common functions with Stream − Stateful: only 1 transition allowed
!26
Promise<String> p = doLater() !
String s = p .onSuccess { s -‐> log.info "consumed string $s" } .onFailure { t -‐> log.error "$t.message" } .onComplete { log.info 'complete' } .await(5, SECONDS) !
p.map{ it.toUpperCase() }.consume{s -‐> log.info "UC: $s"} Block for a return value
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Processor● Thin wrapper around Disruptor RingBuffer − Converts Disruptor API to Reactor API − Uber fast performance for #UberFastData
!27
Processor<Buffer> proc !
Operation<Buffer> op = proc.prepare() op.get().append(data).flip() op.commit() !
proc.batch(512) { it.append(data).flip() }
Fill 512 slots and release once
@glaforge — @smaldini / #DV13-rtweb
Reactor — Spring
!
● Helpers to integrate Reactor into ApplicationContext !
− @EnableReactor for easy configuration !
− Wiring annotated handlers
!28
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Spring
!29
@Configuration @EnableReactor class ReactorConfiguration { !
@Bean Reactor input(Environment env) { Reactors.reactor().env(env) .dispatcher(RING_BUFFER).get() } !
@Bean Reactor output(Environment env) { Reactors.reactor().env(env) .dispatcher(RING_BUFFER).get() } }
Setup Environment
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Spring
!30
@Component class SimpleHandler { !
@Autowired Reactor reactor !
@Selector('test.topic') void onTestTopic(String s) { // Handle data } }
Inject reactor
Register consumer on @reactor
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Spring
● DispatcherTaskExecutor − Not really a high-scale TaskExecutor − Used to get Spring components running in same thread as
Reactor Consumers ● ConversionService integration ● PromiseHandlerMethodReturnValueHandler (!) ● ReactorSubscribableChannel
!31
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Groovy
● First class citizen language implementation − @CompileStatic ready − Prominent use of Closure as Consumers, Functions and
more − Operator overloading for elegant programming − Full Reactor system Builder
!32
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Groovy
!33
@CompileStatic def welcome() { reactor.on('greetings') { String s -‐> reply "hello $s" reply "how are you?" } ! reactor.notify 'greetings', 'Jon' ! reactor.send('greetings', 'Stephane') { println it cancel() } }
Works with Groovy 2 CompileStaticCoerce String to $(string)
Send data back using replyTo key header
Coerce data arg to Event.wrap(data)
Notify & Listen for repliesStop listening for replies
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Groovy Promises and Streams
!34
def promise = Promises.task { longStuff(); 1 } get() !
def transformation = promise | { it + 1 } transformation.await() == 2 !
def deferredStream = Streams.defer().get() (deferredStream & { it > 2 }) << { send(it) } !
deferredStream << 1 << 2 << 3 << 4
Build an async function
Pipe promise with the closure transformation
Filter the stream with the right hand closure
predicate
Add callback after filter
Send data
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Groovy Environment
!35
GroovyEnvironment.create { environment { defaultDispatcher = "test" !
dispatcher('test') { type = DispatcherType.SYNCHRONOUS } } }.environment()
Works with Groovy 2 @CompileStatic
Prepare an environment builder
which dispatcher to use when creating a reactor
Build a standard DispatcherReturn a ready to
use Environment
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Groovy Environment
!36
GroovyEnvironment.create { reactor('test1') { stream('test') { consume { ev-‐> log.info ev.data } } on('test') { reply it } } }
Build a named Reactor
Intercept the data stream coming by the
selector $(‘test’)
Stream builder
Attach inline consumers
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Extensive Awesomeness
● TCP Client/Server, with a Netty 4 implementation
● Buffer tools
● Sequencer support, for event ordering
● Work Queue support with OoB Java Chronicle implementation
● Log Appender
!37
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Roadmap
● 1.1 discussions − StateBox: a safe tool for concurrent writes
− Better Timer management
− Spring XD, Spring 4
− Exploring Distributed Reactors
● Voice your interest and your use-case here
!38
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Roadmap
● 1.1 discussions − HTTP helpers
− Improved RingBuffer API for multithreaded consumers
(slow consumers)
− More Groovy Love: Buffer, TCP, Processor, Time,
!39
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Uber Community Contribs
● Meltdown: A Clojure binding by @michaelklishin & @ifesdjeen
− https://github.com/clojurewerkz/meltdown
!40
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Reactor — Uber Community Contribs
!
● High Performance Couchbase ingestion by @daschl − http://nitschinger.at/Using-the-Reactor-Processor-for-High-
Performance-TCP !
● Benefits of using Reactor Processor, TCP and Batching facilities
!41
Demo
Grails
Part 2
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — The Age of Asynchronous
Fact #1: – HTTP request thread is critical path – Do the barely necessary for fast rendering – If it’s long*, do it in a separate thread
!44
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — The Age of Asynchronous
Fact #2: – Creating new Threads needs caution – Context switching hurts performances – Concurrent programming is tricky*
!45
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — The Age of Asynchronous
Fact #3: – Burden on your application is never constant – Scaling Up is a good start… – …And Scaling Out is only next
!46
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — The Age of Asynchronous
So I have to use background threads ? But using them might lead to issues and headaches ? And what if I really need to scale out ?
!47
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Reactive Programming Recap
• Adding a level of indirection : Driving your application with Events
• Laziness is key • Scale up/out by tweaking dispatching
!48
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — The Big Picture
!49
Events Bus
Publish/Subscribe
Service Consumer
Service Consumer
Service Consumer
Service Consumer
Service Consumer
Application
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — The Big Picture
!50
Events Bus
Publish/Subscribe
App
App
App
App
App
Cloud
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb !51
Introducing GRAILS-‐EVENTS plugin
BOOM!
worse slide ever™
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Origins : Platform-Core plugin
• An initiative to provide modern tools for grails development
!
• Among them: the Events API
• An abstraction used in a few Grails applications today to decouple logic from producers
!
• grails-events can be considered as Platform Core Events API 2.0
!52
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Why a new plugin ?
• New features. And quite a few. – Streaming data, Selectors, Queue
!
• Based on a new solid foundation – Reactor – Where Platform-core Events best ideas have leaked
!
• Semantic changes – But relatively straightforward migration path
!53
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Why a new plugin ?
• Lightweight, only focused on events
!
• Ready to be embedded in a future Grails version
• Complete the asynchronous story
• Could enable runtime plugin deployment
!54
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — So what Grails Events is about
• Grails Apps and Plugins can use Events to:
– Listen for plugins/app events
– Start simple with in-memory eventing (#uberfastdata)
– Do Asynchronous calls (default)
– Increase in flexibility if required
!55
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Installing Grails Events
• It’s a binary plugin (!) • Requires Grails 2.2+
!56
repositories { //... mavenRepo "http://repo.springsource.org/libs-‐snapshot" mavenRepo "http://repo.grails.org/grails/libs-‐snapshots-‐local/" } !
dependencies { compile 'org.grails.plugins:events:1.0.0.BUILD-‐SNAPSHOT' }
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Semantics: Consumer
• A Consumer:
– Accepts an Event
– Is registered in a Service or Events artifact, or by calling on()
– Can be thread safe
• Depending on the dispatcher type
• Assuming the consumer is not registered more than once
!57
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Semantics: Selector
• A Selector:
– Matches an event key
– Is paired with a consumer during its registration
– Any bean method can be transformed into consumer with @Selector
!58
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Semantics: Reactor
• A Reactor: – Is a dedicated Consumer Registry – Has an assigned Dispatcher – Uses a specific Event Router
!
• Usually, if the Dispatcher doesn’t need to be adapted, reuse the default reactor grailsReactor
!59
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Pattern Examples : Event Driven CQRS
!60
GORM
http://martinfowler.com/bliki/CQRS.html
DBPostProcessingService
SaveService
Consume afterInsert events
Save a GORM entity
Insert RecordTrigger afterInsert
event
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Pattern Examples : Modular Architecture
!61
Notification Plugin
RequestServiceMain Application
NotificationService
Trigger ‘request’ event
Create a decoupled module Consume ‘request’ events
Core application untouched
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Pattern Examples : Background Processing
!62
REST service
Grails Controller HTTP Request
Return immediately
Request Service
Trigger ‘request’ event (async)
Long REST call
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Sending Events (grails-platform-core)
!63
def user = new User(params).save() !
event('mailRegistration', user) //event('mailRegistration', user).waitFor() //event topic:'mailRegistration', data:user //event topic:'mailRegistration', data:user, fork:false render(view:'sendingRegistrationMail')
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Sending Events (grails-events)
!64
def user = new User(params).save() !
event('mailRegistration', user) event('mailRegistration', user) { if(it == ‘end') { cancel() } } !
// event key: 'mailRegistration', data: user !
render(view: 'sendingRegistrationMail')
Non blocking call to trigger app consumers
Do things on each reply
Map notation
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Consuming Events (grails-platform-core)
!65
class UserService{ @grails.events.Listener def mailRegistration(User user) { sendMail { to user.mail subject "Confirmation" html g.render(template:"userMailConfirmation") } } ! @grails.events.Listener(topic= "mailRegistration") def mailRegistration2(org.grails.plugin.platform.events.EventMessage msg) { sendMail{ to msg.data.mail subject "Confirmation" html g.render(template: "userMailConfirmation") } } }
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
class UserService{ @reactor.spring.annotation.Selector def mailRegistration(User user){ sendMail{ to user.mail subject "Confirmation" html g.render(template: "userMailConfirmation") } } ! @reactor.spring.annotation.Selector("mailRegistration") def mailRegistration2(reactor.event.Event msg){ sendMail{ to msg.data.mail subject "Confirmation" html g.render(template:"userMailConfirmation") } } }
Grails — Consuming Events (grails-events)
!66
Consume on Selector(method name)
Consume on this specific topic
Event typed signature to inspect enveloppe
(‘headers…)
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
includes = ['default'] !doWithReactor = { reactor(EventsApi.GRAILS_REACTOR) { on('someTopic') { reply 'test' } } reactor(‘someGormReactor') { dispatcher = new SynchronousDispatcher() ext 'gorm', true ! stream { consume { log.info "Some gorm event is flowing with data $it.data" }.when(Throwable) { log.error "Ow snap!", it } } } }
Grails — A new Artifact conf/XxxxEvents
!67
Merge with DefaultsEvents
Build a GroovyEnvironment
Reactor Groovy Builders
Grails extension: accept GORM events
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Reactor awesomeness : Selectors
• Listen for arbitrary keys • import static reactor.event.selector.Selectors.*
!68
on(T(SpecificClassType)) { reply it } !
event(new SpecificClassType(), 'data') !
!
on(uri(‘/some/{captureIt}')) { reply it.headers.captureIt } !
event('/some/hourray', 'data')
Listen for an URI
Listen for SpecificClassType
Send using right key type
Send using matching URI
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Reactor awesomeness : Stream API
• Reactive Extensions programming style • Avoid callback hell
!69
withStream { event(key: 'test', data: 1) }.when(Exception, { log.info "exception:$it" }).map { callExternalService(it) }.consume('clientKey', reactor('browser'))
If a consumer fails
Build a Stream to capture any reply
$(‘test’) consumers may reply
Transform result
Reroute to key ‘clientKey’ on Reactor ‘browser’
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Reactor awesomeness: Promise API
• Grails 2.3 Promises become a Reactor Promises • Benefits from Dispatcher overriding • Powerful once Combined with Consumers
!70
dispatcher(ReactorPromise.PromiseDispatcher) { type = DispatcherType.RingBuffer backlog = 512 }
task { def res = longProcessing() render res }
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Reactor awesomeness : Routing
• During event dispatching, consumers list is selected • Publish Subscribe is the default • Possible to assign different routing strategy
!71
reactor('test1') { routingStrategy 'round-‐robin' on('test') { reply '1' } on('test') { reply '2' } }
Will reply 1, 2, 1, 2, 1, 2…
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Extensibility
!
• Main extension points: – Dispatcher, Selector, Registry, EventRouter, Consumer
!
• Metadata in Reactor Events DSL: – ext(‘someExtension’, [ ‘doStuff ’: true ])
!72
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — GORM events
• GORM is now an extension – Using ext(‘gorm’, true) on any candidate reactor – Applicable Selectors: simple topic form (beforeInsert...) – A boolean reply is evaluated as a cancel directive
!73
reactor('someGormReactor'){ dispatcher = new SynchronousDispatcher() ext 'gorm', true } @Selector(reactor = 'someGormReactor')
@ReplyTo boolean beforeValidate(Book b){ false }
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb !74
GRAILS-‐EVENTS-‐PUSH pluginEventing over HTTP!
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Pattern Examples : “Realtime” web
!75
Browser
ResponseService
Grails Controller
Return immediately
RequestService
Trigger async event
Reply a new eventPush reply to browser: Websocket, SSE, long-polling…
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — An elegant solution to browser push
• Powered by Atmosphere 2
• Automatically picks an adapted protocol:
– WebSockets, ServerSideEvent, Streaming, Polling…
• Consumer bridges for server-to-client push
• Reactor bridge for client-to-server push
• Javascript library
!76
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Installing Grails Events Push
1. Install Grails Events plugin 2. Install Grails Events Push plugin
!77
grails.servlet.version = "3.0" grails.tomcat.nio = true !grails.project.dependency.resolution = { repositories { //... mavenRepo "https://oss.sonatype.org/content/repositories/snapshots" } dependencies { //... compile 'org.grails.plugins:events:1.0.0.BUILD-‐SNAPSHOT' } plugins { //... runtime ":jquery:1.10.2" runtime ":events-‐push:1.0.0.BUILD-‐SNAPSHOT" } }
Use a “long-connection” friendly tomcat configuration
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — grails-events-push Lifecycle 1 - Handshake
!78
Browser EventsPushHandler
Adapt Protocol and Open new persistent connection
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — grails-events-push Lifecycle 2 - Register
!79
Browser Reactor(s)
Bridge a Client Consumer with a remote Reactor Consumer
Consumer registered on all Reactor with ‘browser’ extension enabled
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — grails-events-push Lifecycle 3 - Notify
!80
Browser ‘browser’ Reactor
Notify any key
RequestService
RequestService consumes on matching Selector within the ‘browser’ Reactor
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — grails-events-push Lifecycle 4 - Consume
!81
Browser Reactor(s)
Push event (WS, SSE…)
ResponseService
Notify ‘browser’ enabled Reactor and match key
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Generating Simple Browsers bridges
!82
includes = 'push' !doWithReactor = { reactor('browser') { ext 'browser', [ 'control', 'move', 'fire', 'leave' ] } reactor(‘grailsReactor') { ext 'browser', ['sleepBrowser'] ! stream(‘sleepBrowser') { filter { it.data == 'no' } } } }
Bridge browser to these reactor consumers $(‘control),…
Conventional name to override ‘browser’ reactor
Bridge browser to this reactor consumer $(‘sleepBrowser)
Prevent data ‘no’ to be dispatched to browser bridges
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — React on server side events
!83
Inject grailsEvents.js (requires resources plugin)
Create a connection between the browser and
the current Grails app
Listen for $(‘afterInsert’) events
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Generating Advanced Browsers bridges
!84
includes = ['push'] !doWithReactor = { reactor(‘grailsReactor') { ext 'gorm', true ext 'browser', [ 'test': true, 'afterInsert': [ browserFilter: {m, r -‐> true } ], (R("sampleBro-‐.*")) : true, ] } reactor(EventsPushConstants.FROM_BROWSERS) { ext 'browser', [R("sampleBro-‐.*")] } }
Include PushEvents (configure a reactor for events from Browsers)
Listen on GORM events
Extension to bridge Browsers consumers to
Server-side pair
Override “From Browsers” reactor and bridge consumersEvents are only routed, from
Browsers to bridged Browsers
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — The Bad Stuff
• events-si : Events API on top of Spring Integration – Not here (just) yet
!
• events-vertx : Abandoned experiment – Working around Distributed Reactor
!
• Stream DSL could be optimized – Reducing the number of objects
!85
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Roadmap
• events: – Document, especially for Migration from Platform Core – Stick with latest awesome features from Reactor
• Still Many API to expose: Processors, Buffer, TCP, Queues, Sequencer…
!
• events-push -> events-atmosphere : – Add support for replyTo – extract to standalone module : reactor-atmosphere
!86
@glaforge — @smaldini / #DV13-rtweb@glaforge — @smaldini / #DV13-rtweb
Grails — Roadmap
• events-sockjs: – Involves Reactor work here
!
• events-si: – Supports new events plugin
!
• events-xx: – The plugin where you are the hero
!87
Demo
Summary
Part 4
@glaforge — @smaldini / #DV13-rtweb
Groovy, Reactor, Grails and the realtime web
• Embrace modern reactive architectures with Groovy, Reactor and Grails!
• Groovy is a versatile language, that enables development of concise and functional oriented applications
• Reactor fuels your asynchronous and reactive applications with its ultra fast dispatching engine and non-blocking model.
• Grails supports your today's and tomorrow's web app design, tooled with the right plugins you are prepared for responsive and interactive applications
!90