System Integration with Akka and Apache Camel

56
Connecting Akka to the Rest of the World System integration with Akka and Apache Camel by Martin Krasser

Transcript of System Integration with Akka and Apache Camel

Page 1: System Integration with Akka and Apache Camel

Connecting Akka to theRest of the World

System integration with

Akka and Apache Camel

by Martin Krasser

Page 2: System Integration with Akka and Apache Camel

About me• Freelance software engineer, architect

– eHealth application integration (Java, Scala, Groovy)– Bioinformatics, Cheminformatics (C++, Java)– …

• Open source committer– Akka– Camel– Scalaz-Camel– …

• Java since 1998• Scala since 2009

• Twitter: @mrt1nz

Page 3: System Integration with Akka and Apache Camel

Overview

• Introduction– Camel– Akka

• Akka-Camel integration– Consumer actors– Producer actors– Actor components

• Comparison– Camel routes– Akka-Camel routes– Scalaz-Camel routes

Page 4: System Integration with Akka and Apache Camel

Apache Camel

• Integration framework written in Java– Open source, Apache 2 license– http://camel.apache.org

• Enterprise Integration Patterns (EIP)– G. Hohpe, B. Woolf

• Domain-specific language (DSL)– Internal: Java, Scala– External: XML/Spring

• Pluggable protocol and API bindings– HTTP, FTP, JMS, File … and approx. 100 more– Camel “components” http://camel.apache.org/components.html

Page 5: System Integration with Akka and Apache Camel

Apache Camel

• Usage in Scala applications

– Camel Java/Scala DSL• http://camel.apache.org

– Akka-Camel integration module• http://akka.io/docs/akka-modules/1.1.2/modules/camel.html

– Scalaz-Camel DSL• https://github.com/krasserm/scalaz-camel

Page 6: System Integration with Akka and Apache Camel

Apache Camel

• Architecture

Page 7: System Integration with Akka and Apache Camel

Apache Camel

• Routing example

JMSEndpoint

HTTPEndpoint

Filter

D D

HTTP

http://example.org/docs

jms:queue:docs

Java DSL:

from("jms:queue:docs") .filter().xpath("/person[@name='Jon']") .to("http:example.org/docs");

Scala DSL:

"jms:queue:docs" when(_.in == "…") to("http:example.org/docs");

Page 8: System Integration with Akka and Apache Camel

Apache Camel

• Messaging endpoints– Configured via endpoint URI

• "jms:queue:docs"• "http://example.org/docs"• …

– Endpoint consumer: from(uri)– Endpoint producer: to(uri)

• Message processors– Configured via fluent API

• filter().xpath("…")• transform(body().append("…"))• …

– Custom Java/Scala code (Processor interface)

Page 9: System Integration with Akka and Apache Camel

Akka

• Platform for event-driven, scalable and fault-tolerant architectures on the JVM

• Concurrency– Actors, agents, dataflow, STM

• Scalability– Remote actors, cluster membership

• Fault tolerance– Local and remote actor supervision

CoreServices

Page 10: System Integration with Akka and Apache Camel

Akka• Add-on modules

– akka-camel • Camel integration (untyped actors)

– akka-camel-typed• Camel integration (typed actors)

– akka-kernel • Microkernel

– akka-scalaz• Scalaz support

– akka-spring • Spring integration

– …

Page 11: System Integration with Akka and Apache Camel

Akka• Actor model

– Mathematical model of concurrent computation– Carl Hewitt, 1973

• Akka actor– Encapsulates state– Encapsulates behavior– Exchanges messages– Mailbox– Event-driven– Single-threaded

• API– Scala– Java

Page 12: System Integration with Akka and Apache Camel

Akka

• Actor model benefits– Easier to reason about

– Raised abstraction level– Easier to avoid

• Race conditions

• Dead locks

• Starvation

• Live locks

Page 13: System Integration with Akka and Apache Camel

Akka Actors

• Receive messagesimport akka.actor.Actor

// actor definitionclass ExampleActor extends Actor { def receive = { case "test" => … case _ => … }}

// create actor reference (ActorRef)val actor = Actor.actorOf[ExampleActor]

// start actoractor.start

Page 14: System Integration with Akka and Apache Camel

Akka Actors

• Send messages

– Fire-Forget

actor ! "test"

Page 15: System Integration with Akka and Apache Camel

Akka Actors

• Send messages

– Send-And-Receive-Eventually

