voxxed days vienna - the why and how of reactive web-applications on the jvm

67

Upload: manuel-bernhardt

Post on 17-Jul-2015

243 views

Category:

Internet


1 download

TRANSCRIPT

Agenda1. Why Reactive?

2. Functional programming

3. Scala

4. Play

5. Akka

Who is speaking?

• freelance software consultant based in Vienna

• Vienna Scala User Group

• web, web, web

Who is speaking?

• freelance software consultant based in Vienna

• Vienna Scala User Group

• web, web, web

• writing a book on reactive web-applications

http://www.manning.com/bernhardt

Why Reactive?

Welcome to 2015

It's 2015!And yet we don't have flying cars...

But we have many-core CPUs

• Tilera, Cavium

• Adapteva Parallela

• Xeon PHI

• It's happening!

Too many cores?

• "640 kb ought to be enough for anybody" ~ Bill Gates

Too many cores?

• "640 kb ought to be enough for anybody" ~ Bill Gates

• "4 cores ought to be enough for anybody" ~ Linus Torvalds1

1 http://highscalability.com/blog/2014/12/31/linus-the-whole-parallel-computing-is-the-future-is-a-bunch.html

Programming with many cores

• serial approach does not work

• asynchronous programming with inappropriate tools does not work drives people insane

• we need (and already have) new abstractions

• we have to re-evaluate the use of our old abstractions

The problem with mutable state

car.setPosition(0);car.setPosition(10);

The problem with mutable state

The problem with mutable state

• there is no notion of time, only an illusion thereof

• changes to a mutable model only make sense locally if nobody is watching

• the larger the scope, the harder it gets to prevent inconsistencies

The problem with locks

• solution workaround for a broken conceptual model

• hard to reason about

• performance hit

Distribution is necessary *

* unless you have a lot of money

Let's make things even more complicated: programming with many nodes

• scaling out to handle large loads

• scaling out / replication to handle node failure

Let's make things even more complicated: programming with many nodes

• scaling out to handle large loads

• scaling out / replication to handle node failure

• problem: networks fail

Failure is inevitable

• Jepsen series3

• CAP theorem

• transactions don't really work that way in distributed systems

3 http://aphyr.com

Hard problem to solve

• Paxos: Consensus solving protocols

• CQRS: Command Query Responsibility Segregation

• CRDTs: Commutative Replicated Data Types

Functional programming to the

rescue

Imperative programming

In imperative programming you describe how something is done

Imperative programming

In imperative programming you describe how something is done

Example:List<User> users = ...List<User> minors = new ArrayList<User>();List<User> majors = new ArrayList<User>();for(int i = 0; i < users.size(), i++;) { User u = users.get(i); if(u.getAge() < 18) { minors.add(u); } else { majors.add(u); }}

Declarative programming

In declarative programming you describe what you want to get

Declarative programming

In declarative programming you describe what you want to get

Example:val (minors, majors) = users.partition(_.age < 18)

Declarative programming

In declarative programming you describe what you want to get

Example:val (minors, majors) = users.partition(_.age < 18)

Declarative programming is like driving with a GPS navigation system

You can do without, but it's so comfortable

Core concepts of FP• immutability

• functions

• transforming data with functions

Immutability

case class Car(brand: String, position: Int)

val car = Car(brand = "DeLorean", position = 0)val movedCar = car.copy(position = 10)

Immutability

case class Car(brand: String, position: Int)

val car = Car(brand = "DeLorean", position = 0)val movedCar = car.copy(position = 10)

Immutability

case class Car(brand: String, position: Int)

val car = Car(brand = "DeLorean", position = 0)val movedCar = car.copy(position = 10)

Functions and higher-order functionsval (minors, majors) = users.partition(_.age < 18)

Functions and higher-order functionsval isMinor = (age: Int) => age < 18

val (minors, majors) = users.partition(isMinor)

Functions and higher-order functionsval isMinor = (age: Int) => age < 18

val (minors, majors) = users.partition(isMinor)

Moving behaviour around instead of moving data around

Transforming dataval addresses = users.filter(_.age > 18) .map(_.address) .sortBy(_.city)

Goal: To build increasingly complex behaviour through a series of transformations / by composing functions

Compositiondef fetchUser(id: Long): Option[User] = ...def fetchCar(id: Long): Option[Car] = ...

