Cookies op Tweakers

Tweakers maakt gebruik van cookies, onder andere om de website te analyseren, het gebruiksgemak te vergroten en advertenties te tonen. Door gebruik te maken van deze website, of door op 'Ga verder' te klikken, geef je toestemming voor het gebruik van cookies. Je kunt ook een cookievrije versie van de website bezoeken met minder functionaliteit. Wil je meer informatie over cookies en hoe ze worden gebruikt, bekijk dan ons cookiebeleid.

Meer informatie

Door , , 23 reacties, 13.283 views •

Binnenkort vervangt Tweakers.net een deel van de code achter de categoriepagina's van de Pricewatch. Dit zou geen zichtbare wijzigingen op moeten leveren, maar wordt wel bewust stapsgewijs ingevoerd. Voor hen die niet in het technische verhaal geïnteresseerd zijn in het kort wat er verandert: aan de werking van de Pricewatch verandert niets, alleen de snelheid waarmee de categoriepagina's worden gegenereerd gaat omhoog. Mocht er ergens wel iets anders werken, dan is dat waarschijnlijk juist een bug. Als je twijfelt, vraag het gerust in ons Pricewatch-forum.

Tweakers.net probeert continu de performance van de website te verbeteren. In de afgelopen paar jaar hebben we aan de serverkant Memcached in gebruik genomen, diverse stukken PHP-code (herhaaldelijk) geoptimaliseerd, databasequeries verbeterd of zelfs door caching overbodig gemaakt en nog diverse andere tweaks uitgevoerd. Aan de clientkant hebben we ook niet stil gezeten met diverse css sprites, een apart image-domein en nog een reeks optimalisaties van de javascript- en de html-structuur.

Ondanks dit alles bleef de performance van de zwaarste categoriepagina's in de Pricewatch ons een doorn in het oog. We streven naar pagina's die server-side binnen 0,1 seconde klaar zijn. Dat klinkt weinig, maar de browser kan doorgaans pas daarna wat nuttigs doen met de gegenereerde content. Een vliegende start is dan een stuk prettiger dan eerst al een tijdje op onze servers te hebben moeten wachten. De langzaamste categoriepagina's van de Pricewatch kosten serverside zelfs meer dan 1 seconde. Dat is niet alleen slecht voor onze trots, het is ook domweg een belemmering voor het vlot kunnen gebruiken van de Pricewatch.

De schoonmaak van de categorieën en de almaar groeiende hoeveelheid producten en specificaties heeft dat probleem alleen maar versterkt. Ondanks onze positieve ervaringen met PHP werd duidelijk dat hiermee geen significante winsten meer te halen zijn. Althans, niet zonder sterk op de functionaliteit, onderhoudbaarheid en flexibiliteit in te boeten. PHP gooit na een pageview alle net verwerkte data weer weg, en moet bij een nieuwe pageview al die data dan ook weer opnieuw ophalen of opbouwen.

Performance Report YslowOm die reden hebben we een engine ontwikkeld die in het geheugen van de servers actief kan blijven, zonder steeds opnieuw allerlei data op te moeten bouwen en weer nodeloos weg te gooien. Door al geruime ervaring met het platform viel de keus op een door onszelf in Java geschreven 'middleware'. Hiermee vormen we een laag tussen de database en PHP, die de filtering, sortering en paginering van productdata op zich neemt en dan de weergave ervan aan onze bestaande PHP-code overlaat.

