SlideShare une entreprise Scribd logo
1  sur  84
Télécharger pour lire hors ligne
@nmartignole
                      Nicolas Martignole
                         Février 2013

                                           ZapTravel

vendredi 22 mars 13
L’objectif de cette présentation est de...




                                          Découvrir
                                         Play2/Scala




vendredi 22 mars 13
ZapTravel




vendredi 22 mars 13
ZapTravel

vendredi 22 mars 13
vendredi 22 mars 13
vendredi 22 mars 13
ZapTravel

vendredi 22 mars 13
Framework Web


                                      ZapTravel

vendredi 22 mars 13
Java et Scala


                                      ZapTravel

vendredi 22 mars 13
Créé par
                      Guillaume Bort
                          @guillaumebort




                                           ZapTravel

vendredi 22 mars 13
Rails pour Java/Scala


                                    ZapTravel

vendredi 22 mars 13
Simple, productif

                      Sauvegardez, rechargez, c’est tout


                                                           ZapTravel

vendredi 22 mars 13
Communauté Java


                                        ZapTravel

vendredi 22 mars 13
vendredi 22 mars 13
2 Livres en préparation




vendredi 22 mars 13
Démonstration




                                      ZapTravel

vendredi 22 mars 13
Routes

                      GET   /    Application.index




                                                     ZapTravel

vendredi 22 mars 13
Scala
            def index() = Action {
                val name ="Nicolas"
                Ok(views.html.Application.index(name))
            }




                                                  ZapTravel

vendredi 22 mars 13
Scala - 2
                @(name: String)

                @myTemplate() {

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


                                             ZapTravel

vendredi 22 mars 13
Play 2 en bref




                                       ZapTravel

vendredi 22 mars 13
Play 2 en bref
                      • RESTful             • Sécurité (XSS,CSRF)
                      • Compilateur         • Java NIO
                        CoffeScript, Less
                                            • Driver asynchrone
                      • JSON                  pour MongoDB

                      • Websocket, Server   • Require.js
                        Sent Event, Comet

                      • NoSQL et BigData
                                                                    ZapTravel

vendredi 22 mars 13
Quelques exemples




                                          ZapTravel

vendredi 22 mars 13
Charger une donnée venant de Redis




                                                     ZapTravel

vendredi 22 mars 13
Architecture
                                               Redis
                                          Air/Hotel/Cars/Ac


                                             Redis
                                    Web    Resa/Users

                                    Web      Redis
                      HTTP                Web Content
                      HTTPS
                               LB   Web




                                                              ZapTravel

vendredi 22 mars 13
Architecture
                                                 Redis
                                            Air/Hotel/Cars/Ac
                                    Web
                                               Redis
                                     Web     Resa/Users

                                     Web       Redis
                      HTTP
                      HTTPS
                                    redis   Web Content
                               LB    Web




                                                                ZapTravel

vendredi 22 mars 13
Cas d’usage
               Donne moi le label qui correspond à originId   =380




                                                                 ZapTravel

vendredi 22 mars 13
Cas d’usage
               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))
  }




                                                                 ZapTravel

vendredi 22 mars 13
Cas d’usage
               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))
  }




                                                                 ZapTravel

vendredi 22 mars 13
Cas d’usage
               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/sedis      ZapTravel

vendredi 22 mars 13
Un mot sur les Tests



vendredi 22 mars 13
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
                       }
                     }
                   }
                 }




                      https://gist.github.com/nicmarti/5064048
vendredi 22 mars 13
Charger un objet


                      Charge moi un Objet «Londres»




                                                      ZapTravel

vendredi 22 mars 13
Charger un objet Origine

  1) charger from-london
  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
           {
                .... ...
           }
      }
  }


                                                                ZapTravel

vendredi 22 mars 13
Cas d’usage
   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
            {
                 .... ...
            }
       }
   }


                                                                 ZapTravel

vendredi 22 mars 13
Cas d’usage
   2) charger display...

       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)

              }
       }




                                  for-comprehension
                      https://gist.github.com/nicmarti/5064066
                                                                        ZapTravel

vendredi 22 mars 13
La Tour Eiffel

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




                                             ZapTravel

