of 84 /84
ZapTravel @nmartignole Nicolas Martignole Février 2013 vendredi 22 mars 13

Play2 ou l'architecture web réactive

Embed Size (px)

DESCRIPTION

Technical presentation about Zaptravel. Confoo 2013 - February - Montreal - Canada By Nicolas Martignole, Principal Engineer at Zaptravel.

Citation preview

Page 1: Play2 ou l'architecture web réactive

ZapTravel

@nmartignoleNicolas Martignole

Février 2013

vendredi 22 mars 13

Page 2: Play2 ou l'architecture web réactive

Découvrir Play2/Scala

L’objectif de cette présentation est de...

vendredi 22 mars 13

Page 3: Play2 ou l'architecture web réactive

ZapTravel

vendredi 22 mars 13

Page 4: Play2 ou l'architecture web réactive

ZapTravel

vendredi 22 mars 13

Page 5: Play2 ou l'architecture web réactive

vendredi 22 mars 13

Page 6: Play2 ou l'architecture web réactive

vendredi 22 mars 13

Page 7: Play2 ou l'architecture web réactive

ZapTravel

vendredi 22 mars 13

Page 8: Play2 ou l'architecture web réactive

Framework Web

ZapTravel

vendredi 22 mars 13

Page 9: Play2 ou l'architecture web réactive

Java et Scala

ZapTravel

vendredi 22 mars 13

Page 10: Play2 ou l'architecture web réactive

Créé par Guillaume Bort

@guillaumebort

ZapTravel

vendredi 22 mars 13

Page 11: Play2 ou l'architecture web réactive

Rails pour Java/Scala

ZapTravel

vendredi 22 mars 13

Page 12: Play2 ou l'architecture web réactive

Simple, productif

ZapTravel

Sauvegardez, rechargez, c’est tout

vendredi 22 mars 13

Page 13: Play2 ou l'architecture web réactive

Communauté Java

ZapTravel

vendredi 22 mars 13

Page 14: Play2 ou l'architecture web réactive

vendredi 22 mars 13

Page 15: Play2 ou l'architecture web réactive

2 Livres en préparation

vendredi 22 mars 13

Page 16: Play2 ou l'architecture web réactive

Démonstration

ZapTravel

vendredi 22 mars 13

Page 17: Play2 ou l'architecture web réactive

Routes

GET / Application.index

ZapTravel

vendredi 22 mars 13

Page 18: Play2 ou l'architecture web réactive

Scala

def index() = Action { val name ="Nicolas" Ok(views.html.Application.index(name)) }

ZapTravel

vendredi 22 mars 13

Page 19: Play2 ou l'architecture web réactive

Scala - 2@(name: String)

@myTemplate() {

<h1>Hello @name</h1> ... }

ZapTravel

vendredi 22 mars 13

Page 20: Play2 ou l'architecture web réactive

Play 2 en bref

ZapTravel

vendredi 22 mars 13

Page 21: Play2 ou l'architecture web réactive

Play 2 en bref• RESTful

• Compilateur CoffeScript, Less

• JSON

• Websocket, Server Sent Event, Comet

• NoSQL et BigData

• Sécurité (XSS,CSRF)

• Java NIO

• Driver asynchrone pour MongoDB

• Require.js

ZapTravel

vendredi 22 mars 13

Page 22: Play2 ou l'architecture web réactive

Quelques exemples

ZapTravel

vendredi 22 mars 13

Page 23: Play2 ou l'architecture web réactive

Charger une donnée venant de Redis

ZapTravel

vendredi 22 mars 13

Page 24: Play2 ou l'architecture web réactive

Architecture

LB

Web

Web

Web

HTTPHTTPS

RedisAir/Hotel/Cars/Ac

RedisResa/Users

RedisWeb Content

ZapTravel

vendredi 22 mars 13

Page 25: Play2 ou l'architecture web réactive

Architecture

LB

Web

Web

Web

HTTPHTTPS

RedisAir/Hotel/Cars/Ac

RedisResa/Users

RedisWeb Content

Web

redis

ZapTravel

vendredi 22 mars 13

Page 26: Play2 ou l'architecture web réactive

Cas d’usage

ZapTravel

Donne moi le label qui correspond à originId =380

vendredi 22 mars 13

Page 27: Play2 ou l'architecture web réactive

Cas d’usage

ZapTravel

Donne moi le label qui correspond à originId =380

