Microservices met Oracle Helidon

1. Overzicht

Helidon is het nieuwe Java-microserviceframework dat onlangs door Oracle is open source. Het werd intern gebruikt in Oracle-projecten onder de naam J4C (Java for Cloud).

In deze tutorial behandelen we de belangrijkste concepten van het framework en vervolgens gaan we een op Helidon gebaseerde microservice bouwen en uitvoeren.

2. Programmeermodel

Momenteel, het framework ondersteunt twee programmeermodellen voor het schrijven van microservices: Helidon SE en Helidon MP.

Terwijl Helidon SE is ontworpen als een microframework dat het reactieve programmeermodel ondersteunt, is Helidon MP daarentegen een Eclipse MicroProfile-runtime waarmee de Jakarta EE-gemeenschap microservices op een draagbare manier kan uitvoeren.

In beide gevallen is een Helidon-microservice een Java SE-applicatie die een blikkerige HTTP-server start vanaf de hoofdmethode.

3. Helidon SE

In deze sectie zullen we in meer details de belangrijkste componenten van Helidon SE ontdekken: WebServer, Config en Security.

3.1. Het opzetten van de webserver

Om aan de slag te gaan met het WebServer-API, we moeten de vereiste Maven-afhankelijkheid toevoegen aan het pom.xml het dossier:

 io.helidon.webserver helidon-webserver 0.10.4 

Om een ​​eenvoudige webapplicatie te hebben, we kunnen een van de volgende builder-methoden gebruiken: WebServer.create (serverConfig, routing) of gewoon WebServer.create (routering). De laatste heeft een standaard serverconfiguratie waardoor de server op een willekeurige poort kan draaien.

Hier is een eenvoudige webtoepassing die op een vooraf gedefinieerde poort draait. We hebben ook een eenvoudige handler geregistreerd die met een begroetingsbericht op elk HTTP-verzoek reageert met ‘/begroeten' pad en KRIJGEN Methode:

public static void main (String ... args) gooit uitzondering {ServerConfiguration serverConfig = ServerConfiguration.builder () .port (9001) .build (); Routing routing = Routing.builder () .get ("/ greet", (verzoek, antwoord) -> response.send ("Hallo wereld!")). Build (); WebServer.create (serverConfig, routing) .start () .thenAccept (ws -> System.out.println ("Server gestart op: // localhost:" + ws.port ())); }

De laatste regel is om de server te starten en te wachten op het ontvangen van HTTP-verzoeken. Maar als we deze voorbeeldcode in de hoofdmethode uitvoeren, krijgen we de foutmelding:

Uitzondering in thread "main" java.lang.IllegalStateException: Geen implementatie gevonden voor SPI: io.helidon.webserver.spi.WebServerFactory

De Web Server is eigenlijk een SPI, en we moeten een runtime-implementatie bieden. Momenteel, Helidon biedt de NettyWebServer implementatie die is gebaseerd op Netty Core.

Hier is de Maven-afhankelijkheid voor deze implementatie:

 io.helidon.webserver helidon-webserver-netty 0.10.4 runtime 

Nu kunnen we de hoofdtoepassing uitvoeren en controleren of deze werkt door het geconfigureerde eindpunt aan te roepen:

// localhost: 9001 / greet

In dit voorbeeld hebben we zowel de poort als het pad geconfigureerd met behulp van het builderpatroon.

Helidon SE maakt het ook mogelijk om een ​​configuratiepatroon te gebruiken waarbij de configuratiegegevens worden geleverd door het Config API. Dit is het onderwerp van de volgende sectie.

3.2. De Config API

De Config API biedt tools voor het lezen van configuratiegegevens uit een configuratiebron.

Helidon SE biedt implementaties voor veel configuratiebronnen. De standaardimplementatie wordt geleverd door helidon-config waar de configuratiebron een application.properties bestand dat zich onder het klassenpad bevindt:

 io.helidon.config helidon-config 0.10.4 

Om de configuratiegegevens te lezen, hoeven we alleen de standaardbouwer te gebruiken die standaard de configuratiegegevens haalt uit application.properties:

Config config = Config.builder (). Build ();

Laten we een application.properties bestand onder het src / main / resource directory met de volgende inhoud:

server.port = 9080 web.debug = true web.page-size = 15 user.home = C: / Users / app

Om de waarden te lezen kunnen we de Config.get () methode gevolgd door een handige casting naar de overeenkomstige Java-typen:

int port = config.get ("server.port"). asInt (); int pageSize = config.get ("web.page-size"). asInt (); boolean debug = config.get ("web.debug"). asBoolean (); String userHome = config.get ("user.home"). AsString ();

In feite laadt de standaardbouwer het eerst gevonden bestand in deze volgorde van prioriteit: application.yaml, application.conf, application.json en application.properties. De laatste drie formaten hebben een extra gerelateerde configuratie-afhankelijkheid nodig. Om bijvoorbeeld het YAML-formaat te gebruiken, moeten we de gerelateerde YAML-configuratie-afhankelijkheid toevoegen:

 io.helidon.config helidon-config-yaml 0.10.4 