vendredi 22 mars 13
HGET Pois:PoisHash 52511
           {"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"]}




vendredi 22 mars 13
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
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
Play 2.0




vendredi 22 mars 13
Appel Redis et
                      interprétation JSON




vendredi 22 mars 13
Afficher une liste
                                          ZapTravel

vendredi 22 mars 13
Afficher une liste




                                          ZapTravel

vendredi 22 mars 13
Aller sur Redis
    def allOrigins: List[Origin] =
    Redis.pool.withClient {
        client =>
          // ...
          // ...
      }




                           Modèle
vendredi 22 mars 13
Préparer une liste
    def allUrlOrigins: Seq[(String, String)] =
    {
      Origin.allOrigins.map{
        origin =>
         (origin.slug, origin.label)
      }.sortBy(_._2)
    }




                            Contrôleur
vendredi 22 mars 13
Envoyer la liste au
                          template
   Code dans la page HTML
             <label for="location">Your travel origin is :</label>

             @select( userForm("originCity"),
                      FolioCriteria.allUrlOrigins ,
                      '_label -> "Travel from origin",
                      '_showConstraints -> false
             )




                                        Vue
vendredi 22 mars 13
Afficher une liste




                                          ZapTravel

vendredi 22 mars 13
Gérer
                      l’authentification
vendredi 22 mars 13
Comment protéger l’accès à
                    une ressource ?



                           My Info




vendredi 22 mars 13
Comment protéger l’accès à
                    une ressource ?



                           My Info




vendredi 22 mars 13
Dans le Controller
         object Application extends Controller
         {
           def index = Action {
             implicit request =>
               val username="test"
               Ok(html.index(username))
           }

         }



vendredi 22 mars 13
Dans le Controller
         object Application extends Controller
         with Secured {
           def index = ActionSecure {
             username =>
               implicit request =>
                 Ok(html.index(username))
           }

         }



vendredi 22 mars 13
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
                         String             Request[AnyContent]            HTML




vendredi 22 mars 13
Play2 et Sécurité

                      • Simple
                      • Composable
                      • Facile à tester


vendredi 22 mars 13
Optimiser
                      l’indexation et le
                        référencement


vendredi 22 mars 13
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
vendredi 22 mars 13
routes

                      Compilé et validé




vendredi 22 mars 13
URL
                            /from-boston/quality

         GET     /$origin<from-(.*)>/:classifier
         controllers.Frontoffice.home(origin:String, classifier: String)




   http://www.zaptravel.com/romance/weekend-deals/from-paris/to-athens/
   12-Apr-2013-to-14-Apr-2013/elite-athens-greece




vendredi 22 mars 13
Play2


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




vendredi 22 mars 13
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
vendredi 22 mars 13
Problème : construire le sitemap de
                             façon asynchrone




vendredi 22 mars 13
Solution : Async


                           Akka / Play2


vendredi 22 mars 13
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
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
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
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
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,




                                   Bref...
   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")
         }

             curl http://www.zaptravel.com/sitemap.xml
         Async {
           implicit val timeout = akka.util.Timeout(60 seconds)
           longCall
         }
     }

vendredi 22 mars 13
Gestion du cache
vendredi 22 mars 13
Comment améliorer
                      les performances ?


vendredi 22 mars 13
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
Exemple sur /from-paris/quality



                      Navigateur                             Play2
                                   GET /from-paris/quality




vendredi 22 mars 13
Exemple sur /from-paris/quality
                      Navigateur                                         Play2
                                        OK



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

                                               ce n’est pas une erreur



vendredi 22 mars 13
Recharge /from-paris/quality

                                   GET /from-paris/quality
                                   If-None-Match: 112999307771
                      Navigateur                         Play2




                                        304 Not Modified
                                        Content-Length: 0




vendredi 22 mars 13
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
Optimisation 2
                      Faire de la gestion de cache applicative




vendredi 22 mars 13
Cache applicatif ?



vendredi 22 mars 13
2 types de cache
                Cache technique type
                                         Cache de Play2 ou Redis
                Varnish


                                          - Code applicatif
                      - Process à part
                                          - utilise la mémoire
                      - Cache HTTP
                                          de Play2 ou Redis