val carPrice: Option[BigDecimal] = for { user <- fetchUser(42) car <- fetchCar(23)} yield { user.age + car.price}

Compositiondef fetchUser(id: Long): Future[User] = ...def fetchCar(id: Long): Future[Car] = ...

val carPrice: Future[BigDecimal] = for { user <- fetchUser(42) car <- fetchCar(23)} yield { user.age + car.price}

Compositiondef fetchUser(id: Long): Try[User] = ...def fetchCar(id: Long): Try[Car] = ...

val carPrice: Try[BigDecimal] = for { user <- fetchUser(42) car <- fetchCar(23)} yield { user.age + car.price}

Compositiondef fetchUser(id: Long): [User] = ...def fetchCar(id: Long): [Car] = ...

val carPrice: [BigDecimal] = for { user <- (42) car <- (23)} yield { user.age + car.price}

Maths FTW!

• Option, Future, Try all implement monadic operations2

• set of data structures following the same laws

• know one, know them all

• keeping things DRY

• also, it's not that scary

2 https://www.haskell.org/haskellwiki/Monad_tutorials_timeline

Scala

History

• Martin Odersky, EPFL

• first release in 2003

• Typesafe Inc.

Design goals• Full interoperability with Java

• Cut down boilerplate

• Pure object orientation & functional programming

• Move away from null

• Many-core programming

If I were to pick a language today other than Java, it would be

Scala.— James Gosling

Scala in Vienna

• Monthly Meetup

• http://scala-vienna.org

• Next meetup on 18th of February

Play

Play history

• MVC framework, inspired by RoR, Django, Symfony

• Zenexity

• first version released in 2009

• version 2.0 released in 2012, core rewritten in Scala

Design Principles• everything is compiled

• non-blocking I/O

• controller actions are functions (request => response)

• "share nothing" => horizontal scalability

Threaded servers

• like a train station with multiple tracks

• station chief decides which trains go on which platform

• if there are more trains than platforms, trains queue up

• if too many trains are queuing up, huge delays occur and passengers go home

Evented servers

• like a waiter in a restaurant

• runs back and forth between tables and the kitchen

• does only small tasks that do not take much time

• one server can each serve many tables at once

Advantages of the evented approach• less threads means less memory

• better CPU utilization (reduced context switching)

• (much) higher throughputs than threaded servers

History

• first release in January 2010

• based on the Actor model (Erlang)

• message-based asynchronous concurrency toolkit

• object-oriented programming done right

History

• first release in January 2010

• based on the Actor model (Erlang)

• message-based asynchronous concurrency toolkit

• object-oriented programming done right

• Akka is also a mountain in Sweden

Actors• lightweight objects

• send and receive messages (mailbox)

• can have children (supervision)

Sending and receiving messagescase class Script(text: String)

class AudreyHepburn extends Actor { def receive = { case Script(text) => read(text) }}

Sending and receiving messagescase class Script(text: String)

class AudreyHepburn extends Actor { def receive = { case Script(text) => read(text) }}

val audrey = ActorSystem.actorOf(Props[Audrey])

audrey ! Script(breakfastAtTiffany)

Supervision

class HollyCrazyCatLady extends Actor {

lazy val cats: ActorRef = context .actorOf[Cat] .withRouter( RoundRobinRouter(nrOfInstances = 42) )

}

Supervision

class HollyCrazyCatLady extends Actor {

lazy val cats: ActorRef = context .actorOf[Cat] .withRouter( RoundRobinRouter(nrOfInstances = 42) )

override def supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 3) { case t: Throwable => log.error("A cat had a problem!", t) Restart }}

Actors out there

• remoting

• clustering

• persistent actors � event sourcing

CQRS

• high level: separate writes & reads (performance)

• transform and store everything as events (write only)

• transform into query model in a separate store

Immutability (again!)

Summary

• many-core is here to stay

• FP is essential to take advantage of many-core systems

Summary

• many-core is here to stay

• FP is essential to take advantage of many-core systems

• Play and Akka make it possible to build web-applications that can scale in and out

Thank youhttp://www.manning.com/bernhardt

code mlbernhardt � 50% discount

Questions?http://scala-vienna.org � 18th of February

Join the dark side, we have free drinks