Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Async Microservices with Twitter's Finagle

5,759 views

Published on

Overview of Finagle Framework.

Published in: Technology
  • Be the first to comment

Async Microservices with Twitter's Finagle

  1. 1. Async Microservices with Finagle Vladimir Kostyukov http://vkostyukov.ru
  2. 2. Asynchronous Protocol-Agnostic Full-Stack RPC 2
  3. 3. Adopters 3
  4. 4. Built atop of Netty (#1 Non-Blocking I/O for JVM) 4
  5. 5. Finagle - Netty in a functional setting 5
  6. 6. Building Blocks: Services + Futures + Filters 6
  7. 7. trait Service[-Req, +Rep] extends (Req => Future[Rep]) 7
  8. 8. Service-Oriented rather then Stream-Oriented 8
  9. 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) 9
  10. 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) 10
  11. 11. Servers and Clients talk to each other via services 11
  12. 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) 12
  13. 13. Are there high-order services? 13
  14. 14. 14 Client Service Server Filter Filter Filter Service
  15. 15. // ReqIn -> Service(ReqOut -> RepIn) -> RepOut trait Filter[-ReqIn, +RepOut, +ReqOut, -RepIn] extends ((ReqIn, Service[ReqOut, RepIn]) => Future[RepOut]) 15
  16. 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] 16
  17. 17. Filters are type-safe! 17
  18. 18. A Type-Safe Input Filter 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 18
  19. 19. A Type-Safe Output Filter 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 19
  20. 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 20
  21. 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 21
  22. 22. Future is a monad! 22
  23. 23. Abstraction: Fibonacci Calculator 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 } 23
  24. 24. Sequential Composition: map & flatMap 1 trait Future[+A] { 2 def flatMap[B](f: A => Future[B]): Future[B] 3 def map[B](f: A => B): Future[B] 4 } 24
  25. 25. FlatMap Power 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 25
  26. 26. Sequential Composition: map & flatMap 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 } 26
  27. 27. Sequential Composition: for-comprehension 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 } 27
  28. 28. Concurrent Composition: collect & select 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 } 28
  29. 29. Concurrent Composition: collect 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 } 29
  30. 30. Concurrent Composition: select 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 } 30
  31. 31. Futures should be chained asynchronously 31
  32. 32. Do Not Await // Asynchronous operation. val f = service(req) // Blocking operation. Await.result(f) 32
  33. 33. Involving Side-Effects // Asynchronous operation. val f = service(req) ! f onSuccess { rep => println("Got the value: " + rep) } onFailure { t => t.printStackTrace() } 33
  34. 34. Finagle is a non-blocking glue for services that implement different protocols 34
  35. 35. Finagle is extremely good at gluing things that work with different speed 35
  36. 36. References § http://twitter.github.io/finagle/ ! § http://vkostyukov.ru/posts/finagle-your-fibonacci-calculation/ ! § https://github.com/finagle/finch ! § https://github.com/finagle/finagle-oauth2 36
  37. 37. Stay Finagled! ! And drop your feedbacks to @vkostyukov 37

×