De implementatie is bewust opgezet met het hergebruik van diverse Java-componenten en een sterke scheiding tussen de bestaande weergavecode en de nieuwe engine in gedachten. We hebben een Servlet ontwikkeld die intern met normale GET-requests kan worden aangesproken en alle benodigde data (een paar honderd MB) domweg in het ram-geheugen houdt. Om die engine te laten weten dat data ververst moet worden stuurt onze PHP-backend berichtjes via ActiveMQ. Omdat er erg veel data moet worden weergegeven, blijven de Pricewatch-categorieën ondanks significante snelheidswinsten (de huidige worst-case pagina's worden mogelijk tot wel 8x sneller) - toch tot de traagste pagina's van onze site behoren. Wel schept deze nieuwe engine ruimte voor verdere verbeteringen, waar we die met onze bestaande engine in PHP nauwelijks meer zagen.

In eerste instantie laten we zowel de oude als de nieuwe engine parallel draaien. Daarbij sturen we in het begin slechts 10 procent van de categoriepagina's via de nieuwe engine, en als alles goed werkt verhogen we dat stapsgewijs in de loop van volgende week tot 100 procent. Als het goed is merken bezoekers hier niets van, behalve dan dat de bewuste pagina's wat sneller moeten worden. We kunnen uiteraard bij het testen van de code van alles over het hoofd gezien hebben, dus mocht je een bug vinden, dan horen we het graag en als je twijfelt, vraag het ons dan.

Door Arjen van der Meijden

- Lead Developer

In Oktober 2001 begonnen met als voornaamste taak het technisch beheer van het forum. Daarna doorgegroeid tot senior developer en software architect. Nu lead developer, met een leidinggevende taak aan het team van programmeurs en systeembeheerders van Tweakers.net.

Reacties (23)

Crisp is hier toch ook mee bezig? Serieus respect voor die man, er zijn veel verbeteringen doorgevoerd de laatste tijd. Natuurlijk ook goed werk van de rest van de optimalisatiecrew.
Aan de performanceverbeteringen werkt iedereen, maar in dit specifieke geval was het volgens mij gallery: ACM die het meeste werk heeft gedaan :)
De css-sprites en diverse javascript-optimalisaties zijn inderdaad voor een groot deel door crisp gedaan. Maar hij is niet de enige die optimalisaties heeft uitgevoerd aan onze clientside performance.

Voor deze engine ben ik overigens verantwoordelijk, crisp heeft daar niet zoveel aan gedaan :)
Om die engine te laten weten dat data ververst moet worden stuurt onze PHP-backend berichtjes via ActiveMQ.
Begrijp ik hieruit dat een medewerker van de pricewatch op een knop moet drukken voordat de gegevens in de pricewatch worden geüpdate?

Hoe moet ik me dat voorstellen met dat JAVA gedeelte? Geeft die aan het PHP script een array of xml terug ofzo?
Nee :)

Maar wijzigingen aan productgegevens, categorieen, etc, worden via de PHP-backend gedaan. Als daar iets mee aangepast wordt, wordt er via ActiveMQ ook een berichtje naar de java-engines gestuurd om te laten weten dat ze de cache voor de net aangepaste data moeten verversen.

Overigens is het in principe ook mogelijk om dat met een lijstje te doen zoals jij je voorstelt, maar dat leek me niet zo handig :P

[Reactie gewijzigd door ACM op 20 februari 2009 13:56]

Heb er de ballen verstand van maar de pricewatch is wel een begrip in "electronica aanschaffend" Nederland. Chapeau!
Een soort databaseserver voor de database dus? :+
Verder goed werk natuurlijk :)

[Reactie gewijzigd door Tom op 20 februari 2009 15:48]

Een soort van eigen implementatie van Memcached oid? Ik weet niet wat die allemaal precies doet trouwens, maar het lijkt me aardig hetzelfde.

Gebruiken jullie een zelfgemaakte cache (of wat dan ook) of bijvoorbeeld een pakket als JCS?

En (ik blijf maar doorgaan), welk formaat zijn de gegevens die jullie terugsturen naar PHP?
Nee juist niet een eigen memcached implementatie, want memcached is gewoon "dom" in de zin dat ie niks doet met wat je er in stopt (behalve verwijderen als het te oud wordt). Deze engine is gewoon een tussenlaag die gegevens zelf cached, filtered en combineert en dan hapklaar naar PHP doorstuurt.
Het is wel zo dat het gros van de memcached-calls die we eerst deden wegvallen hiermee, omdat die aanvullende data ook meegegeven wordt.

De productdata past in de Java-omgeving met gemak in het ram-geheugen (zelfs in 512MB) en is dusdanig specifiek en vaak nodig dat het mij het handigst leek gewoon met normale (Concurrent)HashMaps te werken. De voorwaarden waarbinnen data aangepast moet worden is vrijwel nooit tijds-afhankelijk. Mocht tzt de data niet goed meer in het ram geheugen passen (daarvoor moeten we behoorlijk groeien), dan kunnen we waarschijnlijk beter naar partitiering kijken, dan naar naar disk toe cachen.

Ik wilde in eerste instantie vanuit Java domweg JSON genereren om dat naar de PHP-omgeving te sturen. Maar enerzijds was het standaard JSON-gebeuren zo zwaar dat het alleen met hand-coded JSON vlot ging en daarna bleek dat PHP zo veel tijd kwijt is met de JSON naar eigen objecten te vertalen, dat er uiteindelijk nog maar nauwelijks tijdswinst over bleef.
Uiteindelijk heb ik de output-laag maar omgebouwd zodat ie PHP-serialized tekst uitspuugt. Dat is best wel een rotklus en ook minder efficient dan de hand-coded JSON aan de Java-kant... maar de uiteindelijke winst aan de PHP-kant maakt dat allemaal ruimschoots goed.

