Date post: | 26-Jun-2015 |
Category: |
Technology |
Upload: | ontico |
View: | 1,136 times |
Download: | 1 times |
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)
}
Questions?
• http://tinyurl.com/finagle-guide
• http://tinyurl.com/server-as-a-function
• http://finatra.info
• @capotej