En dan voegen we een application.yml:

server: poort: 9080 web: debug: ware paginagrootte: 15 gebruiker: home: C: / Users / app

Evenzo moeten we de afhankelijkheid helidon-config-hocon toevoegen om de CONF, een vereenvoudigd JSON-formaat, of JSON-formaten te gebruiken.

Merk op dat de configuratiegegevens in deze bestanden kunnen worden overschreven door omgevingsvariabelen en Java System-eigenschappen.

We kunnen ook het standaardgedrag van de builder regelen door Omgevingsvariabele en Systeemeigenschappen uit te schakelen of door expliciet de configuratiebron op te geven:

ConfigSource configSource = ConfigSources.classpath ("application.yaml"). Build (); Config config = Config.builder () .disableSystemPropertiesSource () .disableEnvironmentVariablesSource () .sources (configSource) .build ();

Naast het lezen van configuratiegegevens van het klassenpad, kunnen we ook twee externe bronconfiguraties gebruiken, dat wil zeggen de git- en de etcd-configuraties. Hiervoor hebben we de afhankelijkheden helidon-config-git en helidon-git-etcd nodig.

Ten slotte, als al deze configuratiebronnen niet aan onze behoefte voldoen, stelt Helidon ons in staat om een ​​implementatie voor onze configuratiebron te bieden. We kunnen bijvoorbeeld een implementatie leveren die de configuratiegegevens uit een database kan lezen.

3.3. De Routing API

De Routing API biedt het mechanisme waarmee we HTTP-verzoeken aan Java-methoden binden. We kunnen dit bereiken door de verzoekmethode en het pad te gebruiken als overeenkomende criteria of de Verzoek Predicaat object voor het gebruik van meer criteria.

Om een ​​route te configureren, kunnen we dus gewoon de HTTP-methode als criterium gebruiken:

Routing routing = Routing.builder () .get ((verzoek, antwoord) -> {});

Of we kunnen de HTTP-methode combineren met het verzoekpad:

Routing routing = Routing.builder () .get ("/ pad", (verzoek, antwoord) -> {});

We kunnen ook de Verzoek Predicaat voor meer controle. We kunnen bijvoorbeeld controleren op een bestaande header of op het inhoudstype:

Routing routing = Routing.builder () .post ("/ save", RequestPredicate.whenRequest () .containsHeader ("header1") .containsCookie ("cookie1"). Accepteert (MediaType.APPLICATION_JSON) .containsQueryParameter ("param1"). hasContentType ("application / json") .thenApply ((request, response) -> {}) .otherwise ((request, response) -> {})) .build ();

Tot nu toe hebben we handlers geleverd in de functionele stijl. We kunnen ook de Onderhoud klasse die het schrijven van handlers op een meer verfijnde manier mogelijk maakt.

Laten we dus eerst een model maken voor het object waarmee we werken, de Boek klasse:

