Streaming JSON-responses serveren voor snellere interfaces

Moderne websites zijn complex en moeten gegevens vaak asynchroon op de achtergrond laden. JSON-streaming is een manier om dat mogelijk te maken.

Wat is JSON-streaming?

JSON-streaming, of JSONS, is een eenvoudige techniek waarmee we de zoekvertraging voor gebruikers verkleinen, vooral bij een trage verbinding. Mobiele gebruikers met HTTPS-verbindingen merken de grootste verbetering, maar iedereen heeft er baat bij. Voor JSONS heb je geen ingewikkelde JSON-parsers nodig: verstuur gewoon JSON die per regel is gescheiden. De streaming-API van Twitter werkt al jaren zo.

Waarom je een JSON-stream zou moeten gebruiken

Onze controletool voor gebruikersnamen op sociale media laat eenvoudig zien hoe dit werkt. We controleren of je zoekterm beschikbaar is op Facebook, Twitter en Pinterest door op elke website een HTTP HEAD-verzoek voor de naam uit te voeren. De resultaten komen in willekeurige volgorde binnen en we willen ze zo snel mogelijk tonen. Dat zouden we kunnen oplossen met drie HTTPS-verzoeken, maar dat betekent drie TCP-verbindingen, drie SSL-handshakes enzovoort. We kunnen dit vermijden, en waarschijnlijk een bestaande HTTPS-verbinding hergebruiken, door slechts één verzoek te versturen:

https://instantdomainsearch.com/services/vanity/apple?hash=866016287

Het streamingantwoord is vergelijkbaar met, en afhankelijk van, HTTP chunked transfer encoding. De beste manier om te zien hoe dit werkt, is de verbinding met curl te volgen:

curl --trace - "https://instantdomainsearch.com/services/vanity/apple?hash=866016287"
…
<= Recv data, 45 bytes (0x2d)
0000: 32 37 0d 0a 7b 22 54 79 70 65 22 3a 22 54 77 69 27..{"Type":"Twi
0010: 74 74 65 72 22 2c 22 49 73 52 65 67 69 73 74 65 tter","IsRegiste
0020: 72 65 64 22 3a 74 72 75 65 7d 0a 0d 0a          red":true}...
{"Type":"Twitter","IsRegistered":true}
<= Recv data, 46 bytes (0x2e)
0000: 32 38 0d 0a 7b 22 54 79 70 65 22 3a 22 46 61 63 28..{"Type":"Fac
0010: 65 62 6f 6f 6b 22 2c 22 49 73 52 65 67 69 73 74 ebook","IsRegist
0020: 65 72 65 64 22 3a 74 72 75 65 7d 0a 0d 0a       ered":true}...
{"Type":"Facebook","IsRegistered":true}
<= Recv data, 52 bytes (0x34)
0000: 32 39 0d 0a 7b 22 54 79 70 65 22 3a 22 50 69 6e 29..{"Type":"Pin
0010: 74 65 72 65 73 74 22 2c 22 49 73 52 65 67 69 73 terest","IsRegis
0020: 74 65 72 65 64 22 3a 74 72 75 65 7d 0a 0d 0a 30 tered":true}...0
0030: 0d 0a 0d 0a                                     ....
{"Type":"Pinterest","IsRegistered":true}
== Info: Curl_http_done: called premature == 0
== Info: Connection #0 to host instantdomainsearch.com left intact

Misschien kom je de technische dogma’s tegen dat elk eindpunt precies één object moet teruggeven. Of dat netjes opgemaakte JSON regeleinden kan bevatten en je dus eigenlijk een streamingparser nodig hebt. Negeer die. Antwoorden verwerken is zo eenvoudig als splitsen op regeleinden. De bibliotheek jsonpipe is een goed beginpunt voor je clientcode.

Zo zie je het verschil dat JSON maakt

Een goede manier om te zien wat dit voor een mobiele gebruiker betekent, is rvictl op een Mac gebruiken. Daarmee zie je hoe een echte mobiele radio via een echt mobiel netwerk met je server communiceert. Eén HTTPS-verbinding op mijn Verizon 3G-verbinding met twee streepjes bereik ziet er zo uit. De eerste kolom toont de tijd in seconden:

0.000000  DNS  69  Standard query 0x5622 A instantdomainsearch.com
2.573090  DNS  85  Standard query response 0x5622 A instantdomainsearch.com A 130.211.25.142

Wacht, duurde het beantwoorden van de DNS-vraag 2,5 seconden? Wauw. Misschien stond de 3G-modem in slaapstand. Eindelijk kunnen we de TCP-verbinding opzetten:

2.573822  TCP  64  57709 → 443 [SYN] Seq=0 Win=65535 Len=0 MSS=1388 WS=64 TSval=727045548 TSecr=0 SACK_PERM=1
2.574404  TCP  60  443 → 57709 [SYN, ACK] Seq=0 Ack=1 Win=42540 Len=0 MSS=1430 SACK_PERM=1 TSval=2852312183 TSecr=727045548 WS=128

En beginnen aan de SSL/TLS-uitwisseling:

2.575790  TLSv1.2  285  Client Hello
3.582868  TLSv1.2  1428  [TCP Fast Retransmission] Server Hello
3.585061  TLSv1.2  127  Client Key Exchange
3.585771  TLSv1.2  58  Change Cipher Spec
3.586415  TLSv1.2  97  Encrypted Handshake Message
3.587729  TLSv1.2  103  Change Cipher Spec, Hello Request, Hello Request

Hallo! Vanaf dit punt, 3,5 seconden later, versturen en ontvangen we eindelijk versleutelde toepassingsgegevens.

JSON vermindert pakketten en verhoogt de snelheid

Elke toetsaanslag op Instant Domain Search bevraagt meerdere gegevensstructuren voor beschikbaarheidsresultaten, marktplaatsresultaten, suggesties enzovoort. We zagen enorme prestatieverbeteringen voor gebruikers toen we niet meer bij elke toetsaanslag zoveel HTTPS-verbindingen probeerden op te zetten, maar de resultaten via één verbinding per zoekopdracht terugstroomden. Je ziet hoe dit veel minder pakketten oplevert die de netwerkverbinding van de gebruiker belasten. Voor de volledigheid: we probeerden ook een WebSocket, maar ontdekten dat HTTPS-verbindingen per zoekopdracht de betrouwbaarste manier waren om onze gebruikers snel resultaten te leveren.