Async Microservices with Twitter's Finagle

37
Async Microservices with Finagle Vladimir Kostyukov http://vkostyukov.ru

description

Overview of Finagle Framework.

Transcript of Async Microservices with Twitter's Finagle

Page 1: Async Microservices with Twitter's Finagle

Async Microservices with Finagle

Vladimir Kostyukov http://vkostyukov.ru

Page 2: Async Microservices with Twitter's Finagle

2

Asynchronous Protocol-Agnostic

Full-Stack RPC

Page 3: Async Microservices with Twitter's Finagle

3

Adopters

Page 4: Async Microservices with Twitter's Finagle

4

Built atop of Netty (#1 Non-Blocking I/O for JVM)

Page 5: Async Microservices with Twitter's Finagle

5

Finagle - Netty in a functional setting

Page 6: Async Microservices with Twitter's Finagle

6

Building Blocks: Services + Futures + Filters

Page 7: Async Microservices with Twitter's Finagle

7

trait Service[-Req, +Rep] extends (Req => Future[Rep])

Page 8: Async Microservices with Twitter's Finagle

8

Service-Oriented rather then

Stream-Oriented

Page 9: Async Microservices with Twitter's Finagle

9

// TODO: Don't forget to remove it in production.object GetDeveloperPromotionDate extends Service[Developer, Date] { def apply(req: Developer) = Future.never} val date: Future[Date] = GetDeveloperPromotionDate(Developer.Myself)

Page 10: Async Microservices with Twitter's Finagle

10

1 val respond = new Service[HttpRequest, HttpResponse] { 2 def apply(req: HttpRequest) = { 3 val rep = new DefaultHttpResponse(HttpVersion.HTTP_1_1, 4 HttpResponseStatus.OK) 5 rep.setContentString(req.getUri()) 6 Future.value(rep) 7 } 8 } 9 10 Await.ready(Http.serve(":8080", respond)

Page 11: Async Microservices with Twitter's Finagle

11

Servers and Clients talk to each other via services

Page 12: Async Microservices with Twitter's Finagle

12

1 val to = Http.newService("google.com") 2 3 val proxy = new Service[HttpRequest, HttpResponse] { 4 def apply(req: HttpRequest) = to(req) 5 } 6 7 Await.ready(Http.serve(":8080", proxy)

Page 13: Async Microservices with Twitter's Finagle

13

Are there high-order services?

Page 14: Async Microservices with Twitter's Finagle

14

Client Service

Server ServiceFilter Filter Filter

Page 15: Async Microservices with Twitter's Finagle

15

// ReqIn -> Service(ReqOut -> RepIn) -> RepOuttrait Filter[-ReqIn, +RepOut, +ReqOut, -RepIn] extends ((ReqIn, Service[ReqOut, RepIn]) => Future[RepOut])

Page 16: Async Microservices with Twitter's Finagle

16

// A filter that does nothing.def identity[Req, Rep] = new Filter[Req, Rep, Req, Rep] { def apply(req: Req, service: Service[Req, Rep]) = service(req)} val auth = identity[HttpRequest, HttpResponse]

Page 17: Async Microservices with Twitter's Finagle

17

Filters are type-safe!

Page 18: Async Microservices with Twitter's Finagle

A Type-Safe Input Filter

18

1 object TurnAIntoB extends Filter[A, Rep, B, Rep] { 2 def apply(req: A, service: Service[B, Rep]): Future[Rep] = 3 service(req.toB) 4 } 5 6 val serviceOfB: Service[B, Rep] = ... 7 val serviceOfA: Service[A, Rep] = TurnAIntoB andThen serviceOfA

Page 19: Async Microservices with Twitter's Finagle

A Type-Safe Output Filter

19

1 object TurnAIntoB extends Filter[Req, B, Req, A] { 2 def apply(req: Req, service: Service[Req, A]): Future[B] = 3 service(req) map { a => a.toB } 4 } 5 6 val serviceOfA: Service[Req, A] = ... 7 val serviceOfB: Service[Req, B] = TurnAIntoB andThen serviceOfA

Page 20: Async Microservices with Twitter's Finagle

20

Case Study: TimeOut Filter 1 def timeout[Req, Rep](timeout: Duration)(implicit timer: Timer) = 2 new SimpleFilter[Req, Rep] { 3 def apply(req: Req, service: Service[Req, Rep]) = 4 service(req).within(timer, timeout) 5 } 6 7 val handleExceptions = new SimpleFilter[Req, Rep] { 8 def apply(req: Req, service: Service[Req, Rep]) = 9 service(req) handle { 10 case t: TimeOutException => Future.value(defaultRep) 11 } 12 } 13 14 val respond = new Service[Req, Rep] { ... } 15 // We guarantee 3 seconds response time. 16 val backend = handleExceptions andThen 17 timeout(3.seconds) andThen 18 respond

Page 21: Async Microservices with Twitter's Finagle

21

Case Study: OAuth2Filter 1 class OAuth2Filter[U](dataHandler: DataHandler[U]) 2 extends Filter[HttpRequest, HttpResponse, OAuth2Request[U], HttpResponse] 3 with OAuth2 with OAuthErrorHandler { 4 5 def handleError(e: OAuthError) = e.toHttpResponse 6 def apply(req: HttpRequest, service: Service[OAuth2Request[U], HttpResponse]) = 7 authorize(req, dataHandler) flatMap { authInfo => 8 service(OAuth2Request(authInfo, req)) 9 } handle { 10 case e: OAuthError => handleError(e) 11 } 12 } 13 14 val authorize = new OAuth2Filter(...) 15 val respond: Service[OAuth2Request[U], HttpResponse] = ??? 16 val backend: Service[HttpRequest, HttpResponse] = authorize andThen respond

Page 22: Async Microservices with Twitter's Finagle

22

Future is a monad!

Page 23: Async Microservices with Twitter's Finagle

Abstraction: Fibonacci Calculator

23

1 trait FibonacciCalculator { 2 val Zero = BigInt(0) 3 val One = BigInt(1) 4 val Two = BigInt(2) 5 6 def calculate(n: BigInt): Future[BigInt] 7 }

Page 24: Async Microservices with Twitter's Finagle

Sequential Composition: map & flatMap

24

1 trait Future[+A] { 2 def flatMap[B](f: A => Future[B]): Future[B] 3 def map[B](f: A => B): Future[B] 4 }

Page 25: Async Microservices with Twitter's Finagle

FlatMap Power

25

1 val ab: Service[A, B] = ??? 2 val bc: Service[B, C] = ??? 3 val cd: Service[C, D] = ??? 4 5 val req: A = ??? 6 val rep: Future[D] = ab(req) flatMap bc flatMap cd

Page 26: Async Microservices with Twitter's Finagle

Sequential Composition: map & flatMap

26

1 object FlatMapFibonacciCalculator extends FibonacciCalculator { 2 def calculate(n: BigInt): Future[BigInt] = 3 if (n == Zero || n == One) Future.value(n) 4 else calculate(n - One) flatMap { a => 5 calculate(n - Two) flatMap { b => Future.value(a + b) } 6 } 7 } 8 9 object MapFibonacciCalculator extends FibonacciCalculator { 10 def calculate(n: BigInt): Future[BigInt] = 11 if (n == Zero || n == One) Future.value(n) 12 else calculate(n - One) map { a => 13 calculate(n - Two) map { b => a + b } 14 } 15 }

Page 27: Async Microservices with Twitter's Finagle

Sequential Composition: for-comprehension

27

1 object ForFibonacciCalculator extends FibonacciCalculator { 2 def calculate(n: BigInt): Future[BigInt] = 3 if (n == Zero || n == One) Future.value(n) 4 else for { 5 a <- calculate(n - One) 6 b <- calculate(n - Two) 7 } yield a + b 8 }

Page 28: Async Microservices with Twitter's Finagle

Concurrent Composition: collect & select

28

1 object Future { 2 def collect[A](fs: Seq[Future[A]]): Future[Seq[A]] 3 def select[A](fs: Seq[Future[A]]): Future[A] 4 }

Page 29: Async Microservices with Twitter's Finagle

Concurrent Composition: collect

29

1 class FanoutFibonacciCalculator( 2 left: FibonacciCalculator, 3 right: FibonacciCalculator) extends FibonacciCalculator { 4 5 def calculate(n: BigInt): Future[BigInt] = 6 if (n == Zero) || n == One) Future.value(n) 7 else { 8 val seq = Seq(left.calculate(n - One), right.calculate(n - Two)) 9 Future.collect(seq) map { _.sum } 10 } 11 }

Page 30: Async Microservices with Twitter's Finagle

Concurrent Composition: select

30

1 class SelectFibonacciCalculator(seq: Seq[FibonacciCalculator]) 2 extends FibonacciCalculator { 3 4 def calculate(n: BigInt): Future[BigInt] = { 5 val futures: Seq[Future[BigInt]] = seq map { _.calculate(n) } 6 Future.select(futures) 7 } 8 }

Page 31: Async Microservices with Twitter's Finagle

31

Futures should be chained asynchronously

Page 32: Async Microservices with Twitter's Finagle

32

Do Not Await

// Asynchronous operation.val f = service(req)// Blocking operation.Await.result(f)

Page 33: Async Microservices with Twitter's Finagle

33

Involving Side-Effects

// Asynchronous operation.val f = service(req)!f onSuccess { rep => println("Got the value: " + rep) } onFailure { t => t.printStackTrace() }

Page 34: Async Microservices with Twitter's Finagle

34

Finagle is a non-blocking glue for services that implement

different protocols

Page 35: Async Microservices with Twitter's Finagle

35

Finagle is extremely good at gluing things that work

with different speed

Page 36: Async Microservices with Twitter's Finagle

References

36

§ http://twitter.github.io/finagle/ !§ http://vkostyukov.ru/posts/finagle-your-fibonacci-calculation/ !§ https://github.com/finagle/finch !§ https://github.com/finagle/finagle-oauth2

Page 37: Async Microservices with Twitter's Finagle

37

Stay Finagled! !

And drop your feedbacks to @vkostyukov