137
Voyager avec Play 2 Nicolas Martignole [email protected] @nmartignole Scala.IO - 24/25 octobre 2013, Paris samedi 26 octobre 13

Voyager avec play scala

Embed Size (px)

DESCRIPTION

Retour d'expérience sur le développement d'une application avec Play2/Scala/Redis pendant un an chez Zaptravel.

Citation preview

Page 1: Voyager avec play scala

Voyager avec Play 2

Nicolas [email protected]

@nmartignoleScala.IO - 24/25 octobre 2013, Paris

samedi 26 octobre 13

Page 2: Voyager avec play scala

Votre plan de vol

ZapTravel

WhatWhyHow

samedi 26 octobre 13

Page 3: Voyager avec play scala

Avant de commencer...

Current status

samedi 26 octobre 13

Page 4: Voyager avec play scala

ZapTravel

samedi 26 octobre 13

Page 5: Voyager avec play scala

We search destinations & dates, then find the best price, hotel and

transport, so you don’t need to

samedi 26 octobre 13

Page 6: Voyager avec play scala

We search destinations & dates, then find the best price, hotel and

transport, so you don’t need to

filter, map, reduce

samedi 26 octobre 13

Page 7: Voyager avec play scala

ZapTravel

samedi 26 octobre 13

Page 8: Voyager avec play scala

samedi 26 octobre 13

Page 9: Voyager avec play scala

samedi 26 octobre 13

Page 10: Voyager avec play scala

samedi 26 octobre 13

Page 11: Voyager avec play scala

MobileAPI REST

samedi 26 octobre 13

Page 12: Voyager avec play scala

samedi 26 octobre 13

Page 13: Voyager avec play scala

samedi 26 octobre 13

Page 14: Voyager avec play scala

samedi 26 octobre 13

Page 15: Voyager avec play scala

samedi 26 octobre 13

Page 16: Voyager avec play scala

RomanceShow medeals

samedi 26 octobre 13

Page 17: Voyager avec play scala

FamilyShow medeals

samedi 26 octobre 13

Page 18: Voyager avec play scala

samedi 26 octobre 13

Page 19: Voyager avec play scala

</zaptravel>samedi 26 octobre 13

Page 20: Voyager avec play scala

“There are known knowns; there are things we know that we know.There are known unknowns; that is to say, there are things that we now know we don't know.But there are also unknown unknowns – there are things we do not know we don't know.

”—United States Secretary of Defense, Donald Rumsfeld

samedi 26 octobre 13

Page 21: Voyager avec play scala

Aware

KnowDon’t know

Not aware

samedi 26 octobre 13

Page 22: Voyager avec play scala

Aware

KnowDon’t know

Not aware

samedi 26 octobre 13

Page 23: Voyager avec play scala

Quelques chiffres

• 159 000 hôtels

• 1383 destinations

• 840 000 transports (avions/trains)

• 1.4To images sur S3

• 20600 prix chambres hôtels

samedi 26 octobre 13

Page 24: Voyager avec play scala

redis 127.0.0.1:6379> hlen Hotel:Content:Short(integer) 158 041

samedi 26 octobre 13

Page 25: Voyager avec play scala

Ce que je savais

samedi 26 octobre 13

Page 26: Voyager avec play scala

Ce que je savais

• Play! Framework

samedi 26 octobre 13

Page 27: Voyager avec play scala

Ce que je savais

• Play! Framework

•Web development

samedi 26 octobre 13

Page 28: Voyager avec play scala

Ce que je savais

• Play! Framework

•Web development

•Hiring and training developers

samedi 26 octobre 13

Page 29: Voyager avec play scala

Ce que je savais

• Play! Framework

•Web development

•Hiring and training developers

• Kiss-ass project managment

samedi 26 octobre 13

Page 30: Voyager avec play scala

EquipePlay2 ScalaHTML CSS ScalaJavaZe Boss

samedi 26 octobre 13

Page 31: Voyager avec play scala

Equipe

0

1,25

2,5

3,75

5

Mai 2012 Ete 2012 Sept 2012 Oct 2012 Nov 2012 Jan 2013 Oct 2013

Play2 ScalaHTML CSS ScalaJavaZe Boss

samedi 26 octobre 13

Page 32: Voyager avec play scala

samedi 26 octobre 13

Page 33: Voyager avec play scala

Comment apprendre Scala

(et désapprendre Java)

samedi 26 octobre 13

Page 34: Voyager avec play scala

Scala et Zaptravel

• Scala => recrutement

• Facile à apprendre

