Async Microservices with Twitter's Finagle
-
Upload
vladimir-kostyukov -
Category
Technology
-
view
752 -
download
3
description
Transcript of Async Microservices with Twitter's Finagle
2
Asynchronous Protocol-Agnostic
Full-Stack RPC
3
Adopters
4
Built atop of Netty (#1 Non-Blocking I/O for JVM)
5
Finagle - Netty in a functional setting
6
Building Blocks: Services + Futures + Filters
7
trait Service[-Req, +Rep] extends (Req => Future[Rep])
8
Service-Oriented rather then
Stream-Oriented
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)
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)
11
Servers and Clients talk to each other via services
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)
13
Are there high-order services?
14
Client Service
Server ServiceFilter Filter Filter
15
// ReqIn -> Service(ReqOut -> RepIn) -> RepOuttrait Filter[-ReqIn, +RepOut, +ReqOut, -RepIn] extends ((ReqIn, Service[ReqOut, RepIn]) => Future[RepOut])
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]
17
Filters are type-safe!
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
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
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
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
22
Future is a monad!
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 }
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 }
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
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 }
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 }
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 }
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 }
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 }
31
Futures should be chained asynchronously
32
Do Not Await
// Asynchronous operation.val f = service(req)// Blocking operation.Await.result(f)
33
Involving Side-Effects
// Asynchronous operation.val f = service(req)!f onSuccess { rep => println("Got the value: " + rep) } onFailure { t => t.printStackTrace() }
34
Finagle is a non-blocking glue for services that implement
different protocols
35
Finagle is extremely good at gluing things that work
with different speed
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