En introduktion til Vert.x, den hurtigste Java-ramme i dag

Hvis du for nylig har googlet "bedste web-ramme", har du måske snuble over Techempower-benchmarks, hvor mere end tre hundrede rammer er rangeret. Der har du måske bemærket, at Vert.x er en af ​​de øverste rangerede, hvis ikke den første af nogle mål.

Så lad os tale om det.

Vert.x er en polyglot-webramme, der deler fælles funktionalitet blandt de understøttede sprog Java, Kotlin, Scala, Ruby og Javascript. Uanset sprog fungerer Vert.x på Java Virtual Machine (JVM). Da den er modulær og let, er den rettet mod udvikling af mikrotjenester.

Techempower-benchmarks måler ydeevnen ved opdatering, hentning og levering af data fra en database. Jo flere anmodninger der serveres pr. Sekund, jo bedre. I et sådant IO-scenarie, hvor lidt computing er involveret, vil enhver ikke-blokerende ramme have en kant. I de senere år er et sådant paradigme næsten uadskilleligt fra Node.js, som populariserede det med sin single-threaded event loop.

Vert.x, som Node, driver en enkelt hændelsesløkke. Men Vert.x drager også fordel af JVM. Mens Node kører på en enkelt kerne, opretholder Vert.x en trådpulje med en størrelse, der kan matche antallet af tilgængelige kerner. Med større samtidighedssupport er Vert.x velegnet til ikke kun IO, men også CPU-tunge processer, der kræver parallel computing.

Begivenhedsløjfer er dog halvdelen af ​​historien. Den anden halvdel har ikke meget at gøre med Vert.x.

For at oprette forbindelse til en database kræver en klient en connectordriver. I Java-verden er JDBC den mest almindelige driver til SQL. Problemet er, at denne driver blokerer. Og det blokerer på sokkelniveau. En tråd sidder altid fast der, indtil den vender tilbage med et svar.

Det er overflødigt at sige, at føreren har været en flaskehals i at realisere et fuldt ikke-blokerende program. Heldigvis har der været fremskridt (omend uofficielt) med en asynkron driver med flere aktive gafler, blandt dem:

  • //github.com/jasync-sql/jasync-sql (til Postgres og MySql)
  • //github.com/reactiverse/reactive-pg-client (Postgres)

Den gyldne regel

Vert.x er ret simpelt at arbejde med, og en http-server kan opdrages med et par kodelinjer.

Metoden requestHandler er hvor begivenhedssløjfen leverer anmodningshændelsen. Da Vert.x ikke er meningsfuldt, er håndtering af det fri stil. Men husk den eneste vigtige regel for ikke-blokerende tråd: bloker ikke den.

Når vi arbejder med samtidighed, kan vi trække på så mange muligheder, der er tilgængelige i dag som løfte, fremtid, Rx såvel som Vert.xs egen idiomatiske måde. Men efterhånden som applikationens kompleksitet vokser, er det ikke nok at have asynk-funktionalitet alene. Vi har også brug for letheden i at koordinere og kæde opkald, mens vi undgår tilbagekald helvede, samt at videregive enhver fejl yndefuldt.

Scala Future opfylder alle ovenstående betingelser med den yderligere fordel at være baseret på funktionelle programmeringsprincipper. Selvom denne artikel ikke udforsker Scala Future i dybden, kan vi prøve det med en simpel app. Lad os sige, at appen er en API-tjeneste for at finde en bruger, der får deres id:

Der er tre operationer involveret: kontrol af anmodningsparameter, kontrol af om id'et er gyldigt og hentning af data. Vi indpakker hver af disse operationer i en fremtid og koordinerer udførelsen i en "for forståelses" struktur.

  • Det første trin er at matche anmodningen med en service. Scala har en stærk mønstertilpasningsfunktion, som vi kan bruge til dette formål. Her opfanger vi enhver omtale af “/ bruger” og videregiver det til vores service.
  • Dernæst er kernen i denne tjeneste, hvor vores fremtid er arrangeret i en sekventiel forståelse. Den første fremtidige f1 indpakker parameterkontrol. Vi ønsker specifikt at hente id'et fra get-anmodningen og kaste det i int. (Scala kræver ikke eksplicit retur, hvis returværdien er den sidste linje i metoden.) Som du ser, kan denne operation muligvis kaste en undtagelse, da id måske ikke er en int eller ikke engang tilgængelig, men det er okay for nu .
  • Den anden fremtidige f2 kontrollerer gyldigheden af ​​id. Vi blokerer ethvert id under 100 ved eksplicit at ringe til Future.failed med vores egen CustomException. Ellers passerer vi en tom fremtid i form af Future.unit som vellykket validering.
  • Den sidste fremtidige f3 henter brugeren med id'et leveret af f1. Da dette kun er en prøve, opretter vi ikke rigtig forbindelse til en database. Vi returnerer bare en mock-streng.
  • map kører det arrangement, der giver brugerdataene fra f3, og udskriver det derefter i svaret.
  • Hvis der nu opstår en fejl i en hvilken som helst del af sekvensen, sendes en kastbar for at gendanne . Her kan vi matche dens type til en passende genopretningsstrategi. Når vi ser tilbage i vores kode, har vi forventet flere potentielle fejl såsom manglende id eller id, der ikke var int eller ikke gyldigt, hvilket ville kaste specifikke undtagelser. Vi håndterer hver af dem i handleException ved at sende en fejlmeddelelse til klienten.

Dette arrangement giver ikke kun en asynkron strømning fra start til slut, men også en ren tilgang til håndteringsfejl. Og da det er strømlinet på tværs af håndterere, kan vi fokusere på ting, der betyder noget, som databaseforespørgsel.

Vertikler, Event Bus og andre gotchas

Vert.x tilbyder også en samtidighedsmodel kaldet verticle, der ligner Actor-systemet. (Hvis du gerne vil vide mere, skal du gå til min Akka Actor-guide.) Verticle isolerer dens tilstand og opførsel for at give et trådsikkert miljø. Den eneste måde at kommunikere med det på er via en begivenhedsbus.

Vert.x-hændelsesbussen kræver imidlertid, at dens meddelelser er String eller JSON. Dette gør det vanskeligt at videregive vilkårlige ikke-POJO objekter. Og i et højtydende system er det uønsket at håndtere JSON-konvertering, da det pålægger nogle beregningsomkostninger. Hvis du udvikler IO-applikationer, kan det være bedre for dig ikke at bruge hverken verticle eller event bus, da sådanne applikationer kun har ringe behov for lokal stat.

Arbejde med nogle Vert.x-komponenter kan også være ret udfordrende. Du kan finde mangel på dokumentation, uventet opførsel og endda manglende funktion. Vert.x lider muligvis af sin egen ambition, da udvikling af nye komponenter vil kræve portering på mange sprog. Dette er en vanskelig opgave. Af den grund ville det være bedst at holde sig til kernen.

Hvis du udvikler en offentlig API, skal vertx-core være nok. Hvis det er en webapp, kan du tilføje vertx-web, som giver http-parameterhåndtering og JWT / session-godkendelse. Disse to er alligevel dem, der dominerede benchmarks. Der er et fald i ydeevne i nogle tests til brug af vertx-web, men da det ser ud til at have stammer fra optimering, kan det blive stryget ud i de efterfølgende udgivelser.