• Scala c’est simple

samedi 26 octobre 13

Page 35: Voyager avec play scala

Ah tu fais du Scala

samedi 26 octobre 13

Page 36: Voyager avec play scala

Paradigme objet ET fonctionnel

http://parleys.com/p/51c1994ae4b0d38b54f4621b

samedi 26 octobre 13

Page 37: Voyager avec play scala

Ce que j’ai évité

- 18

samedi 26 octobre 13

Page 38: Voyager avec play scala

SBT

samedi 26 octobre 13

Page 39: Voyager avec play scala

SBT

ScalaZ

samedi 26 octobre 13

Page 40: Voyager avec play scala

Les choses que je ne savais pas

• Faut être gonflé

• Communauté

• Parallélisme, Reactivité

• Play2/Scala/Redis en PROD ???

• SEO

• JSON+Redis

• Typesafe / refactoring

samedi 26 octobre 13

Page 41: Voyager avec play scala

samedi 26 octobre 13

Page 42: Voyager avec play scala

Communauté Scala

Place de Scala, la communauté, par rapport à Java

samedi 26 octobre 13

Page 43: Voyager avec play scala

94 516 VUEs

Scala Days 2013 on parleys.com

samedi 26 octobre 13

Page 44: Voyager avec play scala

Parallélisme et concurrence

It’s the web, stupid

Response[HTML] = Fx(Request)

samedi 26 octobre 13

Page 45: Voyager avec play scala

Typesafe

•HTML template•routes•config•LESS

samedi 26 octobre 13

Page 46: Voyager avec play scala

Play2 -> ReactiveReactive In Practice

samedi 26 octobre 13

Page 47: Voyager avec play scala

Play2 -> ReactiveReactive In Practice

samedi 26 octobre 13

Page 48: Voyager avec play scala

août - sept 2012R.I.P

Iteratee, Enumeratee and Enumerator on Zaptravel

samedi 26 octobre 13

Page 49: Voyager avec play scala

BusinessExceptionIteratee/Enumeratee/Enumerator c’est cool, mais nous n’en n’avons pas besoin pour le moment.

samedi 26 octobre 13

Page 50: Voyager avec play scala

/**

* Server sent event streaming controller.

* Date: 06/08/12

* Time: 12:16

*/

object Streaming extends Controller {

  // Streaming using server sent event

  def stream(requestId: String) = Action {

    // Define an implicit EventNameExtractor wich extract the "event" name from the Json event so that the EventSource() sets

the event in the message

    implicit val eventNameExtractor: EventNameExtractor[JsValue]=EventNameExtractor[JsValue](eventName = (zepEvent)=>zepEvent.\

("event").asOpt[String])

        // Streams.events is a composition of HotelPrice and AirfarePrice.

        Ok.feed(Streams.events(requestId) &> EventSource()).as("text/event-stream")

  }