vendredi 22 mars 13
2 types de cache
                Cache technique type
                Varnish

                      • Facile à installer
                      • Evite de solliciter
                        Play2

                      • Scalable
                      • Configurable

vendredi 22 mars 13
2 types de cache
                                Cache applicatif Play2/
                                Redis

                                 • Prend en compte le
                                    métier

                                 • Permet de garder les
                                    pages «authentifiées»

                                 • Pas aussi performant
                                    que la solution
                                    Varnish


vendredi 22 mars 13
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
Et pour terminer
                        Play2
                        Architecture Web
                        Apprentissage
                        Tests unitaires
                        Asynchrone (Enumeratee,
                        Iteratee)

vendredi 22 mars 13
Merci
                      https://joind.in/7951



                         @nmartignole


vendredi 22 mars 13

Contenu connexe

En vedette

9.7 Things Every Programmer Should Know About User Experience
9.7 Things Every Programmer Should Know About User Experience9.7 Things Every Programmer Should Know About User Experience
9.7 Things Every Programmer Should Know About User ExperienceBurr Sutter
 
Open Data Vorlesung 2015: Open Corporate Data
Open Data Vorlesung 2015: Open Corporate DataOpen Data Vorlesung 2015: Open Corporate Data
Open Data Vorlesung 2015: Open Corporate DataMatthias Stürmer
 
An overview of open source in East Asia (China, Japan, Korea)
An overview of open source in East Asia (China, Japan, Korea)An overview of open source in East Asia (China, Japan, Korea)
An overview of open source in East Asia (China, Japan, Korea)OSCON Byrum
 
Enterprise Developer Journey to the IoT
Enterprise Developer Journey to the IoTEnterprise Developer Journey to the IoT
Enterprise Developer Journey to the IoTBurr Sutter
 
Tui the phoenix project book review
Tui the phoenix project book reviewTui the phoenix project book review
Tui the phoenix project book reviewRudiger Wolf
 
SXSW Hacking RSS: Filtering & Processing Obscene Amounts of Information
SXSW Hacking RSS: Filtering & Processing Obscene Amounts of InformationSXSW Hacking RSS: Filtering & Processing Obscene Amounts of Information
SXSW Hacking RSS: Filtering & Processing Obscene Amounts of InformationDawn Foster
 
Cloud State of the Union for Java Developers
Cloud State of the Union for Java DevelopersCloud State of the Union for Java Developers
Cloud State of the Union for Java DevelopersBurr Sutter
 
Devoxx 2011 integration-camel-cxf-servicemix-activemq
Devoxx 2011 integration-camel-cxf-servicemix-activemqDevoxx 2011 integration-camel-cxf-servicemix-activemq
Devoxx 2011 integration-camel-cxf-servicemix-activemqCharles Moulliard
 
My 'Phoenix Project'—One Developer's Evolutionary Journey
My 'Phoenix Project'—One Developer's Evolutionary JourneyMy 'Phoenix Project'—One Developer's Evolutionary Journey
My 'Phoenix Project'—One Developer's Evolutionary JourneyBurr Sutter
 
Event Report - Acumatica Summit 2017
Event Report - Acumatica Summit 2017Event Report - Acumatica Summit 2017
Event Report - Acumatica Summit 2017Holger Mueller
 
Understand Open Source ecosystems
Understand Open Source ecosystemsUnderstand Open Source ecosystems
Understand Open Source ecosystemsKnowmades.com
 
Trusty URIs: Verifiable, Immutable, and Permanent Digital Artifacts for Linke...
Trusty URIs: Verifiable, Immutable, and Permanent Digital Artifacts for Linke...Trusty URIs: Verifiable, Immutable, and Permanent Digital Artifacts for Linke...
Trusty URIs: Verifiable, Immutable, and Permanent Digital Artifacts for Linke...Tobias Kuhn
 
Limited WIP Meeting presentation - The Phoenix Project book review
Limited WIP Meeting presentation - The Phoenix Project book reviewLimited WIP Meeting presentation - The Phoenix Project book review
Limited WIP Meeting presentation - The Phoenix Project book reviewRudiger Wolf
 