def getSlug(originId: Long): Option[String] = Redis.pool.withClient { client => Option(client.hget("Url:From:Rev", originId.toString))}

vendredi 22 mars 13

Page 28: Play2 ou l'architecture web réactive

Cas d’usage

ZapTravel

Donne moi le label qui correspond à originId =380

def getSlug(originId: Long): Option[String] = Redis.pool.withClient { client => Option(client.hget("Url:From:Rev", originId.toString))}

vendredi 22 mars 13

Page 29: Play2 ou l'architecture web réactive

Cas d’usage

ZapTravel

Donne moi le label qui correspond à originId =380

def getSlug(originId: Long): Option[String] = Redis.pool.withClient { client => Option(client.hget("Url:From:Rev", originId.toString))}

Driver Sedis https://github.com/pk11/sedisvendredi 22 mars 13

Page 30: Play2 ou l'architecture web réactive

Un mot sur les Tests

vendredi 22 mars 13

Page 31: Play2 ou l'architecture web réactive

https://gist.github.com/nicmarti/5064048

package models import org.specs2.mutable._ import play.api.test._import play.api.test.Helpers._ class OriginSpecs extends Specification { "An Origin" should { "returns the slug for a valid origin" in { running(FakeApplication()) { Origin.getSlug(380) mustEqual Some("from-london") Origin.getSlug(1) mustEqual Some("from-paris") Origin.getSlug(-9999) mustEqual None } } }}

vendredi 22 mars 13

Page 32: Play2 ou l'architecture web réactive

Charger un objet

ZapTravel

Charge moi un Objet «Londres»

vendredi 22 mars 13

Page 33: Play2 ou l'architecture web réactive

Charger un objet Origine

ZapTravel

def getOrigin(originId: Long): Option[Origin] = Redis.pool.withClient { client => Option(client.hget("Url:From:Rev", originId.toString)).map{ slug=> Option(client.hget("Places:Place:"+originId, "display").map { .... ... } }}

1) charger from-london

vendredi 22 mars 13

Page 34: Play2 ou l'architecture web réactive

Cas d’usage

ZapTravel

2) charger display...

def getOrigin(originId: Long): Option[Origin] = Redis.pool.withClient { client => Option(client.hget("Url:From:Rev", originId.toString)).map{ slug=> Option(client.hget("Places:Place:"+originId, "display").map { .... ... } }}

vendredi 22 mars 13

Page 35: Play2 ou l'architecture web réactive

Cas d’usage

ZapTravel

def getOrigin(originId: Long): Option[Origin] = Redis.pool.withClient { client => for(slug<-Option(client.hget("Url:From:Rev", originId.toString)); display<-Option(client.hget("Places:Place:"+originId, "display") )) yield Origin(originId,display,slug)

}}

2) charger display...

for-comprehensionhttps://gist.github.com/nicmarti/5064066

vendredi 22 mars 13

Page 36: Play2 ou l'architecture web réactive

La Tour Eiffel

ZapTravel

1. Charger du JSON à partir de Redis2. Interpréter et retourner un objet PointOfInterest

vendredi 22 mars 13

Page 37: Play2 ou l'architecture web réactive

