val i: Int = 1234
val foo: String = "bar"
val i = 1234
val foo = "bar"
val addOne: Int => Int = { x: Int =>
x + 1
}
addOne(1) == 2
Every value is an object
val i = 123
i.toString == "123"
i.<(333) == true
i < 333 == true
Array(1, 2, 3, 4)
List(1, 2, 3, 4)
Set(1, 1, 2)
val hostPort = ("localhost", 80)
hostPort._1 == "localhost"
hostPort._2 == 80
1 -> 2 == (1,2)
Map(1 -> 2)
Map(("foo", "bar"))
Map(1 -> Map("foo" -> "bar"))
trait Option[T] {
def isDefined: Boolean
def get: T
def getOrElse(t: T): T
}
val numbers = Map("one" -> 1, "two" -> 2)
numbers.get("two") == Some(2)
numbers.get("three") == None
Option is a container that may or may not hold something.
hostPort match {
case ("localhost", port) => ...
case (host, 80) => ...
}
val result = res1 match {
case Some(n) => n * 2
case None => 0
}
val numbers = List(1, 2, 3, 4)
numbers.map {(n: Int) => n * 2}
numbers.map(_ * 2)
numbers.filter(_ % 2 == 0)
val isEven: Int => Boolean = {
n: Int => n % 2 == 0
}
numbers.filter(isEven)
List(List(1, 2), List(3, 4)).flatten
=> List(1, 2, 3, 4)
List(Some(1), Some(2), None, Some(4)).flatten
=> List(1, 2, 4)
val nestedNumbers = List(List(1, 2), List(3, 4))
nestedNumbers.flatMap(x => x.map(_ * 2))
=> List(2, 4, 6, 8)
nestedNumbers.map((x: List[Int]) => x.map(_ * 2)).flatten
val numbers = List("123", "456", "789")
for {
str <- numbers
num <- str
} yield num
=> List(1, 2, 3, 4, 5, 6, 7, 8, 9)
def addOne(x: Int) = x + 1
def addTwo(x: Int) = x + 2
val addThree = addOne _ compose addTwo _
// f compose g => f(g(x))
val addThree2 = addOne _ andThen addTwo _
// f andThen g => g(f(x))
trait Future[A]
val f: Future[Int]
// an integer valued future
Future[T].map[U](f: T => U): Future[U]
// Converts a Future[T] to a Future[U] by applying f
Future[T].filter(p: T => Boolean): Future[T]
// Convert a successful Future[T] to a failed Future[T]
// by applying predicate
val f: Future[Int] = ???
Await.result(f) // directly query
// you use callback functions instead
f onSuccess { res =>
println("The result is " + res)
} onFailure { exc =>
println("f failed with " + exc)
}
val p: Promise[Int]
// Success
p.setValue(1)
// Failure
p.setException(new Exception)
trait Webpage {
def imageLinks: Seq[String]
def links: Seq[String]
}
def fetch(url: String): Future[Webpage]
def getThumbnail(url: String): Future[Webpage] = ???
def getThumbnail(url: String): Future[Webpage] = {
val promise = new Promise[Webpage]
fetch(url) onSuccess { page =>
fetch(page.imageLinks(0)) onSuccess { p =>
promise.setValue(p)
} onFailure { exc =>
promise.setException(exc)
}
} onFailure { exc =>
promise.setException(exc)
}
promise
}
Callback-hell...
def flatMap[B](f: A => Future[B]): Future[B]
def getThumbnail(url: String): Future[Webpage] =
fetch(url) flatMap { page =>
fetch(page.imageLinks(0))
}
object Future {
def collect[A](fs: Seq[Future[A]]): Future[Seq[A]]
def join(fs: Seq[Future[_]]): Future[Unit]
def select(fs: Seq[Future[A]]): Future[(Try[A], Seq[Future[A]])]
}
def getThumbnails(url: String): Future[Seq[Webpage]] =
fetch(url) flatMap { page =>
Future.collect(
page.imageLinks map { u => fetch(u) }
)
}
Useful for fan-out operations
def rescue[B](f: Exception => Future[B]): Future[B]
// Recovering errors...
val f = fetch(url) rescue {
case ConnectionFailed =>
fetch(url)
}
trait Service[Req, Rep] extends (Req => Future[Rep])
val http: Service[HttpReq, HttpRep]
val redis: Service[RedisCmd, RedisRep]
val thrift: Service[TFrame, TFrame]
Servers implement these, clients make use of them
// A simple service
// server
val multiplier = { i =>
Future.value(i*2)
}
// client
multiplier(123) onSuccess { res =>
println("result", r)
}
// server
Http.serve(":8080", new Service[HttpReq, HttpRep] {
def apply(req: HttpReq): Future[HttpRep] = ???
})
// client
val http = Http.newService("kakao.com:80")
// proxy
Http.serve(":8080", Http.newService("kakao.com:80"))
trait Filter[ReqIn, ReqOut, RepIn, RepOut] extends
((ReqIn, Service[ReqOut, RepIn]) => Future[RepOut])
val timeout = { (req, service) =>
service(req).within(1.second)
}
class TimeoutFilter[Req, Rep](to: Duration)
extends Filter[Req, Rep, Req, Rep] {
def apply(req: Req, service: Service[Req, Rep]) =
service(req).within(to)
}
val timeout: Filter[…]
val auth: Filter[…]
val authAndTimeout = auth andThen timeout
val service: Service[…]
val authAndTimeoutService =
authAndTimeout andThen service
Asynchronous
Non-Blocking
Protocol Agnostic
Fullstack RPC
import com.twitter.finagle.Http
val server = Http.server
.withAdmissionControl.concurrencyLimit(
maxConcurrentRequests = 10,
maxWaiters = 0
)
import com.twitter.conversions.time._
import com.twitter.finagle.Http
val server = Http.server
.withRequestTimeout(42.seconds)
// define your stats
val counter = statsReceiver.counter("requests_counter")
// update the value
counter.incr()
// The value of this counter will be exported
// by the HTTP server and accessible at /admin/metrics.json
import com.twitter.finagle.Http
import com.twitter.conversions.time._
import com.twitter.finagle.service.RetryBudget
val budget = RetryBudget(
ttl = 10.seconds,
minRetriesPerSec = 5,
percentCanRetry = 0.1
)
val client = Http.client.withRetryBudget(budget)
// Allow retrying 10% of total requests on top of 5 retries per sec
import com.twitter.finagle.Http
import com.twitter.conversions.time._
import com.twitter.finagle.service.Backoff
val client = Http.client
.withRetryBackoff(Backoff.exponentialJittered(2.seconds, 32.seconds))
// Backoff for rnd(2s), rnd(4s), rnd(8), ..., rnd(32s), ... rnd(32s)
import com.twitter.conversions.time._
import com.twitter.finagle.Http
val client = Http.client
.withTransport.connectTimeout(1.second) // TCP connect
.withSession.acquisitionTimeout(42.seconds)
.withSession.maxLifeTime(20.seconds) // connection max life time
.withSession.maxIdleTime(10.seconds) // connection max idle time
import com.twitter.conversions.time._
import com.twitter.finagle.Http
import com.twitter.finagle.loadbalancer.Balancers
val balancer = Balancers.aperture(
lowLoad = 1.0, highLoad = 2.0, // the load band adjusting an aperture
minAperture = 10 // min aperture size
)
val client = Http.client.withLoadBalancer(balancer)
import com.twitter.conversions.time._
import com.twitter.finagle.Http
import com.twitter.finagle.service.Backoff
import com.twitter.finagle.service.FailureAccrualFactory.Param
import com.twitter.finagle.service.exp.FailureAccrualPolicy
val twitter = Http.client
.configured(Param(() => FailureAccrualPolicy.successRate(
requiredSuccessRate = 0.95,
window = 100,
markDeadFor = Backoff.const(10.seconds)
)))
// Mark a replica dead if SuccessRate is below 95%
// over the most recent 100 requests and then backoff for 10 sec.