The Phoenix Project DevOps Simulation - Paul Wilkinson
The Phoenix Project DevOps Simulation - Paul WilkinsonThe Phoenix Project DevOps Simulation - Paul Wilkinson
The Phoenix Project DevOps Simulation - Paul WilkinsonPink Elephant
 
Event Report - ADP ReThink 2017
Event Report - ADP ReThink 2017Event Report - ADP ReThink 2017
Event Report - ADP ReThink 2017Holger Mueller
 
Presentation Zabbix en Français du 6 Juin 2013
Presentation Zabbix en Français du 6 Juin 2013Presentation Zabbix en Français du 6 Juin 2013
Presentation Zabbix en Français du 6 Juin 2013Alain Ganuchaud
 
Writing highly scalable WebSocket using the Atmosphere Framework and Scala
Writing highly scalable WebSocket using the Atmosphere Framework and ScalaWriting highly scalable WebSocket using the Atmosphere Framework and Scala
Writing highly scalable WebSocket using the Atmosphere Framework and Scalajfarcand
 
Towards complex adaptive architectures
Towards complex adaptive architecturesTowards complex adaptive architectures
Towards complex adaptive architecturesUwe Friedrichsen
 
Progress Report - Oracle HCM Analyst Summit 2017
Progress Report - Oracle HCM Analyst Summit 2017Progress Report - Oracle HCM Analyst Summit 2017
Progress Report - Oracle HCM Analyst Summit 2017Holger Mueller
 

En vedette (20)

9.7 Things Every Programmer Should Know About User Experience
9.7 Things Every Programmer Should Know About User Experience9.7 Things Every Programmer Should Know About User Experience
9.7 Things Every Programmer Should Know About User Experience
 
Open Data Vorlesung 2015: Open Corporate Data
Open Data Vorlesung 2015: Open Corporate DataOpen Data Vorlesung 2015: Open Corporate Data
Open Data Vorlesung 2015: Open Corporate Data
 
An overview of open source in East Asia (China, Japan, Korea)
An overview of open source in East Asia (China, Japan, Korea)An overview of open source in East Asia (China, Japan, Korea)
An overview of open source in East Asia (China, Japan, Korea)
 
Enterprise Developer Journey to the IoT
Enterprise Developer Journey to the IoTEnterprise Developer Journey to the IoT
Enterprise Developer Journey to the IoT
 
Tui the phoenix project book review
Tui the phoenix project book reviewTui the phoenix project book review
Tui the phoenix project book review
 
SXSW Hacking RSS: Filtering & Processing Obscene Amounts of Information
SXSW Hacking RSS: Filtering & Processing Obscene Amounts of InformationSXSW Hacking RSS: Filtering & Processing Obscene Amounts of Information
SXSW Hacking RSS: Filtering & Processing Obscene Amounts of Information
 
Cloud State of the Union for Java Developers
Cloud State of the Union for Java DevelopersCloud State of the Union for Java Developers
Cloud State of the Union for Java Developers
 
Devoxx 2011 integration-camel-cxf-servicemix-activemq
Devoxx 2011 integration-camel-cxf-servicemix-activemqDevoxx 2011 integration-camel-cxf-servicemix-activemq
Devoxx 2011 integration-camel-cxf-servicemix-activemq
 
My 'Phoenix Project'—One Developer's Evolutionary Journey
My 'Phoenix Project'—One Developer's Evolutionary JourneyMy 'Phoenix Project'—One Developer's Evolutionary Journey
My 'Phoenix Project'—One Developer's Evolutionary Journey
 
Event Report - Acumatica Summit 2017
Event Report - Acumatica Summit 2017Event Report - Acumatica Summit 2017
Event Report - Acumatica Summit 2017
 
Understand Open Source ecosystems
Understand Open Source ecosystemsUnderstand Open Source ecosystems
Understand Open Source ecosystems
 
Trusty URIs: Verifiable, Immutable, and Permanent Digital Artifacts for Linke...
Trusty URIs: Verifiable, Immutable, and Permanent Digital Artifacts for Linke...Trusty URIs: Verifiable, Immutable, and Permanent Digital Artifacts for Linke...
Trusty URIs: Verifiable, Immutable, and Permanent Digital Artifacts for Linke...
 