openbare klasse Boek {privé String-id; private String naam; private String-auteur; privé Integer-pagina's; // ...}

We kunnen REST-services maken voor het Boek class door het implementeren van de Service.update () methode. Hierdoor kunnen de subpaden van dezelfde bron worden geconfigureerd:

public class BookResource implementeert Service {private BookManager bookManager = nieuwe BookManager (); @Override public void update (Routing.Rules rules) {rules .get ("/", this :: books) .get ("/ {id}", this :: bookById); } private void bookById (ServerRequest serverRequest, ServerResponse serverResponse) {String id = serverRequest.path (). param ("id"); Boek boek = bookManager.get (id); JsonObject jsonObject = uit (boek); serverResponse.send (jsonObject); } privé ongeldige boeken (ServerRequest serverRequest, ServerResponse serverResponse) {List books = bookManager.getAll (); JsonArray jsonArray = van (boeken); serverResponse.send (jsonArray); } // ...}

We hebben ook het mediatype geconfigureerd als JSON, dus hiervoor hebben we de helidon-webserver-json-afhankelijkheid nodig:

 io.helidon.webserver helidon-webserver-json 0.10.4 

Tenslotte, wij gebruiken de registreren() methode van de Routing builder om het hoofdpad aan de bron te binden. In dit geval, Paden geconfigureerd door de service worden voorafgegaan door het rootpad:

Routing routing = Routing.builder () .register (JsonSupport.get ()) .register ("/ books", nieuwe BookResource ()) .build ();

We kunnen nu de server starten en de eindpunten controleren:

// localhost: 9080 / books // localhost: 9080 / books / 0001-201810

3.4. Veiligheid

In deze sectie we gaan onze bronnen beveiligen met behulp van de beveiligingsmodule.

Laten we beginnen met het aangeven van alle noodzakelijke afhankelijkheden:

 io.helidon.security helidon-security 0.10.4 io.helidon.security helidon-security-provider-http-auth 0.10.4 io.helidon.security helidon-security-integratie-webserver 0.10.4 

De afhankelijkheden helidon-security, helidon-security-provider-http-auth en helidon-security-integration-webserver zijn beschikbaar via Maven Central.

De beveiligingsmodule biedt veel providers voor authenticatie en autorisatie. Voor dit voorbeeld gebruiken we de HTTP-basisauthenticatieprovider omdat het vrij eenvoudig is, maar het proces voor andere providers bijna hetzelfde is.

Het eerste dat u moet doen, is een Veiligheid voorbeeld. We kunnen het voor de eenvoud programmatisch doen:

Map users = // ... UserStore store = user -> Optional.ofNullable (users.get (user)); HttpBasicAuthProvider httpBasicAuthProvider = HttpBasicAuthProvider.builder () .realm ("myRealm") .subjectType (SubjectType.USER) .userStore (store) .build (); Beveiligingsbeveiliging = Security.builder () .addAuthenticationProvider (httpBasicAuthProvider) .build ();

Of we kunnen een configuratiebenadering gebruiken.

In dit geval declareren we alle beveiligingsconfiguraties in het application.yml bestand dat we laden via het Config API:

#Config 4 Security ==> Toegewezen aan beveiliging Objectbeveiliging: providers: - http-basic-auth: realm: "helidon" hoofdtype: USER # Kan GEBRUIKER of SERVICE zijn, standaard is GEBRUIKER-gebruikers: - login: "gebruiker" wachtwoord: "gebruiker" rollen: ["ROLE_USER"] - login: "admin" wachtwoord: "admin" rollen: ["ROLE_USER", "ROLE_ADMIN"] #Config 4 Beveiliging Webserverintegratie ==> Toegewezen aan WebSecurity Object web- server: securityDefaults: authenticate: true paden: - pad: "/ gebruiker" methoden: ["get"] rollen-toegestaan: ["ROLE_USER", "ROLE_ADMIN"] - pad: "/ admin" methoden: ["get"] rollen toegestaan: ["ROLE_ADMIN"]

En om het te laden, hoeven we alleen maar een Config object en dan roepen we de Security.fromConfig () methode:

Config config = Config.create (); Beveiliging security = Security.fromConfig (config);

Zodra we de Veiligheid we moeten het bijvoorbeeld eerst registreren met de Web Server de ... gebruiken WebSecurity.from () methode:

Routing routing = Routing.builder () .register (WebSecurity.from (beveiliging) .securityDefaults (WebSecurity.authenticate ())) .build ();

We kunnen ook een Webbeveiliging instantie direct met behulp van de configuratiebenadering waarmee we zowel de beveiliging als de webserverconfiguratie laden:

Routing routing = Routing.builder () .register (WebSecurity.from (config)) .build ();

We kunnen nu enkele handlers toevoegen voor de /gebruiker en /beheerder paden, start de server en probeer ze te openen:

Routing routing = Routing.builder () .register (WebSecurity.from (config)) .get ("/ user", (request, response) -> response.send ("Hallo, ik ben Helidon SE")) .get ("/ admin", (verzoek, antwoord) -> response.send ("Hallo, ik ben Helidon SE")) .build ();

4. Helidon MP

Helidon MP is een implementatie van Eclipse MicroProfile en biedt ook een runtime voor het uitvoeren van op MicroProfile gebaseerde microservices.

Omdat we al een artikel hebben over Eclipse MicroProfile, zullen we die broncode bekijken en aanpassen om op Helidon MP te draaien.

Nadat we de code hebben uitgecheckt, verwijderen we alle afhankelijkheden en plug-ins en voegen we de Helidon MP-afhankelijkheden toe aan het POM-bestand:

 io.helidon.microprofile.bundels helidon-microprofile-1.2 0.10.4 org.glassfish.jersey.media jersey-media-json-binding 2.26 

De afhankelijkheden helidon-microprofile-1.2 en jersey-media-json-binding zijn verkrijgbaar bij Maven Central.

De volgende, we zullen de bonen.xml bestand onder het src / main / resource / META-INF directory met deze inhoud:

In de BibliotheekApplication class, overschrijven getClasses () methode zodat de server niet naar bronnen zoekt:

@Override openbare set getClasses () {return CollectionsHelper.setOf (BookEndpoint.class); }

Maak ten slotte een hoofdmethode en voeg dit codefragment toe:

openbare statische leegte hoofd (String ... args) {Server server = Server.builder () .addApplication (LibraryApplication.class) .port (9080) .build (); server.start (); }

En dat is het. We kunnen nu alle boekbronnen oproepen.

5. Conclusie

In dit artikel hebben we de belangrijkste componenten van Helidon onderzocht en ook laten zien hoe u Helidon SE en MP instelt. Omdat Helidon MP slechts een Eclipse MicroProfile-runtime is, kunnen we elke bestaande MicroProfile-gebaseerde microservice gebruiken.

Zoals altijd is de code van alle bovenstaande voorbeelden te vinden op GitHub.