 implicit val eventNameExtractor: EventNameExtractor[JsValue] =EventNameExtractor[JsValue](eventName = (zepEvent)=>zepEvent.\("event").asOpt[String])

samedi 26 octobre 13

Page 51: Voyager avec play scala

Akkafaukon

samedi 26 octobre 13

Page 52: Voyager avec play scala

Akka•Cron Jobs

• Emails (Mailjet/Mailchimp)

• Facebook

• ElasticSearch index (proto)

• Sitemap

•Generate content (R.I.P.)

samedi 26 octobre 13

Page 53: Voyager avec play scala

Akka

samedi 26 octobre 13

Page 54: Voyager avec play scala

Dev ?

EC2 m2.2xlarge

Play2

Redis

RedisPrices

Prix Hotels, Avions,Voitures, Trains, Rating Hotel

27GB870k obj

EC2 m2.xlarge

Lieux, Destinations,Contenu, Routage, URLs, Places, Tags,

Webuser

1.2 GB450k obj

RedisStatic

slave-of

read-only

read/write

S3

samedi 26 octobre 13

Page 55: Voyager avec play scala

Prod ?

Cloudfront ELBRoute53

EC2 c1.medium

wwwredis prices

EC2 m2.2xlarge

redis static

EC2 m2.xlarge

SimpleDB

logs

CloudWatch

S3

redis backup

samedi 26 octobre 13

Page 56: Voyager avec play scala

Play2 + AWS

samedi 26 octobre 13

Page 57: Voyager avec play scala

Idéal : c1.medium

2 vCPUs1.7 GB mémoire

samedi 26 octobre 13

Page 58: Voyager avec play scala

Redis

samedi 26 octobre 13

Page 59: Voyager avec play scala

Ce qu’il faut retenir

• IaaS versus PaaS pour Zaptravel

2300 USD / mois

samedi 26 octobre 13

Page 60: Voyager avec play scala

Boarding...

samedi 26 octobre 13

Page 61: Voyager avec play scala

Des cas d’usages

samedi 26 octobre 13

Page 62: Voyager avec play scala

Fonctionnalités

samedi 26 octobre 13

Page 63: Voyager avec play scala

Fonctionnalités• API REST

samedi 26 octobre 13

Page 64: Voyager avec play scala

Fonctionnalités• API REST

• Facebook

samedi 26 octobre 13

Page 65: Voyager avec play scala

Fonctionnalités• API REST

• Facebook

• Weather

samedi 26 octobre 13

Page 66: Voyager avec play scala

Fonctionnalités• API REST

• Facebook

• Weather

• GeoIP

samedi 26 octobre 13

Page 67: Voyager avec play scala

Fonctionnalités• API REST

• Facebook

• Weather

• GeoIP

• Semantic Search

samedi 26 octobre 13

Page 68: Voyager avec play scala

Fonctionnalités• API REST

• Facebook

• Weather

• GeoIP

• Semantic Search

• Cache Redis

samedi 26 octobre 13

Page 69: Voyager avec play scala

Fonctionnalités• API REST

• Facebook

• Weather

• GeoIP

• Semantic Search

• Cache Redis

• Mobile Web version

samedi 26 octobre 13

Page 70: Voyager avec play scala

Fonctionnalités• API REST

• Facebook

• Weather

• GeoIP

• Semantic Search

• Cache Redis

• Mobile Web version

• Authentification

samedi 26 octobre 13

Page 71: Voyager avec play scala

Fonctionnalités• API REST

• Facebook

• Weather

• GeoIP

• Semantic Search

• Cache Redis

• Mobile Web version

• Authentification

• Statistiques/Parcours visiteur

samedi 26 octobre 13

Page 72: Voyager avec play scala

Fonctionnalités• API REST

• Facebook

• Weather

• GeoIP

• Semantic Search

• Cache Redis

• Mobile Web version

• Authentification

• Statistiques/Parcours visiteur

samedi 26 octobre 13

Page 73: Voyager avec play scala

Quelques exemples

ZapTravel

samedi 26 octobre 13

Page 74: Voyager avec play scala

Charger une donnée venant de Redis

ZapTravel

samedi 26 octobre 13

Page 75: Voyager avec play scala

Architecture

LB

Web

Web

Web

HTTPHTTPS

RedisAir/Hotel/Cars/Ac

RedisResa/Users

RedisWeb Content

ZapTravel

samedi 26 octobre 13

Page 76: Voyager avec play scala

Architecture

LB

Web

Web

Web

HTTPHTTPS

RedisAir/Hotel/Cars/Ac

RedisResa/Users

RedisWeb Content

Web

redis

ZapTravel

samedi 26 octobre 13

Page 77: Voyager avec play scala

Cas d’usage

ZapTravel

Donne moi le label qui correspond à originId =380

samedi 26 octobre 13

Page 78: Voyager avec play scala

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))}

samedi 26 octobre 13

Page 79: Voyager avec play scala

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))}

samedi 26 octobre 13

Page 80: Voyager avec play scala

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/sedissamedi 26 octobre 13

Page 81: Voyager avec play scala

Un mot sur les Tests

samedi 26 octobre 13

Page 82: Voyager avec play scala

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 } } }}

samedi 26 octobre 13

Page 83: Voyager avec play scala

Charger un objet

ZapTravel

Charge moi un Objet «Londres»

samedi 26 octobre 13

Page 84: Voyager avec play scala

Charger un objet Origin

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

samedi 26 octobre 13

Page 85: Voyager avec play scala

Code smells

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{ .... ... } }}

samedi 26 octobre 13

Page 86: Voyager avec play scala

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

samedi 26 octobre 13

Page 87: Voyager avec play scala

La Tour Eiffel

ZapTravel

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

samedi 26 octobre 13

Page 88: Voyager avec play scala