[Reactie gewijzigd door ACM op 20 februari 2009 16:35]

Het doet me een beetje denken aan protocol buffers, waar ook communicatie tussen programmeertalen wordt gedaan alleen geen php support.

Is dit hier zeer specifiek opgezet voor de pricewatch of zou het ook algemener gebruikt kunnen worden? Lijkt me iets waar wel vraag voor is, genoeg php sites die uit hun voegen zijn gegroeid.
PHP heeft al jaren serialize() het enige wat ik vanuit Java doe is dat na-apen :P

En dat is het makkelijke deel als je het eenmaal door hebt, de data die van Java naar PHP gestuurd moet worden is juist het lastige deel en dat is inderdaad nogal op maat gemaakt voor de pricewatch.

Een generieke oplossing heeft waarschijnlijk alleen zin als je beide applicaties compleet van de grond af op gaat bouwen en dan nog zit je met mismatches tussen PHP en Java. Afgezien daarvan suggereert een generieke oplossing dat je Reflection moet gaan gebruiken of alsnog zo veel zaken in een API moet steken dat je er weinig baat bij hebt.

Maar er is natuurlijk altijd nog de PHP Java bridge (en Zend heeft ook een variant daarvan), dat werkt op zich wel grappig als je geen noodzaak hebt de maximale prestaties uit de communicatie te halen en vooral om de onderhoudbaarheid of iets dergelijks geeft voor je code.
"en daarna bleek dat PHP zo veel tijd kwijt is met de JSON naar eigen objecten te vertalen".

Json -> Object houdt twee stappen in lijkt me: decode JSON + aanmaken Object. Heb je getest wat de performance is als je puur associative arrays gebruikt? (tenzij je met Objecten de arrays bedoelt...) Ik heb geen enkel probleem met de PHP json_decode($json, true) snelheid. Ik gebruik json als een universeel geaccepteerd serialize formaat, bevalt me prima.

[Reactie gewijzigd door T.T. op 25 februari 2009 06:34]

Ik bedoel inderdaad de associatieve array's (json-objectbomen had ik eerder al afgeschreven) te vertalen naar eigen de objecten binnen PHP. Er was vast nog wat meer mogelijk kwa performance mbt json, maar uiteindelijk is de winst door het domweg als php-serialized objecten te uitspugen en dus php's unserialize te gebruiken een heel stuk groter dan een paar procentjes winst :)
Was de pricewatch dan zo traag? Nooit echt last van gehad (maar misschien dat ik dan achteraf pas die ervaring krijg als de boel geoptimaliseerd is ;) )

Het gaat steeds beter met T.net! Top!!

[Reactie gewijzigd door MikelenniuM op 21 februari 2009 00:31]

Als de zwaarste pagina (laptops categorie) 0,2 sec erover doet om door de servers uit gespuugt te worden is dat best traag. Zeker als je bedenkt dat we streven naar maximaal 0,1 sec (wat al vrij veel is).
Ik heb eigenlijk pas sinds de laatste paar weken dat pricewatch langzamer werkt, dan staan de balken eerst nog niet allemaal in beeld etc. Hiervoor nooit "last" van gehad.
Duurt nu toch cker een seconde wat voor een tweaker een eeuwigheid is natuurlijk :)
Ik heb de snelheid van pricewatch nooit als vervelend ervaren, maar kan mij goed voorstellen dat de betreffende beheerder zich er mateloos aan stoort als je weet dat het beter zou kunnen :) Alhoewel ik altijd al een voorstander ben van het engelse gezegde: don't fix it if it ain't broken. Maar dat is in dit geval denk ik net hoe je er tegenaan kijkt :) Ik vind het prima, maar de beheerder vind het kapot :p
't Liep tegen de limieten van wat wij als acceptabel vonden en tegelijkertijd was de oplossing niet erg schaalbaar. Dus toen hebben we toch maar wat nieuws ontwikkeld :P De werking op zich hebben we niet verandert, dus dat vonden we inderdaad nog niet 'broken'.
Complimenten voor zowel het nut van deze veranderingen als de communicatie naar ons, de leden toe.. en vooral ook het opmerkelijk aanwezig zijn van bijvoorbeeld ACM tussen de reacties :-) ouderwets fijne communicatie.
Idd erg prettig dat we op deze manier op de hoogte worden gehouden. Toevallig gisteren nog mijn eigen site en tweakers zitten vergelijken met de Safari timeline feature en het lijkt erop dat ik op mijn eigen site nog flink wat werk te doen heb :+