actor !! "test" match { case Some(result) => … // process result case None => … // timeout}

uses Future under the hood (with timeout)

Page 16: System Integration with Akka and Apache Camel

Akka Actors

• Send messages

– Send-And-Receive-Future

val future = actor !!! "test"future.awaitval result = future.get

returns Future directly

Page 17: System Integration with Akka and Apache Camel

Overview

• Introduction– Camel– Akka

• Akka-Camel integration– Consumer actors– Producer actors– Actor components

• Comparison– Camel routes– Akka-Camel routes– Scalaz-Camel routes

Page 18: System Integration with Akka and Apache Camel

Akka-Camel Integration

• Exchange messages with Akka actors via– HTTP, FTP, JMS, File … and approx. 100 more– Any Camel component can be used (pluggable)

• Integration layer of Akka– Consumer actors: receive message from Camel endpoints– Producer actors: produce messages to Camel endpoints– Message exchange patterns (MEPs): in-out and in-only

• Leverages Camel’s asynchronous routing engine– Asynchronous completion of message exchange– No threads blocked waiting for response messages, for example

Page 19: System Integration with Akka and Apache Camel

Consumer Actors

• Basic example – TCP consumer•

– Camel component: camel-mina– MEP: in-out

import akka.actor._import akka.camel._

class TcpConsumer extends Actor with Consumer { def endpointUri = "mina:tcp://localhost:6200?textline=true" def receive = { case Message(body, headers) => { self.reply("received %s" format body) } }}

Page 20: System Integration with Akka and Apache Camel

Consumer Actors

• Basic example – TCP consumer activation

– Asynchronous activation of consumer endpoint

import akka.actor._import akka.camel._

// Consumer actors require a running CamelServiceCamelServiceManager.startCamelService

// Activate TCP endpoint (asynchronously)Actor.actorOf[TcpConsumer].start

Page 21: System Integration with Akka and Apache Camel

Consumer Actors

• Basic example – TCP consumer activation

– Wait for endpoint activation

– Useful for testing purposes

import akka.actor._import akka.camel._

// Consumer actors require a running CamelServiceval service = CamelServiceManager.startCamelService

// Wait for TCP endpoint activationservice.awaitEndpointActivation(1) { Actor.actorOf[TcpConsumer].start}

Page 22: System Integration with Akka and Apache Camel

Consumer Actors

• Basic example – TCP consumer deactivationimport akka.actor._import akka.camel._

val service: CamelService = …val tcpConsumer: ActorRef = …

// deactivate TCP endpoint (asynchronously)tcpConsumer.stop

// … or wait for TCP endpoint deactivationservice.awaitEndpointDeactivation(1) { tcpConsumer.stop}

Page 23: System Integration with Akka and Apache Camel

Consumer Actors

• Basic example – HTTP consumer

– Camel component: camel-jetty– MEP: in-out– Jetty continuations used internally– Camel type converter (msg.bodyAs[…])

class HttpConsumer extends Actor with Consumer { def endpointUri = "jetty:http://localhost:8080/example" def receive = { case msg: Message => { self.reply("received %s" format msg.bodyAs[String]) } }}

Page 24: System Integration with Akka and Apache Camel

Consumer Actors

• Basic example – JMS consumer

– Camel component: camel-jms

– MEP: in-only

class JMSConsumer extends Actor with Consumer { def endpointUri = "jms:queue:example" def receive = { case Message(body, headers) => ... }}

Page 25: System Integration with Akka and Apache Camel

Consumer Actors

• Basic example – IMAP consumer

– Camel component: camel-mail– MEP: in-only

class MailConsumer extends Actor with Consumer { def endpointUri = "imap://[email protected]?password=secret" def receive = { case Message(body, headers) => ... }}

Page 26: System Integration with Akka and Apache Camel

Consumer Actors

• Basic example – Scheduled consumer

– Camel component: camel-quartz– MEP: in-only

class ScheduledConsumer extends Actor with Consumer { def endpointUri = "quartz://example?cron=0/2+*+*+*+*+?" def receive = { case tick => ... }}

Page 27: System Integration with Akka and Apache Camel

Consumer Actors

• Acknowledgements

– Camel component: camel-file– MEP: in-only– Application-level acknowledgement (Ack)

import akka.camel.Ack...

class FileConsumer extends Actor with Consumer { override def autoack = false // default is true def endpointUri = "file:messages/in?delete=true" def receive = { case Message(body, headers) => { // ... self.reply(Ack) // delete consumed file } }}

Page 28: System Integration with Akka and Apache Camel

Consumer Actors

• Failure replies

– Application-level negative acknowledgement (Failure)– Redelivery works also with JMS endpoints, for example– Failure replies can also be used with in-out MEP

import akka.camel.Failure...

class FileConsumer extends Actor with Consumer { override def autoack = false // default is true def endpointUri = "file:messages/in?delete=true" def receive = { case Message(body, headers) => { // ... self.reply(Failure(reason)) // redeliver file } }}

Page 29: System Integration with Akka and Apache Camel

Consumer Actors

• Failure replies

– Should not be made within receive

– Let consumer actors crash (on Exception)

– Use a supervisor for failure replies

Page 30: System Integration with Akka and Apache Camel

Consumer Actors• Supervised consumerclass SupervisedFileConsumer extends Actor with Consumer { override def autoack = false def endpointUri = "file:messages/in?delete=true" def receive = { case Message(body, headers) => { // if exception thrown: actor is restarted or stopped // else self.reply(Ack) // delete file } } override def preRestart(reason: scala.Throwable) { self.reply_?(Failure(reason)) // redeliver file } override def postStop() { self.reply_?(Failure(…)) // or Ack to delete file }}

Page 31: System Integration with Akka and Apache Camel

Consumer Actors• Supervised consumer

– Restart on any Exception– 5 restart attempts within 10 seconds

import akka.actor._ import akka.config.Supervision._

// Create consumer actor referenceval consumer = Actor.actorOf[new SupervisedFileConsumer]

// Create supervisor for consumer actorval supervisor = Supervisor( SupervisorConfig( OneForOneStrategy( List(classOf[Exception]), 5, 10000), Supervise(consumer, Permanent) :: Nil))

Page 32: System Integration with Akka and Apache Camel

Consumer Actors

• Simplified failure reporting– override def blocking = true

– Pros• No need for setting up a supervisor• No need to catch exception within receive• No need to self.reply(Failure(…))

– Cons• Endpoint communicates with actor via !!• Thread blocked waiting for Ack

Page 33: System Integration with Akka and Apache Camel

Consumer Actors

• Simplified failure reportingclass FileConsumer extends Actor with Consumer { override def blocking = true override def autoack = false def endpointUri = "file:messages/in?delete=true" def receive = { case Message(body, headers) => { // if exception thrown: endpoint receives it // else self.reply(Ack) // delete file } }}

Page 34: System Integration with Akka and Apache Camel

Typed Consumer Actors

• Basic example – HTTP/JMS consumerimport akka.actor._import akka.camel._import org.apache.camel.{Body, Header}

trait TypedConsumer { @consume("jetty:http://localhost:8080/example") // in-out def foo(s: String): String

@consume("jms:queue:example") // in-only def bar (@Body s: String, @Header("priority") p: Integer) : Unit}

class TypedConsumerImpl extends TypedActor with TypedConsumer { def foo(s: String) = "received %s" format s def bar(s: String, p: Integer) = println("received %s (priority = %d)" format(s, p))}

Page 35: System Integration with Akka and Apache Camel

Typed Consumer Actors

• Annotations

– Consumer endpoints•@consume(endpointURI)

– Parameter bindings•@Body•@Header•…

Page 36: System Integration with Akka and Apache Camel

Typed Consumer Actors

• Basic example – HTTP/JMS consumer activationimport akka.actor._import akka.camel._

val service = CamelServiceManager.startCamelService

// Activate HTTP and JMS endpoints (asynchronously)TypedActor.newInstance( classOf[TypedConsumer], classOf[TypedConsumerImpl])

// … or wait for HTTP and JMS endpoint activationservice.awaitEndpointActivation(2) { TypedActor.newInstance( classOf[TypedConsumer], classOf[TypedConsumerImpl]) }

Page 37: System Integration with Akka and Apache Camel

Producer Actors

• Basic example – HTTP producer

– Camel component: camel-jetty

– MEP: in-out– receive inherited from Producer trait– Jetty’s async HTTP client used internally

import akka.actor._import akka.camel._

class HttpProducer extends Actor with Producer { def endpointUri = "jetty:http://localhost:8080/example"}

Page 38: System Integration with Akka and Apache Camel

Producer Actors

• Basic example – HTTP producer activationimport akka.actor._import akka.camel._

// Producer actors require an initialized CamelContextCamelServiceManager.startCamelService

// activate producer actorval producer = Actor.actorOf[HttpProducer].start

// POST test message to http://localhost:8080/exampleproducer !! "test" match { case Some(m: Message) => … case Some(f: Failure) => … case None => …} // ! and !!! can also be used

Page 39: System Integration with Akka and Apache Camel

Producer Actors

• Basic example – JMS producer

– Camel component: camel-jms– MEP: in-only

class JmsProducer extends Actor with Producer { def endpointUri = "jms:queue:example" override def oneway = true}

Page 40: System Integration with Akka and Apache Camel

Producer Actors

• oneway = false (default)– Initiates an in-out ME with endpoint– Replies result to initial sender

• oneway = true– Initiates an in-only ME with endpoint– No reply to sender

• receiveAfterProduce– Change default reply behaviour

Page 41: System Integration with Akka and Apache Camel

Producer Actors

• Custom replies

class JmsProducer extends Actor with Producer { def endpointUri = "jms:queue:example" override def oneway = true override def receiveAfterProduce = { case m: Message => self.reply("enqueue succeeded") case f: Failure => self.reply("enqueue failed") } }

Page 42: System Integration with Akka and Apache Camel

Producer Actors

• Forward results

– Chaining of producer actors (pipelines)– Related: Future composition

class HttpProducer extends Actor with Producer { val target: ActorRef = … def endpointUri = "jetty:http://localhost:8080/example" override def receiveAfterProduce = { case msg => target forward msg } }

Page 43: System Integration with Akka and Apache Camel

Producer Actors

• Future composition

– Producer pipeline

val producer1 = Actor.actorOf[HttpProducer1].startval producer2 = Actor.actorOf[HttpProducer2].start

// monadic future composition (non-blocking)val future = for { m1: Message <- producer1 !!! Message("test") m2: Message <- producer2 !!! m1} yield m2

// blocks until result is availableval result: Message = future.get

Page 44: System Integration with Akka and Apache Camel

Actor Components

• Are Camel components– Can be used in any Camel route

• actor Camel component– Send messages to untyped actors– Provided by akka-camel module

• typed-actor Camel component– Send messages to typed actors– Provided by akka-camel-typed module– Extension of Camel’s bean component

• Used by akka-camel internally– Routes to consumer actors

Page 45: System Integration with Akka and Apache Camel

Actor Components

• actor endpoint URI– actor:uuid:[<actor-uuid>][?<params>]

• Parameters– autoack: Boolean

• System or application-level acknowledgements– blocking: Boolean

• Use ! or !! for sending messages to actor

• Supported message headers– CamelActorIdentifier

• Dynamic routing to actors

Page 46: System Integration with Akka and Apache Camel

Actor Components

• Example

– actor receives messages of type Message

import akka.actor._

// can be any actor (no need for Consumer)val actor = Actor.actorOf[SomeActor].start

// ...

// Camel route from JMS endpoint to actorfrom("jms:queue:example") .to("actor:uuid:%s?autoack=false" format actor.uuid)

Page 47: System Integration with Akka and Apache Camel

Overview

• Introduction– Camel– Akka

• Akka-Camel integration– Consumer actors– Producer actors– Actor components

• Comparison– Camel routes– Akka-Camel routes– Scalaz-Camel routes

Page 48: System Integration with Akka and Apache Camel

Comparison

• Criteria– Connectivity– Message processing– Route composition– Other

• High-level• Incomplete

Page 49: System Integration with Akka and Apache Camel

Camel Routes

• Connectivity– Camel components/endpoints

• Message processing– Processor interface

• Predefined processors (all known EIPs)• Custom processors

– Concurrent execution of Processor instances– Mutable messages

Page 50: System Integration with Akka and Apache Camel

Camel Routes

• Route composition– Camel DSL (Java, Scala, XML)

• Other– No built-in mechanism for distributing routes (except

via endpoints)– Distribution addressed by ServiceMix and

FuseSource Fabric, for example

Page 51: System Integration with Akka and Apache Camel

Akka-Camel Routes

• Connectivity– Camel components/endpoints managed by

• Consumer actors• Producer actors

• Message processing– Actor

• Predefined (a few in akka.actor.routing)• Custom

– Sequential execution of actor instance– Immutable messages

Page 52: System Integration with Akka and Apache Camel

Akka-Camel Routes

• Route composition– Wiring actors (low-level)– Future composition– …– No integration DSL (yet)

• Other– Easy to implementing stateful EIPs (aggregator,

resequencer …)– Strong built-in mechanisms for distribution, scalability

and fault-tolerance– Basis for a distributed and scalable Enterprise Service

Bus (?)

Page 53: System Integration with Akka and Apache Camel

Scalaz-Camel Routes

• Connectivity– Camel components/endpoints

• Message processing– Scala functions

• Predefined (some EIPs)• Custom

– Concurrent execution– Immutable messages

Page 54: System Integration with Akka and Apache Camel

Scalaz-Camel Routes

• Route composition– Based on functional programming concepts

• Message processors chained via Kleisli composition (>=>)

– Continuation-based approach• scala.Responder used as continuation monad

– Direct-style DSL

• Other – Configurable concurrency strategies– Camel processors can be re-used– Akka integration

Page 55: System Integration with Akka and Apache Camel

Scalaz-Camel Routes

• Example

– Details at https://github.com/krasserm/scalaz-camel

// custom message processorval validate: MessageProcessor = ...

// Kleisli composition of route (+ implicit conversions)val vroute = validate >=> oneway >=> to("jms:queue:valid") >=> { m: Message => m.setBody("order accepted") }

// consume messages via HTTP and error handling routesfrom(jetty:http://localhost:8080/orders) attempt { vroute } fallback { case e: ValidationException => { m: Message => ... } >=> failWith(e) case _ => ...}

Page 56: System Integration with Akka and Apache Camel

THANK YOU!