Scala na soundcloud [QCon]

Post on 14-Jun-2015

255 views 0 download

description

A linguagem Scala tem ganho adoção nos últimos anos como alternativa de alto desempenho para sistemas com milhões de usuários, com casos de sucesso incluindo Twitter, Linkedin e Nokia. Nesta palestra, apresentaremos o caso da SoundCloud, a rede social líder na área de música e áudio. Construída inicialmente como aplicação monolítica Ruby on Rails, a arquitetura do SoundCloud evoluiu organicamente para um modelo de microservices implementados em diversas linguagens de programação, com parte importante desses serviços criada na linguagem Scala. Veremos aqui um pouco dessa jornada, focando nas vantagens e desafios encontrados ao desenvolver microservices, e na nossa experiência ao usar Scala em um ambiente de alta demanda e complexidade.

Transcript of Scala na soundcloud [QCon]

@flaviowbrasil

Aficionado por open source

Expert em escalabilidade

Mestre Jedi Scala

Prefere uma sessão de profiling a uma tarde na praia

@herval

Fundador de Startups

Dois Easter Eggs publicados

Padawan Scala

Tem um cachorro

A Nuvem de Som

12 horas de novo conteúdo/ minuto350 milhões de usuários/mês12 milhões de horas ouvidas/mês1 usuário no espaço

Era uma vez uma startup...

Era uma vez uma startup...

Uma base de código

Era uma vez uma startup...

Uma base de códigoUm punhado de construtores

Move fast and break things

Um milhão de featuresBase de código gigante

Quebrando os muros

Muitos vilarejos, muitos idiomas

Muitos vilarejos, muitos idiomas

Microserviços → "tudo é http"Culturas de códigoInfraestruturaMonitoramento"production ready"

Ninguém entende ninguém

Move fast without breaking everything, bitte.

Java

JavaServiços de baseBibliotecas compartilhadasMonitoriaAutenticação/segurança (cross-cutting concerns)

JavaServiços de baseBibliotecas compartilhadasMonitoriaAutenticação/segurança (cross-cutting concerns)

INFRAESTRUTURA BÁSICA

JVM-Kit + Finagle

Estudo de caso

O novo Stream

Reescrever pra quê?

APIMySQL

Cassandra

Reescrever pra quê?

APIMySQL

Cassandra

Reescrever pra quê?

APIMySQL

Cassandra

Reescrever pra quê?80% request queueing

Reescrever pra quê?

Novas featuresLatênciaVazãoMicroserviços

Timeline

API

Timeline

API

Roshi

Timeline

API

Roshi

Autentic.

Autoriz.

Geo loc.

Timeline

API

Roshi

Autentic.

Autoriz.

Geo loc.

Timeline

API

Roshi

Autentic.

Autoriz.

Geo loc.

Playlists

Tracks

Usuários

Timeline

API

Roshi

Autentic.

Autoriz.

Geo loc.

Playlists

Tracks

Usuários

Likes

Coment.

Promoted

Stats

Timeline

API

Roshi

Autentic.

Autoriz.

Geo loc.

Playlists

Tracks

Usuários

Likes

Coment.

Promoted

Stats

...

Timeline

API

Roshi

Autentic.

Autoriz.

Geo loc.

Playlists

Tracks

Usuários

Likes

Coment.

Promoted

Stats

...

BuscarAgregar

BuscarLatência

Timeline

API

Roshi

Autentic.

Autoriz.

Geo loc.

Playlists

Tracks

Usuários

Likes

Coment.

Promoted

Stats

...

AgregarComplexidade

Timeline

API

Roshi

Autentic.

Autoriz.

Geo loc.

Playlists

Tracks

Usuários

Likes

Coment.

Promoted

Stats

...

Scala yay!

Buscar

Paralelizar

Futuros

Futuros

Referência para um valor que será disponibilizado no futuro.

Futuros val response: Future[Int] = …

// Não compila

response + 1

val response: Future[Int] = …

// Adiciona um callback

response.onSuccess { int =>

println(int)

}

Futuros

val response: Future[Int] = …

// Compõe um novo futuro

val count: Future[Int] =

response.map { int =>

int + 1

}

Futuros

for {

user <- authenticate(request)

} yield {

}

Futuros

for {

user <- authenticate(request)

geo <- geoLocationFor(request, user)

} yield {

}

Futuros

for {

user <- authenticate(request)

geo <- geoLocationFor(request, user)

timeline <- timelineFor(user, geo)

} yield {

}

Futuros

for {

user <- authenticate(request)

geo <- geoLocationFor(request, user)

timeline <- timelineFor(user, geo)

resources <-

fetchTracks(timeline)

.join(fetchPlaylists(timeline))

.join(fetchComments(timeline))

} yield {

}

Futuros

for {

user <- authenticate(request)

geo <- geoLocationFor(request, user)

timeline <- timelineFor(user, geo)

resources <-

fetchTracks(timeline)

.join(fetchPlaylists(timeline))

.join(fetchComments(timeline))

} yield {

new EnrichedTimeline(timeline, resources)

}

Futuros