{"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

samedi 26 octobre 13

Page 89: Voyager avec play scala

Play 2.1

• Définir une case class POI

• Définir un implicit Json.format[POI]

•C’est tout... ou presque

samedi 26 octobre 13

Page 90: Voyager avec play scala

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

samedi 26 octobre 13

Page 91: Voyager avec play scala

Play 2.0

samedi 26 octobre 13

Page 92: Voyager avec play scala

Play 2.1

samedi 26 octobre 13

Page 93: Voyager avec play scala

Play 2.1(Parser lorsque le JSON stocké sur Redis utilise une déclaration différente

de la case class)

samedi 26 octobre 13

Page 94: Voyager avec play scala

Appel Redis et interprétation JSON

samedi 26 octobre 13

Page 95: Voyager avec play scala

Afficher une listeZapTravel

samedi 26 octobre 13

Page 96: Voyager avec play scala

Afficher une liste

ZapTravel

samedi 26 octobre 13

Page 97: Voyager avec play scala

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

Modèlesamedi 26 octobre 13

Page 98: Voyager avec play scala

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

Contrôleursamedi 26 octobre 13

Page 99: Voyager avec play scala

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

Vuesamedi 26 octobre 13

Page 100: Voyager avec play scala

Afficher une liste

ZapTravel

samedi 26 octobre 13

Page 101: Voyager avec play scala

Gérer l’authentification

samedi 26 octobre 13

Page 102: Voyager avec play scala

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

My Info

samedi 26 octobre 13

Page 103: Voyager avec play scala

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

My Info

samedi 26 octobre 13

Page 104: Voyager avec play scala

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

}

samedi 26 octobre 13

Page 105: Voyager avec play scala

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

}

samedi 26 octobre 13

Page 106: Voyager avec play scala

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]

samedi 26 octobre 13

Page 107: Voyager avec play scala

Play2 et Sécurité

• Simple

•Composable

• Facile à tester

samedi 26 octobre 13

Page 108: Voyager avec play scala

Optimiser l’indexation et le référencement

samedi 26 octobre 13

Page 109: Voyager avec play scala

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é

samedi 26 octobre 13

Page 110: Voyager avec play scala

samedi 26 octobre 13

Page 111: Voyager avec play scala

routes

Compilé et validé

samedi 26 octobre 13

Page 113: Voyager avec play scala

Play2

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

samedi 26 octobre 13

Page 114: Voyager avec play scala

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

samedi 26 octobre 13

Page 115: Voyager avec play scala

samedi 26 octobre 13

Page 116: Voyager avec play scala

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

samedi 26 octobre 13

Page 117: Voyager avec play scala

Solution : Async

Akka / Play2

samedi 26 octobre 13

Page 118: Voyager avec play scala

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 } }

samedi 26 octobre 13

Page 119: Voyager avec play scala

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 } }

samedi 26 octobre 13

Page 120: Voyager avec play scala

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 } }

samedi 26 octobre 13

Page 121: Voyager avec play scala

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 } }

samedi 26 octobre 13

Page 122: Voyager avec play scala

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

samedi 26 octobre 13

Page 123: Voyager avec play scala

Gestion du cachesamedi 26 octobre 13

Page 124: Voyager avec play scala

Comment améliorer les performances ?

samedi 26 octobre 13

Page 125: Voyager avec play scala

Eviter de recharger la même page,

utilisez code 304 NotModified

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

samedi 26 octobre 13

Page 126: Voyager avec play scala

Exemple sur /from-paris/quality

Navigateur Play2

GET /from-paris/quality

samedi 26 octobre 13

Page 127: Voyager avec play scala

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

samedi 26 octobre 13

Page 128: Voyager avec play scala

Recharge /from-paris/quality

Navigateur Play2

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

304 Not ModifiedContent-Length: 0

samedi 26 octobre 13

Page 129: Voyager avec play scala

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.

samedi 26 octobre 13

Page 130: Voyager avec play scala

Optimisation 2

Faire de la gestion de cache applicative

samedi 26 octobre 13

Page 131: Voyager avec play scala

Cache applicatif ?

samedi 26 octobre 13

Page 132: Voyager avec play scala

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

samedi 26 octobre 13

Page 133: Voyager avec play scala

2 types de cache

• Facile à installer

• Evite de solliciter Play2

• Scalable

• Configurable

Cache technique type Varnish

samedi 26 octobre 13

Page 134: Voyager avec play scala

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

samedi 26 octobre 13

Page 135: Voyager avec play scala

Sur Zaptravel• Page d’accueil

optimisé avec Cache de Play2

• Page Folio, section top Deal avec cache Play2

• Page Deal, cache avec Redis

samedi 26 octobre 13

Page 136: Voyager avec play scala

Et pour terminer

samedi 26 octobre 13

Page 137: Voyager avec play scala

Merci

[email protected]

@nmartignole

samedi 26 octobre 13