Limited WIP Meeting presentation - The Phoenix Project book review
Limited WIP Meeting presentation - The Phoenix Project book reviewLimited WIP Meeting presentation - The Phoenix Project book review
Limited WIP Meeting presentation - The Phoenix Project book review
 
The Phoenix Project DevOps Simulation - Paul Wilkinson
The Phoenix Project DevOps Simulation - Paul WilkinsonThe Phoenix Project DevOps Simulation - Paul Wilkinson
The Phoenix Project DevOps Simulation - Paul Wilkinson
 
Event Report - ADP ReThink 2017
Event Report - ADP ReThink 2017Event Report - ADP ReThink 2017
Event Report - ADP ReThink 2017
 
Subcultures
SubculturesSubcultures
Subcultures
 
Presentation Zabbix en Français du 6 Juin 2013
Presentation Zabbix en Français du 6 Juin 2013Presentation Zabbix en Français du 6 Juin 2013
Presentation Zabbix en Français du 6 Juin 2013
 
Writing highly scalable WebSocket using the Atmosphere Framework and Scala
Writing highly scalable WebSocket using the Atmosphere Framework and ScalaWriting highly scalable WebSocket using the Atmosphere Framework and Scala
Writing highly scalable WebSocket using the Atmosphere Framework and Scala
 
Towards complex adaptive architectures
Towards complex adaptive architecturesTowards complex adaptive architectures
Towards complex adaptive architectures
 
Progress Report - Oracle HCM Analyst Summit 2017
Progress Report - Oracle HCM Analyst Summit 2017Progress Report - Oracle HCM Analyst Summit 2017
Progress Report - Oracle HCM Analyst Summit 2017
 

Plus de Nicolas Martignole

Recettes, services et API pour vos équipes et vos développeurs
Recettes, services et API pour vos équipes et vos développeursRecettes, services et API pour vos équipes et vos développeurs
Recettes, services et API pour vos équipes et vos développeursNicolas Martignole
 
Le Personal Branding pour les Développeurs (mais pas que...)
Le Personal Branding pour les Développeurs (mais pas que...)Le Personal Branding pour les Développeurs (mais pas que...)
Le Personal Branding pour les Développeurs (mais pas que...)Nicolas Martignole
 
Devoxx Maroc 2015 HTTP 1, HTTP 2 and folks
Devoxx Maroc  2015 HTTP 1, HTTP 2 and folksDevoxx Maroc  2015 HTTP 1, HTTP 2 and folks
Devoxx Maroc 2015 HTTP 1, HTTP 2 and folksNicolas Martignole
 
Play! framework : Soft-Shake presentation
Play! framework : Soft-Shake presentationPlay! framework : Soft-Shake presentation
Play! framework : Soft-Shake presentationNicolas Martignole
 
Usi2010 presentation nmartignole slideshare
Usi2010 presentation nmartignole slideshareUsi2010 presentation nmartignole slideshare
Usi2010 presentation nmartignole slideshareNicolas Martignole
 

Plus de Nicolas Martignole (7)

Recettes, services et API pour vos équipes et vos développeurs
Recettes, services et API pour vos équipes et vos développeursRecettes, services et API pour vos équipes et vos développeurs
Recettes, services et API pour vos équipes et vos développeurs
 
Le Personal Branding pour les Développeurs (mais pas que...)
Le Personal Branding pour les Développeurs (mais pas que...)Le Personal Branding pour les Développeurs (mais pas que...)
Le Personal Branding pour les Développeurs (mais pas que...)
 
Devoxx Maroc 2015 HTTP 1, HTTP 2 and folks
Devoxx Maroc  2015 HTTP 1, HTTP 2 and folksDevoxx Maroc  2015 HTTP 1, HTTP 2 and folks
Devoxx Maroc 2015 HTTP 1, HTTP 2 and folks
 
Voyager avec play scala
Voyager avec play scalaVoyager avec play scala
Voyager avec play scala
 
Play! framework : Soft-Shake presentation
Play! framework : Soft-Shake presentationPlay! framework : Soft-Shake presentation
Play! framework : Soft-Shake presentation
 
Recettes d'une passion
Recettes d'une passionRecettes d'une passion
Recettes d'une passion
 
