Création de microservices avec Eclipse MicroProfile
1. Vue d'ensemble
Dans cet article, nous nous concentrerons sur la création d'un microservice basé sur Eclipse MicroProfile.
Nous verrons comment écrire une application Web RESTful à l'aide des API JAX-RS, CDI et JSON-P.
2. Une architecture de microservice
En termes simples, les microservices sont un style d'architecture logicielle qui forme un système complet sous la forme d'une collection de plusieurs services indépendants.
Chacun se concentre sur un périmètre fonctionnel et communique avec les autres via un protocole indépendant de la langue, tel que REST.
3. MicroProfile Eclipse
Eclipse MicroProfile est une initiative qui vise à optimiser Enterprise Java pour l’architecture de Microservices. Il est basé sur un sous-ensemble d'API Java EE WebProfile, ce qui nous permet de créer des applications MicroProfile comme nous en construisons des Java EE.
L'objectif de MicroProfile est de définir des API standard pour la création de microservices et de fournir des applications portables sur plusieurs exécutions de MicroProfile.
4. Dépendances Maven
Toutes les dépendances requises pour créer une application Eclipse MicroProfile sont fournies par cette dépendance de nomenclature:
org.eclipse.microprofile
microprofile
1.2
pom
provided
La portée est définie surprovided car le runtime MicroProfile inclut déjà l'API et l'implémentation.
5. Modèle de représentation
Commençons par créer une classe de ressources rapide:
public class Book {
private String id;
private String name;
private String author;
private Integer pages;
// ...
}
Comme nous pouvons le voir, il n'y a pas d'annotation sur cette classeBook.
6. Utilisation de CDI
CDI est une API qui fournit l’injection de dépendances et la gestion du cycle de vie. Cela simplifie l'utilisation des beans entreprise dans les applications Web.
Créons maintenant un bean géré par CDI en tant que magasin pour la représentation du livre:
@ApplicationScoped
public class BookManager {
private ConcurrentMap inMemoryStore
= new ConcurrentHashMap<>();
public String add(Book book) {
// ...
}
public Book get(String id) {
// ...
}
public List getAll() {
// ...
}
}
Nous annotons cette classe avec@ApplicationScoped car nous n'avons besoin que d'une seule instance dont l'état est partagé par tous les clients. Pour cela, nous avons utilisé unConcurrentMap comme magasin de données en mémoire de type sécurisé. Ensuite, nous avons ajouté des méthodes pour les opérationsCRUD.
Maintenant, notre bean est un CDI prêt et peut être injecté dans le beanBookEndpoint using l'annotation@Inject.
7. API JAX-RS
Pour créer une application REST avec JAX-RS, nous devons créer une classeApplication annotée avec@ApplicationPath et une ressource annotée avec@Path.
7.1. Application JAX RS
L'application JAX-RS identifie l'URI de base sous lequel nous exposons la ressource dans une application Web.
Créons l’application JAX-RS suivante:
@ApplicationPath("/library")
public class LibraryApplication extends Application {
}
Dans cet exemple, toutes les classes de ressources JAX-RS de l'application Web sont associées auxLibraryApplication, ce qui les place sous le même cheminlibrary, c'est-à-dire la valeur desApplicationPath annotation.
Cette classe annotée indique au moteur d'exécution JAX RS qu'elle doit rechercher les ressources automatiquement et les expose.
7.2. Point de terminaison JAX RS
Une classeEndpoint, également appelée classeResource, devrait définir une ressource bien que plusieurs des mêmes types soient techniquement possibles.
Chaque classe Java annotée avec@Path, ou ayant au moins une méthode annotée avec@Path or @HttpMethod est un point de terminaison.
Nous allons maintenant créer un point de terminaison JAX-RS qui expose cette représentation:
@Path("books")
@RequestScoped
public class BookEndpoint {
@Inject
private BookManager bookManager;
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response getBook(@PathParam("id") String id) {
return Response.ok(bookManager.get(id)).build();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAllBooks() {
return Response.ok(bookManager.getAll()).build();
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response add(Book book) {
String bookId = bookManager.add(book);
return Response.created(
UriBuilder.fromResource(this.getClass())
.path(bookId).build())
.build();
}
}
À ce stade, nous pouvons accéder à la ressourceBookEndpoint sous le chemin/library/books dans l'application Web.
7.3. Type de support JAX RS JSON
JAX RS prend en charge de nombreux types de supports pour communiquer avec les clients REST, maisEclipse MicroProfile restricts the use of JSON car il spécifie l'utilisation de l'API JSOP-P. En tant que tel, nous devons annoter nos méthodes avec@Consumes(MediaType.APPLICATION_JSON) et @Produces(MediaType.APPLICATION_JSON).
L'annotation@Consumes limite les formats acceptés - dans cet exemple, seul le format de données JSON est accepté. L'en-tête de requête HTTPContent-Type doit êtreapplication/json.
La même idée se cache derrière l'annotation@Produces. Le Runtime JAX RS doit organiser la réponse au format JSON. L'en-tête HTTP de la requêteAccept doit êtreapplication/json.
8. JSON-P
JAX RS Runtime prend en charge JSON-P prêt à l'emploi afin que nous puissions utiliserJsonObject comme paramètre d'entrée de méthode ou type de retour.
Mais dans le monde réel, nous travaillons souvent avec des classes POJO. Nous avons donc besoin d'un moyen de faire le mappage entreJsonObject et POJO. Voici où le fournisseur d'entités JAX RS va jouer.
Pour marshaler le flux d'entrée JSON vers le POJOBook, cela appelle une méthode de ressource avec un paramètre de typeBook,, nous devons créer une classeBookMessageBodyReader:
@Provider
@Consumes(MediaType.APPLICATION_JSON)
public class BookMessageBodyReader implements MessageBodyReader {
@Override
public boolean isReadable(
Class> type, Type genericType,
Annotation[] annotations,
MediaType mediaType) {
return type.equals(Book.class);
}
@Override
public Book readFrom(
Class type, Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap httpHeaders,
InputStream entityStream) throws IOException, WebApplicationException {
return BookMapper.map(entityStream);
}
}
Nous effectuons le même processus pour démarseler un flux de sortieBook vers JSON, c'est-à-dire en appelant une méthode de ressource dont le type de retour estBook, en créant unBookMessageBodyWriter:
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class BookMessageBodyWriter
implements MessageBodyWriter {
@Override
public boolean isWriteable(
Class> type, Type genericType,
Annotation[] annotations,
MediaType mediaType) {
return type.equals(Book.class);
}
// ...
@Override
public void writeTo(
Book book, Class> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap httpHeaders,
OutputStream entityStream) throws IOException, WebApplicationException {
JsonWriter jsonWriter = Json.createWriter(entityStream);
JsonObject jsonObject = BookMapper.map(book);
jsonWriter.writeObject(jsonObject);
jsonWriter.close();
}
}
CommeBookMessageBodyReader etBookMessageBodyWriter sont annotés avec@Provider, ils sont automatiquement enregistrés par le moteur d'exécution JAX RS.
9. Création et exécution de l'application
Une application MicroProfile est portable et doit s'exécuter dans n'importe quel environnement d'exécution MicroProfile conforme. Nous allons expliquer comment créer et exécuter notre application dansOpen Liberty, mais nous pouvons utiliser n'importe quel MicroProfile Eclipse conforme.
Nous configurons le runtime Open Liberty via un fichier de configurationserver.xml:
jaxrs-2.0
cdi-1.2
jsonp-1.0
Ajoutons le pluginliberty-maven-plugin à notre pom.xml:
net.wasdev.wlp.maven.plugins
liberty-maven-plugin
2.1.2
io.openliberty
openliberty-runtime
17.0.0.4
zip
${basedir}/src/main/liberty/config/server.xml
${package.file}
${packaging.type}
false
project
/
${project.artifactId}-${project.version}.war
9080
9443
install-server
prepare-package
install-server
create-server
install-feature
package-server-with-apps
package
install-apps
package-server
Ce plugin est configurable avec un ensemble de propriétés:
library
${project.build.directory}/${app.name}-service.jar
runnable
L'objectif exec ci-dessus génère un fichier jar exécutable, de sorte que notre application constitue un microservice indépendant pouvant être déployé et exécuté de manière isolée. Nous pouvons également le déployer en tant qu'image Docker.
Pour créer un fichier jar exécutable, exécutez la commande suivante:
mvn package
Et pour exécuter notre microservice, nous utilisons cette commande:
java -jar target/library-service.jar
Cela démarrera le runtime Open Liberty et déploiera notre service. Nous pouvons accéder à notre point de terminaison et obtenir tous les livres à cette adresse URL:
curl http://localhost:9080/library/books
Le résultat est un JSON:
[
{
"id": "0001-201802",
"isbn": "1",
"name": "Building Microservice With Eclipse MicroProfile",
"author": "example",
"pages": 420
}
]
Pour obtenir un seul livre, nous demandons cette URL:
curl http://localhost:9080/library/books/0001-201802
Et le résultat est JSON:
{
"id": "0001-201802",
"isbn": "1",
"name": "Building Microservice With Eclipse MicroProfile",
"author": "example",
"pages": 420
}
Nous allons maintenant ajouter un nouveau livre en interagissant avec l'API:
curl
-H "Content-Type: application/json"
-X POST
-d '{"isbn": "22", "name": "Gradle in Action","author": "example","pages": 420}'
http://localhost:9080/library/books
Comme nous pouvons le voir, le statut de la réponse est 201, indiquant que le livre a été créé avec succès, et leLocation est l'URI par lequel nous pouvons y accéder:
< HTTP/1.1 201 Created
< Location: http://localhost:9080/library/books/0009-201802
10. Conclusion
Cet article a montré comment créer un microservice simple basé sur Eclipse MicroProfile et traitant de JAX RS, JSON-P et CDI.
Le code est disponibleover on Github; il s'agit d'un projet basé sur Maven, il devrait donc être simple à importer et à exécuter tel quel.