{"name":"Eiffel Tower","address":"","latitude":"48.8582493546","longitude":"2.2945117950","website":"www.tour-eiffel.fr","rank":3,"photo":{"r":"eiffel-tower-paris-france","k":"6b56","e":"jpg","w":2406,"h":1600,"a":"Mirari Erdoiza","l":"http:\\/\\/www.fotopedia.com\\/items\\/anboto-RiKxAA3gE6I"},"sentences":{"gbs":[{"d":"The Eiffel Tower is one of the most famous monuments in the world (324 metres, 10,100 tonnes).","a":"Paris","l":"http:\\/\\/www.paris.com\\/paris_landmarks\\/monuments\\/eiffel_tower_paris"},{"d":"This is without doubt one of the most recognizable structures in the world.","a":"Frommers","l":"http:\\/\\/www.frommers.com\\/destinations\\/paris\\/A25288.html"},{"d":"If the Statue of Liberty is emblematic of New York, Big Ben is London, and the Kremlin is Moscow, then the Eiffel Tower is the symbol of Paris.","a":"Fodors","l":"http:\\/\\/www.fodors.com\\/world\\/europe\\/france\\/paris\\/review-97417.html"},{"d":"When it was built for the 1889 Exposition Universelle (World Fair), marking the centenary of the Revolution, the Tour Eiffel faced massive opposition from Paris' artistic and literary elite.","a":"Lonely Planet","l":"http:\\/\\/www.lonelyplanet.com\\/france\\/paris\\/sights\\/famous-landmark\\/eiffel-tower"}],"tips":[{"d":"It's pretty high!.","a":"annawelford","l":"http:\\/\\/www.lonelyplanet.com\\/france\\/paris\\/sights\\/famous-landmark\\/eiffel-tower","s":"Lonely Planet"},{"d":"Bigger than you think.","a":"anomolly","l":"http:\\/\\/www.lonelyplanet.com\\/france\\/paris\\/sights\\/famous-landmark\\/eiffel-tower","s":"Lonely Planet"},{"d":"Overcrowded.","a":"anshjain","l":"http:\\/\\/www.lonelyplanet.com\\/france\\/paris\\/sights\\/famous-landmark\\/eiffel-tower","s":"Lonely Planet"},{"d":"The restaurant on the first floor is an amazing experience!.","a":"ansofie","l":"http:\\/\\/www.lonelyplanet.com\\/france\\/paris\\/sights\\/famous-landmark\\/eiffel-tower","s":"Lonely Planet"}]},"tags":["Landmark","Memorials\\/Monuments","Sights","Famous landmark"]}

HGET Pois:PoisHash 52511

vendredi 22 mars 13

Page 38: Play2 ou l'architecture web réactive

Play 2.0

• Définir une case class POI

• Définir un Format[POI]

• Ecrire la fonction pour lire et parser le JSON

Note : Play 2.1 apporte un nouveau parser JSON plus simple

vendredi 22 mars 13

Page 39: Play2 ou l'architecture web réactive

Play 2.0

case class POI(name: String, address: String, latitude: String, longitude: String, website: Option[String], photo: Option[SightPhoto] = None, sentences: Sentences, tags: Option[List[String]])

POI = Point of Interest = notre Tour Eiffel

vendredi 22 mars 13

Page 40: Play2 ou l'architecture web réactive

Play 2.0

vendredi 22 mars 13

Page 41: Play2 ou l'architecture web réactive

Appel Redis et interprétation JSON

vendredi 22 mars 13

Page 42: Play2 ou l'architecture web réactive

Afficher une listeZapTravel

vendredi 22 mars 13

Page 43: Play2 ou l'architecture web réactive

Afficher une liste

ZapTravel

vendredi 22 mars 13

Page 44: Play2 ou l'architecture web réactive

