Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use Quarkus to build a micro service in Openshift

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/02 Report--

How to use Quarkus to build a micro-service in Openshift, this article introduces the corresponding analysis and solution in detail, hoping to help more partners who want to solve this problem to find a more simple and feasible method.

1. Create applications-dependencies

When creating a new application, you can execute a Maven command that uses quarkus-maven-plugin. Dependencies should be declared in the parameter-Dextensions.

Mvn io.quarkus:quarkus-maven-plugin:0.21.1:create\-DprojectGroupId=pl.piomin.services\-DprojectArtifactId=employee-service\-DclassName= "pl.piomin.services.employee.controller.EmployeeController"\-Dpath= "/ employees"-Dextensions= "resteasy-jackson, hibernate-validator"

Here is the structure of our pom.xml:

0.21.1 UTF-8 11 11 io.quarkus quarkus-bom ${quarkus.version} pom import io.quarkus quarkus-maven-plugin ${quarkus.version} Build

For building simple REST applications using input validation, we don't need too many modules. You may have noticed that I only declared two extensions, which are the same as the list of dependencies in the following pom.xml:

Io.quarkus quarkus-resteasy-jackson io.quarkus quarkus-hibernate-validator2. Create an application-code

It may be a bit strange for Spring Boot or Micronaut users that there is no main running class that uses the static code main method. The resource/controller class is actually the main class. Quarkus's resource/controller classes and methods should be marked with annotations in the javax.ws.rs library.

The following is the implementation of employee-service 's REST controller:

@ Path ("/ employees") @ Produces (MediaType.APPLICATION_JSON) public class EmployeeController {private static final Logger LOGGER = LoggerFactory.getLogger (EmployeeController.class); @ Inject EmployeeRepository repository; @ POST public Employee add (@ Valid Employee employee) {LOGGER.info ("Employee add: {}", employee); return repository.add (employee) } @ Path ("/ {id}") @ GET public Employee findById (@ PathParam ("id") Long id) {LOGGER.info ("Employee find: id= {}", id); return repository.findById (id);} @ GET public Set findAll () {LOGGER.info ("Employee find"); return repository.findAll () } @ Path ("/ department/ {departmentId}") @ GET public Set findByDepartment (@ PathParam ("departmentId") Long departmentId) {LOGGER.info ("Employee find: departmentId= {}", departmentId); return repository.findByDepartment (departmentId);} @ Path ("/ organization/ {organizationId}") @ GET public Set findByOrganization (@ PathParam ("organizationId") Long organizationId) {LOGGER.info ("Employee find: organizationId= {}", organizationId) Return repository.findByOrganization (organizationId);}}

We use CDI for dependency injection and SLF4J for logging. The Controller class uses the memory repository bean to store and retrieve data. Repository bean uses the CDI @ ApplicationScoped annotation and injects controller:

@ ApplicationScopedpublic class EmployeeRepository {private Set employees = new HashSet (); public EmployeeRepository () {add (new Employee (1L, 1L, "John Smith", 30, "Developer")); add (new Employee (1L, 1L, "Paul Walker", 40, "Architect"));} public Employee add (Employee employee) {employee.setId ((long) (employees.size () + 1)); employees.add (employee) Return employee;} public Employee findById (Long id) {Optional employee = employees.stream (). Filter (a-> a.getId (). Equals (id)). FindFirst (); if (employee.isPresent ()) return employee.get (); else return null;} public Set findAll () {return employees } public Set findByDepartment (Long departmentId) {return employees.stream () .filter (a-> a.getDepartmentId () .equals (departmentId)) .equals (Collectors.toSet ());} public Set findByOrganization (Long organizationId) {return employees.stream () .filter (a-> a.getOrganizationId () .equals (organizationId)) .duration (Collectors.toSet ());}}

The last component is the entity class with validation:

Public class Employee {private Long id; @ NotNull private Long organizationId; @ NotNull private Long departmentId; @ NotBlank private String name; @ Min @ Max private int age; @ NotBlank private String position; / /. GETTERS AND SETTERS} 3. Unit testing

For most popular Java frameworks, unit testing with Quarkus is simple. If you are testing a REST-based web application, you should include the following dependencies in pom.xml:

Io.quarkus quarkus-junit5 test io.rest-assured rest-assured test

