Julio Capote, Twitter

Post on 26-Jun-2015

1,136 views 1 download

Tags:

description

HighLoad++ 2013

transcript

Distributing Systems in Scala

with Finagle Julio Capote

About me

• Julio Capote

• API team at Twitter

• @capotej

This talk

• Services

• Scala

• Finagle

• Examples

Scaling

Monolithic System

• Limited to a single language and runtime

• Can only support few developers

• Deploys get more dangerous

Solution?

Distributed System

• Each service written in appropriate language

• Can only support many developers

• Granular deploys

But, this is complex...

Quick Scala Intro

What is Scala?

• JVM language

• Interoperable with Java

• Functional

• But Object Oriented too

Values

val foo: String = "bar"

val age: Int = 27

Type inference

val foo = "bar"

val foo: String = "bar"

Collections

List(61,31,34)

Seq(1,2,3)

Dictionaries / Hashes

Map("foo" -> "bar")

Functional

val addOne =

{ x: Int => x + 1 }

addOne.apply(2) => 3

addOne(2) => 3

val list = Seq(1,2,3,4)

list.map { x => addOne(x) }

list.map(addOne(_))

list.map(addOne)

list map(addOne)

list map addOne

list map addOne filter

{ item: Int =>

item % 2 == 0 }

Function Composition

val addOne =

{ x: Int => x + 1 }

val addTwo =

{ x: Int => x + 2 }

val addThree =

addOne compose addTwo

val addOne =

{ x: Int => x + 1 }

val addTwo =

{ x: Int => x + 2 }

val addThree =

addOne compose addTwo

addThree(2) => 5

Object oriented

class Dog extends Animal

def bark = {

println("woof")

}

}

Options

Map("foo" -> "bar").get("foo")

Some(bar)

Map("foo" -> "bar").get("zzz")

None

val a = Map("foo" -> "bar")

val opt = a.get("zzz")

val result =

opt.getOrElse("default")

val a = Map("foo" -> "bar")

val opt = a.get("zzz")

val result = opt match {

case Some(x) => x

case None => "default"

}

val a = Map("foo" -> "bar")

val opt = a.get("zzz")

opt foreach { result =>

println(result)

}

Finagle

What is Finagle?

• Client/Server RPC Framework

• Asynchronous

• Functional

• Protocol Agnostic

Futures

def fetchUrl(url: String) = Future[Response]

val f: Future[Response] =

fetchUrl("google.ru")

f map { response =>

println(response.content)

}

val f: Future[Response] =

fetchUrl("google.ru")

f onSuccess { response =>

println(response.content)

} onFailure {

case e => log.error(e)

}

val f: Future[Response] =

fetchUrl("google.ru")

f onSuccess { response =>

println(response.content)

} onFailure {

case e => log.error(e)

}

Services

“Your Server as a Function”

type Service[Req, Rep] = Req => Future[Rep]

Request => Future[Response]

Http Client

val client: Service[Request, Response] =

Http.newService("www.google.ru:80")

Making a request

val req = Request("/")

client(req) onSuccess { resp =>

println(resp.content)

}

val req = Request("/")

client.apply(req) onSuccess { resp =>

println(resp.content)

}

val req = Request("/")

val resp = client(req)

resp onSuccess { resp =>

println(resp.content)

}

Await.ready(resp)

val req = Request("/")

val resp = client(req)

resp onSuccess { resp =>

println(resp.content)

}

Await.ready(resp)

Http Server

class MyServer

extends Service[Request, Response] {

def apply(request: Request) = {

Future.value(new Response("hi"))

}

}

class MyServer

extends Service[Request, Response] {

def apply(request: Request) = {

Future.value(new Response("hi"))

}

}

Request => Future[Response]

Starting the server

val service = new MyServer

val server = Http.serve(":8080", service)

Await.ready(server)

Http Proxy

object Proxy extends App {

val client: Service[Request, Response] =

Http.newService("www.google.ru:80")

val server = Http.serve(":8080", client)

Await.ready(server)

}