Het is me overigens nog niet helemaal duidelijk wat de java engine nu precies doet wat memcache b.v. niet zou kunnen doen? Een pagina als dit:

http://tweakers.net/pricewatch/cat/496

Lijkt me prima cachebaar? Verder lijken de DB queries voor dit soort overzichten ook vrij eenvoudig, het gaat niet om honderduizenden records en ook de presentatie van het geheel ziet er (op het eerste oog :) ) niet heel ingewikkeld uit?

Vraag me eigenlijk vooral af waar dan de grote bottleneck in PHP zat en waarom dat met iets eenvoudigs als Memcached niet op te lossen was?

Ik zou overigens een uitgebreider artikel (review style?) over de performance van tweakers.net erg op prijs stellen. Wat zijn de grote problemen die jullie tegen zijn gekomen en wat hebben jullie gedaan om de snelheid aan zowel voor als achterkant te verbeteren? Dus zowel op html/css vlak als PHP/MySQL als loadbalancer/Apache?

[Reactie gewijzigd door bartvb op 3 maart 2009 14:19]

Een pagina als dit:

http://tweakers.net/pricewatch/cat/496

Lijkt me prima cachebaar?
True, en er werd al cacheing toegepast voor de 'kale' categorie-overzichten. Echter heb je daar niets aan als je binnen die categorie wilt gaan filteren, of een andere sortering wilt gaan toepassen etcetera.
Verder lijken de DB queries voor dit soort overzichten ook vrij eenvoudig, het gaat niet om honderduizenden records en ook de presentatie van het geheel ziet er (op het eerste oog :) ) niet heel ingewikkeld uit?
Het gaat om aanzienlijk meer relaties dan je op het eerste gezicht zou denken. Er hangt een hoop meta-data aan een produkt, en die meta-data is ook weer afhankelijk van andere factoren zoals het soort produkt (voor de specificaties bijvoorbeeld).
Vraag me eigenlijk vooral af waar dan de grote bottleneck in PHP zat en waarom dat met iets eenvoudigs als Memcached niet op te lossen was?
Het probleem met memcached is dat het puur een memory-hashmap is. Je kan er wel een stuk HTML in stoppen, of een array met 100 produkten, maar je kan niet zeggen: ik heb van die 100 produkten de produkten nodig die voldoen aan 'x' en gesorteerd op 'y'. Dat laatste moesten we altijd alsnog in de code doen, maar daarvoor moesten wel elke pageview eerst die 100 produkten opgehaald worden wat al een behoorlijke performancedrain was. Een java-engine kan deze 100 produkten (met bijbehorende meta-data) ook in z'n geheugen houden, en daar kan je wel aan vragen 'geef me die produkten die hier en daar aan voldoen'. PHP zelf kan dat uiteraard niet omdat die geen persistent state kent.
Ik zou overigens een uitgebreider artikel (review style?) over de performance van tweakers.net erg op prijs stellen. Wat zijn de grote problemen die jullie tegen zijn gekomen en wat hebben jullie gedaan om de snelheid aan zowel voor als achterkant te verbeteren? Dus zowel op html/css vlak als PHP/MySQL als loadbalancer/Apache?
In mijn laatste blogpost vertel ik al eea over hoe we performance meten en proberen te verbeteren. Nog niet echt uitgebreid, maar misschien een begin :)
In mijn laatste blogpost vertel ik al eea over hoe we performance meten en proberen te verbeteren. Nog niet echt uitgebreid, maar misschien een begin :)
Indeed, nice!
Dank voor de verdere uitleg ook :)

Ook gekeken naar de shared memory functies van PHP? Nadeel is dat dat allemaal per server gaat maar je bent wel meteen van je netwerk latency af?

(en hoe krijg ik notificaties van nieuwe reacties op deze .plan? :D)

Op dit item kan niet meer gereageerd worden.



Apple iPhone 6Samsung Galaxy Note 4Apple iPad Air 2FIFA 15Motorola Nexus 6Call of Duty: Advanced WarfareApple WatchWorld of Warcraft: Warlords of Draenor, PC (Windows)Microsoft Xbox One 500GBSamsung

© 1998 - 2014 Tweakers.net B.V. Tweakers is onderdeel van De Persgroep en partner van Computable, Autotrack en Carsom.nl Hosting door True