Slide 1

Slide 1 text

Heavy Lifting trivago goes Symfony

Slide 2

Slide 2 text

Intro Foto: probe.ch / flickr

Slide 3

Slide 3 text

Mario Müller

Slide 4

Slide 4 text

Worum geht's? ● Bestandsaufnahme ● Zielsetzung ● Evaluierung ● Konzept / Umsetzung ● Lesson Learned

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

trivago business facts ● Hotel Meta-Suche mit ~130 Partnern und mehr als 550.000 Hotels ● bis zu 1.000.000.000 PHP Req. / Monat ● > 20 Sprachen ● aktuell 30 Länder-Plattformen

Slide 7

Slide 7 text

trivago Teilbereiche ● Hotelsuche und Preisvergleich ● Community mit Bewertungen und Bonusprogramm (UGC) ● Hotelier Plattform (UGC) ● Partner Plattform mit Statistiken und Informationen zur jeweiligen Partner- Anbindung (non-pulic)

Slide 8

Slide 8 text

trivago Technik ● PHP 5.3 / JavaScript / Java / C / Bash ● FreeBSD ● Apache / Percona / Memcache / Redis ● Diverse CDN Provider ● Direkte Level 3 / NTT Anbindung

Slide 9

Slide 9 text

Foto: (c) ibikenz / flickr

Slide 10

Slide 10 text

● PHP 5.2 kompatibler Code ● ca. 1.2 Mio. Zeilen ● Teilweise > 5 Jahre alt ● Ein Mix aus prozedualem, teilw. objektorientiertem und "Ninja" - Code mit $GLOBALS, global und einer Menge "&". "Legacy"

Slide 11

Slide 11 text

Zielsetzung ● Hohe first-byte Performance (um oder unter 200ms, aktuell 175-350ms) ● Steigerung der Wartbarkeit und Erweiterbarkeit ● Testbarkeit des Codes sicherstellen ● Auftrennung des Codes nach OOP Mustern (SOC, IOC, ...)

Slide 12

Slide 12 text

Evaluierung

Slide 13

Slide 13 text

● Ist für trivago technisch der wichtigste KPI ● Alle PHP Requests (weltweit) werden aus Düsseldorf bedient. ● Kein CDN / HTTP Caching für HTML ● Pro Suche zwischen 10 und 100 AJAX Requests (Polling, Filtering, etc.) Performance

Slide 14

Slide 14 text

Zend Framework 2 ● Performance- Overhead ~60ms ● Wenig Dokumentation ● Unklare API Stabilität, da Beta Kontrahenten Flow 3 ● Performance- Overhead ~300ms ● Akzeptable Dokumentation

Slide 15

Slide 15 text

Symfony 2 ● Performance Overhead ~60ms ● Akzeptable Dokumentation ● 2.0 mit mehreren Patch-Releases ● Bonus: Bisherige Architektur Planung passt gut in das Symfony Konzept

Slide 16

Slide 16 text

Cherry-Picking ● Config / Routing / Event Dispatcher ● HttpFoundation ● Service Container ● Twig ● Assetic

Slide 17

Slide 17 text

Allerdings ... ● Kein Doctrine ○ Zu langsam für unsere Bedürfnisse ○ Bei stark denormalisierten Daten macht ein ORM keinen Spaß mehr ● Kein Monolog im "prod" Env. ● XML statt YAML (XSDs, weniger anfällig)

Slide 18

Slide 18 text

Architektur

Slide 19

Slide 19 text

Konzept Idee ● Separation of Concerns !important; ● Web Framework von Business Code trennen (Physikalisch & Architektur) ● trivago's Business Code enthält keine Referenzen auf Symfony APIs ● Ausnahme: Symfony APIs mit eigener Impl. (Security, Event Listener, Twig Extensions).

Slide 20

Slide 20 text

● Components ○ Keine Queries (Kosten / Nutzen) ○ Keine Entities (Dürfen keine Dependencies haben) ● Services ● Business Cases ● Traits als alternativer Ansatz Service Container

Slide 21

Slide 21 text

Grundlegender Aufbau Components (DB Pooling, Locale/Lang Handling, etc) Query (pro Query eine Class) Entities ("dumme" Klassen mit public Properties) Services (Datenbeschaffung durch Components, Cache Steuerung, Aggregation mehrerer Services) Business Cases (Modellierung der Geschäftslogik durch die Verwendung von Services) Symfony2 Controller In manchen Fällen erw- eist sich ein Business Case als ineffizient

Slide 22

Slide 22 text

Unsere "Evolution"

Slide 23

Slide 23 text

Vorab ● Die XML Beispiele in der Online-Doku sind häufig unvollständig, die XSDs sind die bessere Doku. ● Konfiguration pro Hostname + config_local für lokale Überlagerung macht Sinn ● Versioniertes Cache-Dir (Release ID) um zwei Stände gegen einander zu halten + Rollback wenns brennt

Slide 24

Slide 24 text

Alpha Version ● Konnte noch nicht viel, gemessen am Projektziel ● Brauchte 900 - 1500ms bis first-byte ● Ajax Calls lagen bei ~600ms ● Company-interner Test mit ~80 Usern.

Slide 25

Slide 25 text

Symfony 2 ● Listener zusammengefassen, weniger ist mehr ● Serverseitiges Rendern von HTML für AJAX veringert, durch json_encode ersetzen ● Erster Versuch: Universal Classloader ggn XCacheClassloader ersetzen. #fail ● Statischer Autoloader (@arneblankerts) #win

Slide 26

Slide 26 text

Beta Version ● Konnte schon ein Bisschen mehr ● First-byte bei 600 - 700ms ● Ajax Calls bei 400 - 460ms ● Erster Live-Test in Frankreich

Slide 27

Slide 27 text

● Mehrere AppKernels + eigenen AbstractAppKernel bringen Ordnung (und Performance) ● Konfigurationen mit mehr als 4 Environments sucken ● addClassesToCompile() nutzen! Symfony 2

Slide 28

Slide 28 text

Twig ● Autoescape abschalten. Händisch escapen +15% Performance ● ext/Twig nutzen! Twig's getAttribute ist teuer! ● Macros vermeiden, besonders in Loops!

Slide 29

Slide 29 text

Release Candidate ● Funktionalität produktionsreif ● First-byte bei ~300ms ● Ajax Calls bei < 200ms ● Mehrere Live-Tests in Frankreich und Deutschland

Slide 30

Slide 30 text

Assetic ● Base URLs shuffeln ist machbar, reicht uns aber nicht ● Base URL - Bestimmung zur Laufzeit nach Locale notwendig ● Haben wir selbst gebaut auf Basis von Assetic, ist (noch) nicht Open-Source

Slide 31

Slide 31 text

Und dann ... fingen wir an das JavaScript für den IE zu "optimieren" ... ... aber das ist eine andere, längere Geschichte.

Slide 32

Slide 32 text

Vielen Dank