object Proxy extends App {

val client: Service[Request, Response] =

Http.newService("www.google.ru:80")

val server = Http.serve(":8080", client)

Await.ready(server)

}

object Proxy extends App {

val client: Service[Request, Response] =

Http.newService("www.google.ru:80")

val server = Http.serve(":8080", client)

Await.ready(server)

}

Filters

Filter[InRequest,

InResponse,

OutRequest,

OutResponse]

Filter[InRequest,

InResponse,

OutRequest,

OutResponse]

Filter[InRequest,

InResponse,

OutRequest,

OutResponse]

SimpleFilter[Request, Response]

def apply(request: Req,

service: Service[Req, Rep]): Future[Rep]

def apply(request: Req,

service: Service[Req, Rep]): Future[Rep]

def apply(request: Req,

service: Service[Req, Rep]): Future[Rep]

def apply(request: Req,

service: Service[Req, Rep]): Future[Rep]

class ScreamFilter[Req, Rep]

extends SimpleFilter[Req, Rep]

def apply(request: Req,

service: Service[Req, Rep]) = {

service(request) onSuccess { response =>

response.setContent(response.content + "!")

response

}

}

}

class ScreamFilter[Req, Rep]

extends SimpleFilter[Req, Rep]

def apply(request: Req,

service: Service[Req, Rep]) = {

service(request) onSuccess { response =>

response.setContent(response.content + "!")

response

}

}

}

class ScreamFilter[Req, Rep]

extends SimpleFilter[Req, Rep]

def apply(request: Req,

service: Service[Req, Rep]) = {

service(request) onSuccess { response =>

response.setContent(response.content + "!")

response

}

}

}

class ScreamFilter[Req, Rep]

extends SimpleFilter[Req, Rep]

def apply(request: Req,

service: Service[Req, Rep]) = {

service(request) onSuccess { response =>

response.setContent(response.content + "!")

response

}

}

}

class ScreamFilter[Req, Rep]

extends SimpleFilter[Req, Rep]

def apply(request: Req,

service: Service[Req, Rep]) = {

service(request) onSuccess { response =>

response.setContent(response.content + "!")

response

}

}

}

class ScreamFilter[Req, Rep]

extends SimpleFilter[Req, Rep]

def apply(request: Req,

service: Service[Req, Rep]) = {

service(request) onSuccess { response =>

response.setContent(response.content + "!")

response }

}

}

val service = new MyServer

val server = Http.serve(":8080", service)

Await.ready(server)

val service = new MyServer

val myFilter = new MyFilter

val newService = myFilter andThen service

val server = Http.serve(":8080", newService)

Await.ready(server)

val service = new MyServer

val myFilter = new MyFilter

val newService = myFilter andThen service

val server = Http.serve(":8080", newService)

Await.ready(server)

val service = new MyServer

val myFilter = new MyFilter

val newService = myFilter andThen service

val server = Http.serve(":8080", newService)

Await.ready(server)

Metrics

curl http://localhost:9990/admin/metrics.json

Add your own

class MyServer(stats: StatsReceiver)

extends Service[Request, Response] {

def apply(request: Request) = {

stats.counter("my counter").incr()

stats.time("fetch") {

expensiveOperation()

}

Future.value(new Response("hi"))

}

}

class MyServer(stats: StatsReceiver)

extends Service[Request, Response] {

def apply(request: Request) = {

stats.counter("my counter").incr()

stats.time("fetch") {

expensiveOperation()

}

Future.value(new Response("hi"))

}

}

class MyServer(stats: StatsReceiver)

extends Service[Request, Response] {

def apply(request: Request) = {

stats.counter("my counter").incr()

stats.time("fetch") {

expensiveOperation()

}

Future.value(new Response("hi"))

}

}

Finatra

Finagle with Sinatra

class Example extends Controller {

get("/") { request =>

render.plain("hi").toFuture

}

}

class Example extends Controller {

get("/") { request =>

render.plain("hi").toFuture

}

}

class MyServer extends FinatraServer {

val controller = new Example()

register(controller)

}

http://finatra.info