My presentation at Mostly Functional (http://mostlyfunctional.com), part of this year's Turing Festival Fringe (http://turingfestival.com) in Edinburgh. The example source code is up on Github at https://github.com/geoffballinger/simple-sharder
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Sharding and Load Balancing in Scala - Twitter's Finagle
1. Sharding and Load Balancing in Scala
Twitter's Finagle
Geoff Ballinger
Technical Advisor at Mobile Acuity Ltd.
2. • some ML and Lisp in the early 90s
• C, C++ and Java since – embedded and server
• apps on BB, Android and iOS
• Mobile
• Startups
• Scaling
• Integration
• Deployment & Ops
>>> Background
4. • Multiple HTTP workers – part of the result
• Each shard is one or more identical workers
• Sharder scatters requests to the shards and
gathers the results
• Load balance between workers
• Fault tolerant
• Chance to play w/ Scala in anger!
• (Simple rational reconstruction)
>>> A simple sharder?
5. object Worker extends App {
try {
// Collect args
val port = Integer.parseInt(args(0))
val value = args(1)
// Define our service - just return the value provided!
val service = new Service[HttpRequest, HttpResponse] {
def apply(req: HttpRequest) = {
val buffer = ChannelBuffers.copiedBuffer(value, Charset.forName("UTF-8"))
val response = new DefaultHttpResponse(req.getProtocolVersion, HttpResponseStatus.OK)
response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/plain")
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, buffer.readableBytes())
response.setContent(buffer)
Future.value(response)
}
}
>>> Worker (1)
6. // Run this worker over the required port
val server = ServerBuilder()
.codec(Http())
.bindTo(new InetSocketAddress(port))
.name("Shard")
.build(service)
} catch {
case t: Throwable => {
System.err.println(t.getMessage())
System.err.println("Usage: Worker <port> <value>")
}
}
}
>>> Worker (2)
7. // Collect port arg
val port = Integer.parseInt(args(0))
// Build up shards from host specs on remaining command line
val shards = args.tail.map(spec => {
ClientBuilder()
.codec(Http())
.hosts(spec)
.hostConnectionLimit(10)
.retryPolicy(policy)
.build()
})
>>> Sharder (setup)
8. // Define our service - scatter to the shards and gather the results
val service = new Service[HttpRequest, HttpResponse] {
def apply(req: HttpRequest) = {
Future.collect(shards.map(shard => { // Scatter
shard(req).map(resp => resp.getContent().toString(CHARSET))
}))
.map(resps => { // Gather
val bf = ChannelBuffers.copiedBuffer(resps.reduceLeft(_+":"+_), CHARSET)
val resp = new DefaultHttpResponse(req.getProtocolVersion, HttpResponseStatus.OK)
resp.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/plain")
resp.setHeader(HttpHeaders.Names.CONTENT_LENGTH, bf.readableBytes())
resp.setContent(bf)
resp
})
}
}
>>> Sharder (service)
10. >>> Reflections and Opinions
• Concise and expressive
• Shoulders of giants
• Poorly documented
• Exposes too many lower APIs
• Twitter vs Scala vs Akka Futures!