Projet Calypso

Présentation

Purch

  • Online marketing business
  • À Grenoble : anciennement Best of Media
  • Responsables de :
    • Tom's Guide / IT / Hardware
    • TopTenReviews
    • Space.com
    • ...

Projet Calypso

  • Moteur de recommandation
  • Publicités
  • Basé sur le comportements des autres utilisateurs
  • Plusieurs centaines de millions d'actions à traiter par jour

L'équipe

  • Projet conjoint entre :
    • Purch
    • Kelkoo
    • LIG
  • Parties communes / spécifiques

Quelles informations ?

  • Plates-formes de publicités
  • Tracking de clicks

Quelles contraintes ?

  • BLAZIN' FAST
    • < 200ms
  • Beaucoup de données
  • Suivi "temps réel" des tendances

Technologies

  • Kafka
  • Spark
  • HDFS
  • Scala

Scala

Un tour rapide

Introduction

  • Langage de programmation multi-paradigme
  • Conçu à l'EPFL
  • Compilable en bytecode JAVA
    • Profite des librairies JAVA

Pourquoi Scala ?

  • Langage natif de Spark
  • Fonctionnel

Principaux apports de Scala

  • Fonctions lambda
  • Currying
  • Extracteurs
  • Inférence de type
  • Syntaxe très sucrée
  • ...
foo.bar("toto");

def add(x: Int, y: Int): Int = {
    return x + y;
}

List(1, 2, 3, 4).map(x => {
    return add(x, 5);
});

Le sucre syntaxique

foo.bar("toto");

def add(x: Int, y: Int): Int = {
    return x + y;
}

List(1, 2, 3, 4).map(x => {
    return add(x, 5);
});

Le sucre syntaxique

foo bar "toto"
def add(x: Int, y: Int): Int = x + y
(1 to 4).map { add(_, 5) }

def process[A](filter:A=>Boolean)(list:List[A]):List[A] = {
  lazy val recurse = process(filter) _
 
  list match {
    case head::tail => if (filter(head)) {
      head::recurse(tail)
    } else {
      recurse(tail)
    }
 
    case Nil => Nil
  }
}
 
val even = (a:Int) => a % 2 == 0
 
val numbersAsc = 1::2::3::4::5::Nil
val numbersDesc = 5::4::3::2::1::Nil
 
process(even)(numbersAsc)   // [2, 4]
process(even)(numbersDesc)  // [4, 2]

Un exemple assez ouf

L'underscore en Scala

Ou : "What the actual fuck"

// Imports all the classes in the package matching
import scala.util.matching._

// Imports all the members of the object Fun (static import in Java).
import com.test.Fun._

// Imports all the members of the object Fun but renames Foo to Bar
import com.test.Fun.{ Foo => Bar , _ }

// Imports all the members except Foo. To exclude a member rename it to _
import com.test.Fun.{ Foo => _ , _ }

The Underscore: part 1

def matchTest(x: Int): String = x match {
     case 1 => "one"
     case 2 => "two"
     case _ => "anything other than one and two"
 }

 expr match {
     case List(1,_,_) => " a list with three element and the first element is 1"
     case List(_*)  => " a list with zero or more elements "
     case Map[_,_] => " matches a map with any key type and any value type "
     case _ =>
 }

 List(1,2,3,4,5).foreach( a => print(a))
 // Same as:
 List(1,2,3,4,5).foreach(print(_))

 List((1,2), (3,4)).foreach( (a, b) => print(a + b))
 // Same as:
 List((1,2), (3,4)).foreach(print(_ + _))

The Underscore strikes back

def f() = { println("Yo dawg.") ; 42 }

def g(): Unit = { f() }
// warning: discarded non-Unit value
// def g(): Unit = { f() }
//                    ^

def g(): Unit = { val _ = f() }

// scala> g
// Yo dawg.

The Underscore 3: in space

Les extracteurs

case class User(
    firstName: String,
    lastName: String,
    score: Int
)

val user = new User("Maxime", "Vacher", 2)


user match {
    case User(firstName, lastName, score) => println(s"Hello, $firstName !")
    case _ =>
}


def advance(xs: List[User]) = xs match {
    case User(_, _, score1) :: User(_, _, score2) :: _ => score1 - score2
    case _ => 0
}

Les extracteurs, juge et flic

trait User {
  def name: String
}
class FreeUser(val name: String) extends User
class PremiumUser(val name: String) extends User

object FreeUser {
  def unapply(user: FreeUser): Option[String] = Some(user.name)
}
object PremiumUser {
  def unapply(user: PremiumUser): Option[String] = Some(user.name)
}


val user: User = new PremiumUser("Daniel")
user match {
  case FreeUser(name) => "Hello " + name
  case PremiumUser(name) => "Welcome back, dear " + name
}

Les extracteurs contre César

object Twice {
    def apply(x: Int): Int = x * 2
    def unapply(z: Int): Option[Int] = if (z%2 == 0) Some(z/2) else None
}

object TwiceTest extends App {
    val x = Twice(21)
    x match { 
        case Twice(n) => Console.println(n) // prints 21
    }
}

Les extracteurs et les gendarmettes

Implicit

Et après on arrête avec Scala

def add(a: Int, b: Int): Int = a + b

> add("maxime", "vacher") // Won't compile

Les convertisseurs implicites

implicit toInt(string: String): Int = string.length

> add("maxime", "vacher") // 12
class Prefixer(val prefix: String)

def addPrefix(s: String)(implicit p: Prefixer) = p.prefix + s

Les paramètres implicites

implicit val myImplicitPrefixer = new Prefixer("<3 ")

addPrefix("Maxime Vacher")  // <3 Maxime Vacher

Traitement Big Data

C'est bientôt fini

Kafka

  • Système distribué de messagerie
  • Flux de données en temps réel

Spark

  • Framework de calcul distribué
  • Travaille sur la RAM
  • Abstraction (partielle) pour le développeur
    • Notion de RDD

Winter is coming

HDFS

  • Système de fichier du framework Hadoop
  • Distribué
  • Tolérant à la panne

Merci de votre sage attention

Présentation Purch

By Marc Schaller

Présentation Purch

  • 390