Usi2010 presentation nmartignole slideshare
Usi2010 presentation nmartignole slideshareUsi2010 presentation nmartignole slideshare
Usi2010 presentation nmartignole slideshare
 

Play2 ou l'architecture web réactive

  • 1. @nmartignole Nicolas Martignole Février 2013 ZapTravel vendredi 22 mars 13
  • 2. L’objectif de cette présentation est de... Découvrir Play2/Scala vendredi 22 mars 13
  • 8. Framework Web ZapTravel vendredi 22 mars 13
  • 9. Java et Scala ZapTravel vendredi 22 mars 13
  • 10. Créé par Guillaume Bort @guillaumebort ZapTravel vendredi 22 mars 13
  • 11. Rails pour Java/Scala ZapTravel vendredi 22 mars 13
  • 12. Simple, productif Sauvegardez, rechargez, c’est tout ZapTravel vendredi 22 mars 13
  • 13. Communauté Java ZapTravel vendredi 22 mars 13
  • 15. 2 Livres en préparation vendredi 22 mars 13
  • 16. Démonstration ZapTravel vendredi 22 mars 13
  • 17. Routes GET / Application.index ZapTravel vendredi 22 mars 13
  • 18. Scala def index() = Action { val name ="Nicolas" Ok(views.html.Application.index(name)) } ZapTravel vendredi 22 mars 13
  • 19. Scala - 2 @(name: String) @myTemplate() { <h1>Hello @name</h1> ... } ZapTravel vendredi 22 mars 13
  • 20. Play 2 en bref ZapTravel vendredi 22 mars 13
  • 21. Play 2 en bref • RESTful • Sécurité (XSS,CSRF) • Compilateur • Java NIO CoffeScript, Less • Driver asynchrone • JSON pour MongoDB • Websocket, Server • Require.js Sent Event, Comet • NoSQL et BigData ZapTravel vendredi 22 mars 13
  • 22. Quelques exemples ZapTravel vendredi 22 mars 13
  • 23. Charger une donnée venant de Redis ZapTravel vendredi 22 mars 13
  • 24. Architecture Redis Air/Hotel/Cars/Ac Redis Web Resa/Users Web Redis HTTP Web Content HTTPS LB Web ZapTravel vendredi 22 mars 13
  • 25. Architecture Redis Air/Hotel/Cars/Ac Web Redis Web Resa/Users Web Redis HTTP HTTPS redis Web Content LB Web ZapTravel vendredi 22 mars 13
  • 26. Cas d’usage Donne moi le label qui correspond à originId =380 ZapTravel vendredi 22 mars 13
  • 27. Cas d’usage 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)) } ZapTravel vendredi 22 mars 13
  • 28. Cas d’usage 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)) } ZapTravel vendredi 22 mars 13
  • 29. Cas d’usage 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/sedis ZapTravel vendredi 22 mars 13
  • 30. Un mot sur les Tests vendredi 22 mars 13
  • 31. 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 } } } } https://gist.github.com/nicmarti/5064048 vendredi 22 mars 13
  • 32. Charger un objet Charge moi un Objet «Londres» ZapTravel vendredi 22 mars 13
  • 33. Charger un objet Origine 1) charger from-london 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 { .... ... } } } ZapTravel vendredi 22 mars 13
  • 34. Cas d’usage 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 { .... ... } } } ZapTravel vendredi 22 mars 13
  • 35. Cas d’usage 2) charger display... 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) } } for-comprehension https://gist.github.com/nicmarti/5064066 ZapTravel vendredi 22 mars 13
  • 36. La Tour Eiffel 1. Charger du JSON à partir de Redis 2. Interpréter et retourner un objet PointOfInterest ZapTravel vendredi 22 mars 13
  • 37. HGET Pois:PoisHash 52511 {"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"]} vendredi 22 mars 13
  • 38. 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
  • 39. 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
  • 41. Appel Redis et interprétation JSON vendredi 22 mars 13
  • 42. Afficher une liste ZapTravel vendredi 22 mars 13
  • 43. Afficher une liste ZapTravel vendredi 22 mars 13
  • 44. Aller sur Redis def allOrigins: List[Origin] = Redis.pool.withClient { client => // ... // ... } Modèle vendredi 22 mars 13
  • 45. Préparer une liste def allUrlOrigins: Seq[(String, String)] = { Origin.allOrigins.map{ origin => (origin.slug, origin.label) }.sortBy(_._2) } Contrôleur vendredi 22 mars 13
  • 46. Envoyer la liste au template Code dans la page HTML <label for="location">Your travel origin is :</label> @select( userForm("originCity"), FolioCriteria.allUrlOrigins , '_label -> "Travel from origin", '_showConstraints -> false ) Vue vendredi 22 mars 13
  • 47. Afficher une liste ZapTravel vendredi 22 mars 13
  • 48. Gérer l’authentification vendredi 22 mars 13
  • 49. Comment protéger l’accès à une ressource ? My Info vendredi 22 mars 13
  • 50. Comment protéger l’accès à une ressource ? My Info vendredi 22 mars 13
  • 51. Dans le Controller object Application extends Controller { def index = Action { implicit request => val username="test" Ok(html.index(username)) } } vendredi 22 mars 13
  • 52. Dans le Controller object Application extends Controller with Secured { def index = ActionSecure { username => implicit request => Ok(html.index(username)) } } vendredi 22 mars 13
  • 53. 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 String Request[AnyContent] HTML vendredi 22 mars 13
  • 54. Play2 et Sécurité • Simple • Composable • Facile à tester vendredi 22 mars 13
  • 55. Optimiser l’indexation et le référencement vendredi 22 mars 13
  • 56. 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
  • 58. routes Compilé et validé vendredi 22 mars 13
  • 59. URL /from-boston/quality GET /$origin<from-(.*)>/:classifier controllers.Frontoffice.home(origin:String, classifier: String) http://www.zaptravel.com/romance/weekend-deals/from-paris/to-athens/ 12-Apr-2013-to-14-Apr-2013/elite-athens-greece vendredi 22 mars 13
  • 60. Play2 • La séparation entre la partie routage et la partie contrôleur permet de créer des URLs «propres» vendredi 22 mars 13
  • 61. 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
  • 63. Problème : construire le sitemap de façon asynchrone vendredi 22 mars 13
  • 64. Solution : Async Akka / Play2 vendredi 22 mars 13
  • 65. 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
  • 66. 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
  • 67. 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
  • 68. 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
  • 69. 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, Bref... 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") } curl http://www.zaptravel.com/sitemap.xml Async { implicit val timeout = akka.util.Timeout(60 seconds) longCall } } vendredi 22 mars 13
  • 71. Comment améliorer les performances ? vendredi 22 mars 13
  • 72. 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
  • 73. Exemple sur /from-paris/quality Navigateur Play2 GET /from-paris/quality vendredi 22 mars 13
  • 74. Exemple sur /from-paris/quality Navigateur Play2 OK HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 ETag: 11299930771 Cache-Control: max-age=600, s-maxage=600, must-revalidate Content-Length: 103586 ... ... ce n’est pas une erreur vendredi 22 mars 13
  • 75. Recharge /from-paris/quality GET /from-paris/quality If-None-Match: 112999307771 Navigateur Play2 304 Not Modified Content-Length: 0 vendredi 22 mars 13
  • 76. 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
  • 77. Optimisation 2 Faire de la gestion de cache applicative vendredi 22 mars 13
  • 79. 2 types de cache Cache technique type Cache de Play2 ou Redis Varnish - Code applicatif - Process à part - utilise la mémoire - Cache HTTP de Play2 ou Redis vendredi 22 mars 13
  • 80. 2 types de cache Cache technique type Varnish • Facile à installer • Evite de solliciter Play2 • Scalable • Configurable vendredi 22 mars 13
  • 81. 2 types de cache Cache applicatif Play2/ Redis • Prend en compte le métier • Permet de garder les pages «authentifiées» • Pas aussi performant que la solution Varnish vendredi 22 mars 13
  • 82. 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
  • 83. Et pour terminer Play2 Architecture Web Apprentissage Tests unitaires Asynchrone (Enumeratee, Iteratee) vendredi 22 mars 13
  • 84. Merci https://joind.in/7951 @nmartignole vendredi 22 mars 13