def handle(request: Request): Future[EnrichedTimeline] =

for {

user <- authenticate(request)

geo <- geoLocationFor(request, user)

timeline <- timelineFor(user, geo)

resources <-

fetchTracks(timeline)

.join(fetchPlaylists(timeline))

.join(fetchComments(timeline))

} yield {

new EnrichedTimeline(timeline, resources)

}

Futuros

Futuros

+ NIO

Escalabilidade

Máquinas

MáquinasProcessos

MáquinasProcessosThreads

MáquinasProcessosThreadsFuturos

def handle(request: Request): Future[EnrichedTimeline] =

for {

user <- authenticate(request)

geo <- geoLocationFor(request, user)

timeline <- timelineFor(user, geo)

resources <-

fetchTracks(timeline)

.join(fetchPlaylists(timeline))

.join(fetchComments(timeline))

} yield {

new EnrichedTimeline(timeline, resources)

}

Futuros

Agregar

Funcional

Collections

val timeline: Timeline = …

val timeline: Timeline = …

val actorsToFetch: List[User] =

timeline.items.map(_.actor)

val timeline: Timeline = …

val actorsToFetch: List[User] =

timeline.items.map(_.actor)

val itemsByActor: Map[User, List[Item]] =

timeline.items.groupBy(_.actor)

val timeline: Timeline = …

val actorsToFetch: List[User] =

timeline.items.map(_.actor)

val itemsByActor: Map[User, List[Item]] =

timeline.items.groupBy(_.actor)

val numberOfItemsByActor: Map[User, Int] =

itemsByActor.mapValues(_.size)

def createEnrichedTimeline(

timeline: Timeline,

users: Map[User, EnrichedUser]) = {

timeline.items.map { item =>

new EnrichedItem(

item, users.get(item.actor))

}

}

Options

Some(value)None

Option[T]

def findUser(id: Int): Option[User]

// Não compila

render.json(findUser(666).name)

Options

def findUser(id: Int): Option[User]

// Compila

findUser(666).map {

case Some(user) => render.json(user.name)

case None => render.notFound

}

Options

PatternMatching

case class User(name: String, gender: Gender, email: Email)

case class User(name: String, gender: Gender, email: Email)

timelineActors.foreach {

}

case class User(name: String, gender: Gender, email: Email)

timelineActors.foreach {

case User(name, Female, Email(_, “soundcloud.com”) =>

println(“mulher trabalhando na soundcloud”)

}

case class User(name: String, gender: Gender, email: Email)

timelineActors.foreach {

case User(name, Female, Email(_, “soundcloud.com”) =>

println(“mulher trabalhando na soundcloud”)

case User(name, Male, Email(_, “soundcloud.com”) =>

println(“homem trabalhando na soundcloud”)

}

case class User(name: String, gender: Gender, email: Email)

timelineActors.foreach {

case User(name, Female, Email(_, “soundcloud.com”) =>

println(“mulher trabalhando na soundcloud”)

case User(name, Male, Email(_, “soundcloud.com”) =>

println(“homem trabalhando na soundcloud”)

case User(name, gender, Email(_, “qconrio.com.br”) =>

println(“organizador da qconrio”)

}

case class User(name: String, gender: Gender, email: Email)

timelineActors.foreach {

case User(name, Female, Email(_, “soundcloud.com”) =>

println(“mulher trabalhando na soundcloud”)

case User(name, Male, Email(_, “soundcloud.com”) =>

println(“homem trabalhando na soundcloud”)

case User(name, gender, Email(_, “qconrio.com.br”) =>

println(“organizador da qconrio”)

case _ =>

println(“pessoa desconhecida”)

}

Nem tudo são flores...

Código denso

def flatMap[B, That] (f: (A) ⇒ Traversable[B]) (implicit bf: CanBuildFrom[

List[A], B, That]): That

Código denso

def flatMap[B, That] (f: (A) ⇒ Traversable[B]) (implicit bf: CanBuildFrom[

List[A], B, That]): That

listOfThings.flatMap(_.toUpperCase)

XML também é código?

<fruits> <fruit type="banana" origin="brazil"/> <fruit type="apple"/> <fruit type="orange" origin="usa"/> <fruit type="orange" origin="brazil"/></fruits> \\ "fruit"

filter { _ \\ "@origin" exists (_.text == "brazil") }

Mas e o Stream...?

Mas e o Stream...?

APIMySQL

Cassandra

Mas e o Stream...?

API

MySQL

RedisTimeline Service

Tracks ServicePlaylists ServiceMultiple Services

Mas e o Stream...?

API

MySQL

RedisTimeline Service

Tracks ServicePlaylists ServiceMultiple Services

~150 ms~0% request queueing

Mas só o Stream...?

Data teamOutros serviços em ScalaFerramental jvmkit

Stranglers

Stranglers

StranglersNovo

streamCache

O presente

Em resumo...

Produtivo como Ruby, typesafe como Java*

Move faster break fewer things

O futuro

Padrões de códigoBibliotecas mais estáveisFacilidade de inovação"One Scala"

O futuro

Perguntas?