Aller sur Redisdef allOrigins: List[Origin] = Redis.pool.withClient { client => // ... // ... }

Modèlevendredi 22 mars 13

Page 45: Play2 ou l'architecture web réactive

Préparer une listedef allUrlOrigins: Seq[(String, String)] = { Origin.allOrigins.map{ origin => (origin.slug, origin.label) }.sortBy(_._2)}

Contrôleurvendredi 22 mars 13

Page 46: Play2 ou l'architecture web réactive

Envoyer la liste au template

<label for="location">Your travel origin is :</label>

@select( userForm("originCity"),

FolioCriteria.allUrlOrigins , '_label -> "Travel from origin", '_showConstraints -> false)

Code dans la page HTML

Vuevendredi 22 mars 13

Page 47: Play2 ou l'architecture web réactive

Afficher une liste

ZapTravel

vendredi 22 mars 13

Page 48: Play2 ou l'architecture web réactive

Gérer l’authentification

vendredi 22 mars 13

Page 49: Play2 ou l'architecture web réactive

Comment protéger l’accès à une ressource ?

My Info

vendredi 22 mars 13

Page 50: Play2 ou l'architecture web réactive

Comment protéger l’accès à une ressource ?

My Info

vendredi 22 mars 13

Page 51: Play2 ou l'architecture web réactive

Dans le Controllerobject Application extends Controller { def index = Action { implicit request => val username="test" Ok(html.index(username)) }

}

vendredi 22 mars 13

Page 52: Play2 ou l'architecture web réactive

Dans le Controllerobject Application extends Controller with Secured { def index = ActionSecure { username => implicit request => Ok(html.index(username)) }

}

vendredi 22 mars 13

Page 53: Play2 ou l'architecture web réactive

trait Secured {

def username(request: RequestHeader) = request.session.get(Security.username)

def onUnauthorized(request: RequestHeader) = Results.Redirect(routes.Auth.login)

def ActionSecure(f: => String => Request[AnyContent] => Result) = { Security.Authenticated(username, onUnauthorized) { user => Action{ request => f(user)(request) } } }

} Result

HTMLString Request[AnyContent]

vendredi 22 mars 13

Page 54: Play2 ou l'architecture web réactive

Play2 et Sécurité

• Simple

• Composable

• Facile à tester

vendredi 22 mars 13

Page 55: Play2 ou l'architecture web réactive

Optimiser l’indexation et le référencement

vendredi 22 mars 13

Page 56: Play2 ou l'architecture web réactive

Indexation et référencement

• URLs propres et pondérées

• Mots clés

• Liens et Sitemap

• Microformat (Hotel, Avion, Lieux)

• Contenu non répété

vendredi 22 mars 13

Page 57: Play2 ou l'architecture web réactive

vendredi 22 mars 13

Page 58: Play2 ou l'architecture web réactive

routes

Compilé et validé

vendredi 22 mars 13

Page 60: Play2 ou l'architecture web réactive

Play2

• La séparation entre la partie routage et la partie contrôleur permet de créer des URLs «propres»

vendredi 22 mars 13

Page 61: Play2 ou l'architecture web réactive

Sitemap

• Déclarer la table des matières de son site

• Optimise le référencement

• Permet de mettre en cache les pages

curl http://www.zaptravel.com/sitemap.xml

vendredi 22 mars 13

Page 62: Play2 ou l'architecture web réactive

vendredi 22 mars 13

Page 63: Play2 ou l'architecture web réactive

Problème : construire le sitemap de façon asynchrone

vendredi 22 mars 13

Page 64: Play2 ou l'architecture web réactive

Solution : Async

Akka / Play2

vendredi 22 mars 13

Page 65: Play2 ou l'architecture web réactive

def sitemap = Action { implicit request => val longCall = Akka.future { val today = ZapFormats.formatForJson(new DateTime()) val listOfSEOCards = FolioCard.allForSEO().flatMap { seo => val localURL = routes.Frontoffice.showFolioDynamically(seo.category, seo.duration, seo.origin, seo.destination, seo.dateRange, seo.segment).absoluteURL(secure = false) val updatedUrl = (for (l <- localAddress; p <- publicAddress) yield localURL.replaceAll(l, p)) updatedUrl } Ok(views.xml.Application.sitemap(today, listOfSEOCards)).as("text/xml") } Async { implicit val timeout = akka.util.Timeout(60 seconds) longCall } }

vendredi 22 mars 13

Page 66: Play2 ou l'architecture web réactive

def sitemap = Action { implicit request => val longCall = Akka.future { val today = ... // some other code val listOfSEOCards = FolioCard.allForSEO().flatMap { seo => val localURL = routes.Frontoffice.showFolioDynamically(seo.category, seo.duration, seo.origin, seo.destination, seo.dateRange, seo.segment).absoluteURL(secure = false) val updatedUrl = (for (l <- localAddress; p <- publicAddress) yield localURL.replaceAll(l, p)) updatedUrl } Ok(views.xml.Application.sitemap(today, listOfSEOCards) ).as("text/xml") } Async { implicit val timeout = akka.util.Timeout(60 seconds) longCall } }

vendredi 22 mars 13

Page 67: Play2 ou l'architecture web réactive

def sitemap = Action { implicit request => val longCall = Akka.future { val today = ZapFormats.formatForJson(new DateTime()) val listOfSEOCards = FolioCard.allForSEO().flatMap { seo => val localURL = routes.Frontoffice.showFolioDynamically(seo.category, seo.duration, seo.origin, seo.destination, seo.dateRange, seo.segment).absoluteURL(secure = false)

val updatedUrl = (for (l <- localAddress; p <- publicAddress) yield localURL.replaceAll(l, p))

updatedUrl } Ok(views.xml.Application.sitemap(today, listOfSEOCards)).as("text/xml") } Async { implicit val timeout = akka.util.Timeout(60 seconds) longCall } }

vendredi 22 mars 13

Page 68: Play2 ou l'architecture web réactive

def sitemap = Action { implicit request => val longCall = Akka.future { val today = ZapFormats.formatForJson(new DateTime()) val listOfSEOCards = FolioCard.allForSEO().flatMap { seo => val localURL = routes.Frontoffice.showFolioDynamically(seo.category, seo.duration, seo.origin, seo.destination, seo.dateRange, seo.segment).absoluteURL(secure = false)

val updatedUrl = (for (l <- localAddress; p <- publicAddress) yield localURL.replaceAll(l, p))

updatedUrl } Ok(views.xml.Application.sitemap(today, listOfSEOCards)).as("text/xml") } Async { implicit val timeout = akka.util.Timeout(60 seconds) longCall } }

vendredi 22 mars 13

Page 69: Play2 ou l'architecture web réactive

def sitemap = Action { implicit request => val longCall = Akka.future { val today = ZapFormats.formatForJson(new DateTime()) val listOfSEOCards = FolioCard.allForSEO().flatMap { seo => val localURL = routes.Frontoffice.showFolioDynamically(seo.category, seo.duration, seo.origin, seo.destination, seo.dateRange, seo.segment).absoluteURL(secure = false)

val updatedUrl = (for (l <- localAddress; p <- publicAddress) yield localURL.replaceAll(l, p))

updatedUrl } Ok(views.xml.Application.sitemap(today, listOfSEOCards)).as("text/xml") } Async { implicit val timeout = akka.util.Timeout(60 seconds) longCall } }

Bref...curl http://www.zaptravel.com/sitemap.xml

vendredi 22 mars 13

Page 70: Play2 ou l'architecture web réactive

Gestion du cachevendredi 22 mars 13

Page 71: Play2 ou l'architecture web réactive

Comment améliorer les performances ?

vendredi 22 mars 13

Page 72: Play2 ou l'architecture web réactive

Eviter de recharger la même page,

utilisez code 304 NotModified

Note: @rosstuck a fait une session sur HTTP à Confoo mercredi dernier

vendredi 22 mars 13

Page 73: Play2 ou l'architecture web réactive

Exemple sur /from-paris/quality

Navigateur Play2

GET /from-paris/quality

vendredi 22 mars 13

Page 74: Play2 ou l'architecture web réactive

Exemple sur /from-paris/qualityNavigateur Play2

OK

HTTP/1.1 200 OKContent-Type: text/html; charset=utf-8ETag: 11299930771Cache-Control: max-age=600, s-maxage=600, must-revalidateContent-Length: 103586......

ce n’est pas une erreur

vendredi 22 mars 13

Page 75: Play2 ou l'architecture web réactive

Recharge /from-paris/quality

Navigateur Play2

GET /from-paris/qualityIf-None-Match: 112999307771

304 Not ModifiedContent-Length: 0

vendredi 22 mars 13

Page 76: Play2 ou l'architecture web réactive

Optimisation 1

• Evitez de faire travailler votre serveur pour rien

• Déterminez des ETags «métiers»

• Attention à la gestion du cache et des serveurs mandataires.

vendredi 22 mars 13

Page 77: Play2 ou l'architecture web réactive

Optimisation 2

Faire de la gestion de cache applicative

vendredi 22 mars 13

Page 78: Play2 ou l'architecture web réactive

Cache applicatif ?

vendredi 22 mars 13

Page 79: Play2 ou l'architecture web réactive

2 types de cacheCache technique type Varnish

Cache de Play2 ou Redis

- Process à part- Cache HTTP

- Code applicatif- utilise la mémoire de Play2 ou Redis

vendredi 22 mars 13

Page 80: Play2 ou l'architecture web réactive

2 types de cache

• Facile à installer

• Evite de solliciter Play2

• Scalable

• Configurable

Cache technique type Varnish

vendredi 22 mars 13

Page 81: Play2 ou l'architecture web réactive

2 types de cache

• Prend en compte le métier

• Permet de garder les pages «authentifiées»

• Pas aussi performant que la solution Varnish

Cache applicatif Play2/Redis

vendredi 22 mars 13

Page 82: Play2 ou l'architecture web réactive

Sur Zaptravel• Page d’accueil

optimisé avec Cache de Play2

• Page Folio, section top Deal avec cache Play2

• Page Deal, cache avec Redis

vendredi 22 mars 13

Page 83: Play2 ou l'architecture web réactive

Et pour terminer

Play2Architecture WebApprentissageTests unitairesAsynchrone (Enumeratee, Iteratee)

vendredi 22 mars 13

Page 84: Play2 ou l'architecture web réactive

Merci

https://joind.in/7951

@nmartignole

vendredi 22 mars 13