Въведение в Vert.x, най-бързата Java рамка днес

Ако наскоро сте потърсили в Google „най-добрата уеб рамка“, може би сте попаднали на бенчмарковете на Techempower, където са класирани повече от триста рамки. Там може би сте забелязали, че Vert.x е един от най-добре класираните, ако не и първият по някои показатели.

Така че нека поговорим за това.

Vert.x е многослойна уеб рамка, която споделя общи функционалности сред поддържаните езици Java, Kotlin, Scala, Ruby и Javascript. Независимо от езика, Vert.x работи на Java Virtual Machine (JVM). Тъй като е модулен и лек, той е насочен към развитие на микроуслуги.

Тестовете на Techempower измерват ефективността на актуализирането, извличането и доставянето на данни от база данни. Колкото повече заявки се обслужват в секунда, толкова по-добре. В такъв IO сценарий, при който са включени малко изчисления, всяка неблокираща рамка би имала предимство. През последните години подобна парадигма е почти неотделима от Node.js, който я популяризира със своя еднонишков цикъл на събития.

Vert.x, подобно на Node, управлява един цикъл на събития. Но Vert.x също се възползва от JVM. Докато Node работи на едно ядро, Vert.x поддържа пул от нишки с размер, който може да съответства на броя на наличните ядра. С по-голяма паралелна поддръжка Vert.x е подходящ не само за IO, но и за тежки процесори, които изискват паралелни изчисления.

Циклите на събитията обаче са половината от историята. Другата половина няма много общо с Vert.x.

За да се свърже с база данни, клиентът изисква драйвер за конектор. В сферата на Java най-често срещаният драйвер за Sql е JDBC. Проблемът е, че този драйвер блокира. И блокира на ниво сокет. Нишката винаги ще заседне там, докато се върне с отговор.

Излишно е да се споменава, че водачът е пречка при реализирането на напълно неблокиращо приложение. За щастие има напредък (макар и неофициален) по асинхронния драйвер с няколко активни разклонения, сред които:

  • //github.com/jasync-sql/jasync-sql (за Postgres и MySql)
  • //github.com/reactiverse/reactive-pg-client (Postgres)

Златното правило

Vert.x е доста лесен за работа и http сървърът може да бъде изведен с няколко реда код.

Методът requestHandler е мястото, където цикълът на събитията доставя събитието на заявката. Тъй като Vert.x не е мнителен, боравенето с него е свободен стил. Но имайте предвид единственото важно правило за неблокираща нишка: не я блокирайте.

Когато работим с паралелност, можем да черпим от толкова много налични днес опции като Promise, Future, Rx, както и от собствения идиоматичен начин на Vert.x. Но тъй като сложността на приложението нараства, наличието на асинхронна функционалност само по себе си не е достатъчно. Също така ни е необходима лекотата на координиране и веригиране на обаждания, като същевременно избягваме ада за обратно извикване, както и изящно предаване на всяка грешка.

Scala Future отговаря на всички условия по-горе с допълнителното предимство, че се основава на принципите на функционалното програмиране. Въпреки че тази статия не изследва в дълбочина Scala Future, можем да я изпробваме с просто приложение. Да приемем, че приложението е API услуга за намиране на потребител, като се посочи неговия идентификатор:

Включени са три операции: проверка на параметъра на заявката, проверка дали идентификаторът е валиден и извличане на данните. Ще обгърнем всяка от тези операции в бъдеще и ще координираме изпълнението в структура „за разбиране“.

  • Първата стъпка е да съчетаете заявката с услуга. Scala има мощна функция за съвпадение на шаблони, която можем да използваме за тази цел. Тук прихващаме всяко споменаване на „/ потребител“ и го предаваме в нашата услуга.
  • Следва ядрото на тази услуга, където нашите фючърси са подредени в последователно разбиране. Първата бъдеща проверка на параметрите на f1 . Ние специално искаме да извлечем идентификатора от заявката за получаване и да го хвърлим в int. (Scala не изисква изрично връщане, ако връщаната стойност е последният ред в метода.) Както виждате, тази операция може потенциално да хвърли изключение, тъй като id може да не е int или дори да не е налице, но засега е добре .
  • Второто бъдещо f2 проверява валидността на id. Блокираме всеки идентификатор по-нисък от 100, като изрично извикваме Future.failed със собствения си CustomException. В противен случай изпращаме празно бъдеще под формата на Future.unit като успешно валидиране.
  • Последното бъдещо f3 извлича потребителя с идентификатора, предоставен от f1. Тъй като това е само пример, ние всъщност не се свързваме с база данни. Ние просто връщаме някакъв фалшив низ.
  • map изпълнява подредбата, която дава потребителски данни от f3, след което ги отпечатва в отговора.
  • Сега, ако в която и да е част от последователността възникне грешка, се предава Throwable за възстановяване . Тук можем да съпоставим типа му с подходяща стратегия за възстановяване. Поглеждайки назад в нашия код, сме очаквали няколко потенциални грешки, като липсващ идентификатор или идентификатор, който не е int или невалиден, което би довело до конкретни изключения. Ние обработваме всеки от тях в handleException, като предаваме съобщение за грешка на клиента.

Тази подредба осигурява не само асинхронен поток от началото до края, но и чист подход за обработка на грешките. И тъй като тя е рационализирана между манипулаторите, ние можем да се съсредоточим върху важни неща, като заявка за база данни.

Verticles, Bus Bus и други проблеми

Vert.x предлага и модел на паралелност, наречен verticle, който прилича на системата Actor. (Ако искате да научите повече, преминете към моето ръководство за актьора на Akka.) Verticle изолира състоянието и поведението си, за да осигури среда, безопасна за нишки. Единственият начин за комуникация с него е чрез шина за събития.

Шината за събития Vert.x обаче изисква нейните съобщения да бъдат String или JSON. Това затруднява предаването на произволни обекти, които не са POJO. И в една високоефективна система, справянето с преобразуването на JSON е нежелателно, тъй като налага някои изчислителни разходи. Ако разработвате IO приложения, може да е по-добре да не използвате нито вертикала, нито шината на събитията, тъй като такива приложения имат малка нужда от локално състояние.

Работата с някои компоненти на Vert.x също може да бъде доста предизвикателна. Може да откриете липса на документация, неочаквано поведение и дори неработене. Vert.x може да страда от собствената си амбиция, тъй като разработването на нови компоненти ще изисква пренасяне на много езици. Това е трудно начинание. По тази причина придържането към сърцевината би било най-доброто.

Ако разработвате публичен API, тогава vertx-core трябва да е достатъчен. Ако това е уеб приложение, можете да добавите vertx-web, който осигурява обработка на http параметри и JWT / Session удостоверяване. Тези две са тези, които така или иначе доминираха в еталоните. Има известно намаляване на производителността в някои тестове за използване на vertx-web, но тъй като изглежда, че произтича от оптимизацията, може да се изглади в следващите версии.