39
Reasonable RPC with Remotely ScalaDays, 18th March 2015 Rúnar Bjarnason Tim Perrett

Reasonable RPC with Remotely

Embed Size (px)

Citation preview

Page 1: Reasonable RPC with Remotely

Reasonable RPC with Remotely

ScalaDays, 18th March 2015

Rúnar Bjarnason Tim Perrett

Page 2: Reasonable RPC with Remotely
Page 3: Reasonable RPC with Remotely

Existing solutionsNo panaceas. Just tradeoffs.

Page 4: Reasonable RPC with Remotely

Monolith

• All functionality in a single binary

• Doesn’t scale

• Difficult to evolve and maintain

Page 5: Reasonable RPC with Remotely

Broker pattern

• Message bus or broker mediates between services

• Single point of failure/integration

• If broker goes down, the whole system is down

Page 6: Reasonable RPC with Remotely

Message-passing

• Lightweight and inexpensive

• Reliable and scales well

• Discovery is a pain

Page 7: Reasonable RPC with Remotely

Message passing solutions

• Finagle

• Thrift

• Akka

• HTTP*(JSON+XML)/REST

Page 8: Reasonable RPC with Remotely

Finagle

• Imperative API

• No reuse

• Too powerful

• No higher-order constructs

Page 9: Reasonable RPC with Remotely

Thrift

• All of the problems with Finagle

• Tough tooling

• Documentation is lacking

• No formal spec for wire format

Page 10: Reasonable RPC with Remotely

Akka

• Too powerful

• Untyped

• Actor system API bleeds to the outside

• Behavior of complex systems is unreasonable

Page 11: Reasonable RPC with Remotely

HTTP*(JSON+XML)/REST

• Unproductive, developer time wasted

• Marshalling/unmarshalling

• Defining HTTP endpoints and using HTTP clients

• Resources wasted on HTTP layer and parsing

• Does what we want, but at tremendous cost

Page 12: Reasonable RPC with Remotely

Our VisionProductivity, Safety, Reuse

Page 13: Reasonable RPC with Remotely

Productivity

Don’t want to think about:

• HTTP protocol specifics

• Every possible failure mode

• Serialization format

• Compatibility

Page 14: Reasonable RPC with Remotely

Safety

• Ad-hoc evolution doesn’t scale

• Incompatibilities should be compile-time failures

• Fail as early as possible

Page 15: Reasonable RPC with Remotely

Reuse

• Use an actual programming language

• Build protocols from modular primitives

• Compose large protocols from small ones

Page 16: Reasonable RPC with Remotely

RemotelyRPC for reasonable people

Page 17: Reasonable RPC with Remotely

RemotelyRPC for reasonable people

BETA

Page 18: Reasonable RPC with Remotely

Remotely

• Pilot for Verizon open-source

• Taking the time to build exactly what we want to use

• You can use it too!

Page 19: Reasonable RPC with Remotely

Remote call

val x = factorial(9)

Page 20: Reasonable RPC with Remotely

Remote reference

factorial: Remote[Int => Int]

Page 21: Reasonable RPC with Remotely

Applying a remote function

val x: Remote[Int] = factorial(9)

Page 22: Reasonable RPC with Remotely

Getting a response

val x: Response[Int] = factorial(9).run(endpoint)

Page 23: Reasonable RPC with Remotely

Response monad

type Demo[A] = Kleisli[Response, Endpoint, A]

implicit def remoteToK[A](r: Remote[A]): Demo[A] = Kleisli.ask[Response, Endpoint].flatMapK(r.run)

def call: Demo[(Movie,List[Actor])] = for { a <- MovieClient.getMovie("m2") b <- MovieClient.getActors(a) } yield (a,b)

Page 24: Reasonable RPC with Remotely

Running Responses

val address = new InetSocketAddress("10.1.0.1",8080)

val exe: Task[(Movie,List[Actor])] = for { transport <- NettyTransport.single(address) endpoint = Endpoint.single(transport) output <- call(endpoint)(Context.empty) _ <- transport.shutdown } yield output

val (movie, actors) = exe.run

Page 25: Reasonable RPC with Remotely

Demo

Page 26: Reasonable RPC with Remotely

Feature Pageant

Page 27: Reasonable RPC with Remotely

Binary Codecs

• Fast, lightweight binary serialization • Built on scodec • Ships with codecs for standard Scala types • Easy to make codecs for your own types

• Future goal: Fully automate codec-creation

Page 28: Reasonable RPC with Remotely

Pluggable transports

• Uses Netty 4 out of the box

• Akka I/O

• Netty 3

• ØMQ

Page 29: Reasonable RPC with Remotely

Pluggable transports

type Handler = Process[Task,BitVector] => Process[Task,BitVector]

Page 30: Reasonable RPC with Remotely

Endpoints

case class Endpoint( connections: Process[Task,Handler])

Page 31: Reasonable RPC with Remotely

Endpoint combinators

def circuitBroken: (Duration,Int,Endpoint) => Endpoint

def failoverChain: (Duration,Process[Task,Endpoint]) => Endpoint

Page 32: Reasonable RPC with Remotely

Context-passing

• Context provides arbitrary container for experiment propagation.

• Every request tagged with a UUID.

• Contains stack of request UUIDs.

• highlights the “path” through the system request graph when debugging.

Page 33: Reasonable RPC with Remotely

Pluggable monitoring

• Report round-trip latency timing.

• Trace every request uniquely.

• Trace the request stack entirely.

• Bring your own by implementing the Monitoring trait.

• Uses println out of the box.

Page 34: Reasonable RPC with Remotely

Capability checking

• Protocol versioning over time inevitable

• Determine client/server compatibility before establishing connection

Page 35: Reasonable RPC with Remotely

How fast is it?

• Went from 56 ms to 32 ms(production payload)

• In benchmarks on modest hardware

• Min: ~400 μs

• Mean: ~1.5 ms

• Throughput is an issue currently

Page 36: Reasonable RPC with Remotely

Spray I/O Netty 4

Page 37: Reasonable RPC with Remotely

Future work

• Prettier API

• Drastically improve throughput

• Extensible protocol language

• More built-ins for e.g. server-side map and flatMap

• Remote polymorphic functions

Page 38: Reasonable RPC with Remotely

Summary

• Remotely is productive

• It’s safe

• It promotes code reuse

• You can contribute!

Page 39: Reasonable RPC with Remotely

EOF@runarorama @timperrett

github.com/oncue/remotely github.com/oncue/remotely-demo