Let's take a look at the test classes from organization-service, another of our microservices, as well as employee-service and department-service. The test class should be annotated with @ QuarkusTest. We can inject other bean through the @ Inject annotation. The rest is typical of JUnit and RestAssured-. We are testing the API method exposed by controller. Because we use an in-memory repository, we don't need to simulate anything other than inter-service communication (which we'll discuss later in this article). We have some positive scenarios for the GET and POST methods, and a negative scenario (testInvalidAdd) that does not pass input validation.

@ QuarkusTestpublic class OrganizationControllerTests {@ Inject OrganizationRepository repository; @ Testpublic void testFindAll () {given () .when () .get ("/ organizations") .then () .statusCode (200) .body (notNullValue ());} @ Testpublic void testFindById () {Organization organization = new Organization ("Test3", "Address3"); organization = repository.add (organization) Given (). When (). Get ("/ organizations/ {id}", organization.getId ()). Then (). StatusCode (200) .body ("id", equalTo (organization.getId (). IntValue () .body ("name", equalTo (organization.getName () } @ Test public void testFindByIdWithDepartments () {given (). When (). Get ("/ organizations/ {id} / with-departments", 1L) .then () .statusCode (200) .body (notNullValue ()) .body ("departments.size ()", is (1));} @ Test public void testAdd () {Organization organization = new Organization ("Test5", "Address5") Given () .contentType ("application/json") .body (organization) .when () .post ("/ organizations") .then () .statusCode (200) .body ("id", notNullValue ()) .body ("name", equalTo (organization.getName ();} @ Test public void testInvalidAdd () {Organization organization = new Organization () Given (). ContentType ("application/json") .body (organization). When (). Post ("/ organizations"). Then (). StatusCode (400);} 4. Inter-service communication

Because Quarkus aims to run on Kubernetes, it does not provide any built-in support for third-party service discovery (for example, through Consul or Netflix Eureka) and HTTP clients integrated with this discovery. However, Quarkus provides dedicated client support for REST communications. To use it, we first need to include the following dependencies:

Io.quarkus quarkus-rest-client

Quarkus provides a declarative REST client based on the MicroProfile REST client. You need to create an interface with the required methods and annotate it with @ RegisterRestClient. Other annotations are very similar to the server side. Because you use @ RegisterRestClient to mark Quarkus, you should know that this interface is available for CDI injection as a REST client.

@ Path ("/ departments") @ RegisterRestClientpublic interface DepartmentClient {@ GET @ Path ("/ organization/ {organizationId}") @ Produces (MediaType.APPLICATION_JSON) List findByOrganization (@ PathParam ("organizationId") Long organizationId); @ GET @ Path ("/ organization/ {organizationId} / with-employees") @ Produces (MediaType.APPLICATION_JSON) List findByOrganizationWithEmployees (@ PathParam ("organizationId") Long organizationId);}

Now, let's take a look at the controller class in organization-service. Along with @ Inject, we need to use the @ RestClient annotation to inject the REST client bean correctly. You can then use interface methods to invoke other exposed services

@ Path ("/ organizations") @ Produces (MediaType.APPLICATION_JSON) public class OrganizationController {private static final Logger LOGGER = LoggerFactory.getLogger (OrganizationController.class); @ Inject OrganizationRepository repository; @ Inject @ RestClient DepartmentClient departmentClient; @ RestClient EmployeeClient employeeClient; / /. OTHER FIND METHODS @ Path ("/ {id} / with-departments") @ GET public Organization findByIdWithDepartments (@ PathParam ("id") Long id) {LOGGER.info ("Organization find: id= {}", id); Organization organization = repository.findById (id); organization.setDepartments (departmentClient.findByOrganization (organization.getId (); return organization } @ Path ("/ {id} / with-departments-and-employees") @ GET public Organization findByIdWithDepartmentsAndEmployees (@ PathParam ("id") Long id) {LOGGER.info ("Organization find: id= {}", id); Organization organization = repository.findById (id); organization.setDepartments (departmentClient.findByOrganizationWithEmployees (organization.getId (); return organization } @ Path ("/ {id} / with-employees") @ GET public Organization findByIdWithEmployees (@ PathParam ("id") Long id) {LOGGER.info ("Organization find: id= {}", id); Organization organization = repository.findById (id); organization.setEmployees (employeeClient.findByOrganization (organization.getId (); return organization;}}

The last thing missing in the communication is the address of the target service. We can use the @ RegisterRestClient annotated field baseUri to provide them. However, a better solution seems to be to put them in application.properties. The property name needs to contain the fully qualified name of the client interface and the suffix mp-rest/url.

Pl.piomin.services.organization.client.DepartmentClient/mp-rest/url= http://localhost:8090pl.piomin.services.organization.client.EmployeeClient/mp-rest/url=http://localhost:8080

In the previous section, I already mentioned unit testing and inter-service communication. To test the API method of communicating with other applications, we need to impersonate the REST client. Below is the creation of a DepartmentClient for the simulation example. It should only be visible during testing, so we have to put it in src/test/java. If we annotate it with @ Mock and @ RestClient, the bean is automatically used by default instead of the declarative REST client defined in src/main/java.

@ Mock@ApplicationScoped@RestClientpublic class MockDepartmentClient implements DepartmentClient {@ Override public List findByOrganization (Long organizationId) {return Collections.singletonList (new Department ("Test1"));} @ Override public List findByOrganizationWithEmployees (Long organizationId) {return null;} 5. Monitoring and recording

We can easily use Quarkus to expose health checks or API documents. API documents are built using OpenAPI/Swagger. Quarkus takes advantage of the libraries available in the SmallRye project. We should include the following dependencies in pom.xml:

Io.quarkus quarkus-smallrye-openapi io.quarkus quarkus-smallrye-health

We can define two types of health checks: readiness and liveness. There are / health/ready and / health/live context paths. To expose them outside of the application, we need to define a bean that implements the MicroProfile HealthCheck interface. The Readiness side should be marked with @ Readiness, while the liveness side should be marked with @ Liveness.

@ ApplicationScoped@Readinesspublic class ReadinessHealthcheck implements HealthCheck {@ Override public HealthCheckResponse call () {return HealthCheckResponse.named ("Employee HealthCheck") .up () .build ();}}

To enable Swagger documents, we only need to add a dependency. Quarkus also provides built-in UI for Swagger. By default, it is enabled in development mode, so if you want to use it in a production environment, you should add quarkus.swagger-ui.always-include=true to your application.properties file. Now, if you run the application employee-service locally in development mode by executing the Maven command mvn compile quarkus:dev, you can view the available API specifications under URL http://localhost:8080/swagger-ui.

This is my log when I started from the application. It prints a list of listening ports and loaded extensions.

6. Run the microservice on the local machine

Because we want to run multiple applications on the same machine, we need to override their default HTTP listening port. Although employee-service still runs on the default port 8080, other microservices use different ports, as shown below.

Department-service:

Organization-service:

Let's test inter-service communication in Swagger UI. I called GET / organizations/ {id} / with-departments, which called the endpoint GET GET / departments/organization/ {organizationId} exposed by department-service. The result is shown in the following figure.

7. Running microservices on OpenShift

We have completed the implementation of the sample microservice architecture and run them on the local machine. Now we can take the final step and try to deploy these applications on Minishift. When deploying Quarkus applications on OpenShift, we have several different approaches. Today, I'll show you how to take advantage of the mechanism that S2I built for this.

We will use Quarkus GraalVM Native S2I Builder. Can be found in the quarkus/ubi-quarkus-native-s2i of quai.io. Of course, we need to start Minishift before deploying the application. According to Quarkus documentation, GraalVM-based local architecture takes up a lot of memory and CPU, so I decided to set up 6GB and four cores for Minishift.

$minishift start-vm-driver=virtualbox-memor

In addition, we need to modify the source code of the application slightly. As you may recall, we used JDK 11 to run them locally. Quarkus S2I builder only supports JDK 8, so we need to change it in pom.xml. We also need to include a declared native configuration file as follows:

0.21.1 UTF-8 1.8 1.8... Native native io.quarkus quarkus-maven-plugin ${quarkus.version} Native-image true Maven-failsafe-plugin 2.22.1 integration-test verify ${project.build.directory} / ${project.build.finalName}-runner

In addition, there are two changes to the application.properties file. We do not need to override the port number because Minishift dynamically assigns a virtual IP to each pod. Communication between services is achieved through OpenShift discovery, so we only need to set the name of the service instead of localhost.

Quarkus.swagger-ui.always-include=truepl.piomin.services.organization.client.DepartmentClient/mp-rest/url= http://department:8080pl.piomin.services.organization.client.EmployeeClient/mp-rest/url=http://employee:8080

Finally, we can deploy our application to Minishift. To do this, you should use the oc client to execute the following command:

$oc new-app quay.io/quarkus/ubi-quarkus-native-s2i:19.1.1~ https://github.com/piomin/sample-quarkus-microservices.git#openshift-context-dir=employee-name=employee$ oc new-app quay.io/quarkus/ubi-quarkus-native-s2i:19.1.1~ https://github.com/piomin/sample-quarkus-microservices.git#openshift-context-dir=department-name=department$ oc new-app quay.io/quarkus/ubi-quarkus-native-s2i:19.1 .1 ~ https://github.com/piomin/sample-quarkus-microservices.git#openshift-- context-dir=organization-- name=organization

As you can see, the source code of the program can be found on my GitHub account at https://github.com/piomin/sample-quarkus-microservices.git. The version running on Minishift is already shared in the branch openshift. The version running on the local machine is available on the main branch. Because all applications are stored in a library, we need to define a parameter context-dir for each deployment.

I'm disappointed. Although it took me a long time to set up more memory and CPU for minishift-- about 25 minutes.

However, after a long wait, all my applications are finally deployed.

I expose them outside of Minishift by executing the following visible commands. They can be tested using OpenShift routing under DNS http://${APP_NAME}-myproject.192.168.99.100.nip.io.

$oc expose svc employee$ oc expose svc department$ oc expose svc organization

In addition, you can enable readiness and liveness health checks on OpenShift because they are disabled by default.

This is the answer to the question about how to use Quarkus to build a micro-service in Openshift. I hope the above content can be of some help to you. If you still have a lot of doubts to be solved, you can follow the industry information channel for more related knowledge.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report