blob: b3ef012cb4dfdee64cc94d04fb10adc866ead0ee [file] [log] [blame]
<?php
/*******************************************************************************
* Copyright (c) 2015, 2016 Eclipse Foundation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eric Poirier (Eclipse Foundation) - Initial implementation
* Christopher Guindon (Eclipse Foundation)
*******************************************************************************/
// This file must be included
if(basename(__FILE__) == basename($_SERVER['PHP_SELF'])){exit();}
?>
<h1 class="article-title"><?php echo $pageTitle; ?></h1>
<h3>Why Cloud Native Microservices?</h3>
<p>Cloud, visible or invisible, is everywhere. You might be able to guess which country I live in! In this article, I am talking about the invisible Cloud. There are three type of clouds: private, public and multi cloud! More and more companies are working towards application modernization so that they can create cloud native microservices in order to save cost and make the best use of cloud resources. When a microservice runs in the cloud, it only costs you money when it is running. Depending on the load, it can scale up or down. In this case, you just pay for what you use. There won't be idle costs. You don't need to worry about purchasing any hardware just for occasional use, Pay as You Go model.</p>
<p>In terms of application modernisation, there are different routes to achieve the goal. For existing monolith applications, it is time to think about how to deploy them in the Cloud. You can either containerise them or break them into microservices. For newly-written applications, the best choice is to build green-field cloud native microservices. This article focuses on how to develop the most efficient cloud native microservices that can function well in the cloud. Let's take a step back first and clarify what cloud native means.</p>
<h3>What Is Cloud Native?</h3>
<p>The first question that might come to your mind is: what does cloud native mean? Generally speaking, Cloud Native refers to an application that's built for the cloud. The cloud native microservices make best use of the cloud infrastructure and form a great ecosystem. They can be easily configured without being repackaged, secure, resilient, monitorable, traceable etc. Let's summarise the characteristics of cloud native microservices.</p>
<p><strong>Externalise configuration</strong><p>
<p>Cloud native microservices must be configurable. When changing configuration, the microservcies should not be repackaged. Build once, configure everywhere. This is also highly recommended by the <a href="https://www.12factor.net/">12-factor methodology</a>.</p>
<p><strong>RESTful</strong><p>
<p>Cloud Native microservices must be stateless. The state should be stored in the database, not in the memory. The microservice itself should be treated as cattle, not pet. In this case, when a microservice container is not responding, the underline cloud infrastructure can replace it without losing anything.</p>
<p><strong>Fault Tolerance</strong><p>
<p>Cloud Native microservices should be resilient. It should function under whatever the situation is. For example, even if its downstream service is out of service.</p>
<p><strong>Discoverable</strong><p>
<p>Cloud Native microservices should be able to advertise itself for what function it provides or its capability so that clients can invoke the services.</p>
<p><strong>Secure</strong><p>
<p>Security is extremely important in cloud native microservices. In a monolith application, only a small portion of the functions was exposed while majority functions were hidden. When moving to cloud native, every single service has a unique identity. You will need to ensure that only authenticated and authorised users can invoke the relevant microservices. For instance, if you deploy a microservice of salary query, you might only want a caller with the access right of "Manager" and "Owner" access this service. </p>
<p><strong>Monitorable</strong><p>
<p>Once a cloud native microservice is running in the cloud, it is essential to emit some metrics so that DevOps can monitor it in order to find out how fast it responds and how much loads it is taking, etc.</p>
<p><strong>Traceable</strong><p>
<p>A cloud native microservice mostly needs to talk to other services, which then talks to different services. If you try to draw an invocation chain, you might end up a->b->d->f->e and so on. Once a service is not functioning, you need to figure out which one is faulty. Without the ability to be able to visualise the invocation chain, it will be very hard to identify the faulty service. In order to build the invocation chain, microservices need to be able to propagate the correlation id. With this, some tools such as ZipKin or Jaeger can build a trace span to illustrate each service status.</p>
<p><strong>Ability to communicate with the Cloud</strong><p>
<p>A cloud native microservice needs to be able to communicate with cloud infrastructure about its healthy status. It should be able to tell the infrastructure whether it is ready to receive the request or whether it is still alive. If it is not alive, cloud infrastructure, e.g. Kubernetes, can easily replace it. If it is not ready, e.g. its database connection is not ready, cloud infrastructure will not route requests to this service.</p>
<p>When you read up to this point, you might sigh. Let alone your business logic, there are so many QoS to worry about as well. If you take care of all of these, you might have to spend the majority of time worrying about the above requirements while spending less time doing something related to your business. It is too much. I hear you. Luckily, I am here to help. Read on.</p>
<h3>How to Build Cloud Native Microservices</h3>
<p>No need to build one from scratch. There are frameworks that can help you with that; the most popular frameworks for developing cloud native microservices are <a href="https://microprofile.io/">Eclipse MicroProfile</a> and <a href="https://spring.io/">Spring framework</a>.</p>
<p>Spring framework has been around for a few years; therefore, I won't talk too much about it. Eclipse MicroProfile is relatively new. It was established in mid-2016 by IBM, Red Hat, Payara, Tomitribe, LJC, and other individuals. Later on, other companies, e.g. Microsoft, Oracle, etc also joined the community. The aim of Eclipse MicroProfile is to evolve the best programming models for developing cloud native microservices. So far, it has more than eight releases, with MicroProfile 3.0 announced not long ago. It has gained a massive amount of attention lately. Most Java EE runtimes now supports MicroProfile, such as Open Liberty, Quarkus, Payara, TomEE, Helidon, KumuluzEE, etc. You can find the implementation details <a href="https://wiki.eclipse.org/MicroProfile/Implementation">here</a>.</p>
<table class="table table-sm">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Spring</th>
<th scope="col">MicroProfile</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="col">REST APIs</th>
<td scope="col"></td>
<td scope="col"></td>
</tr>
<tr>
<td scope="col">REST Service</td>
<td scope="col">Spring MVC</td>
<td scope="col">JAX-RS</td>
</tr>
<tr>
<td scope="col">Dependency Injection</td>
<td scope="col">Spring IoC &amp; DI</td>
<td scope="col">CDI</td>
</tr>
<tr>
<td scope="col">API Documentation</td>
<td scope="col">Spring REST Docs</td>
<td scope="col">MicroProfile Open API</td>
</tr>
<tr>
<td scope="col">REST Client</td>
<td scope="col">Spring MVC Feign</td>
<td scope="col">MicroProfile REST Client</td>
</tr>
<tr>
<td scope="col">JSON Binding/Processing</td>
<td scope="col">Bring Your Own Library<br> Jackson</td>
<td scope="col">JSON-B<br> JSON-P</td>
</tr>
<tr>
<th scope="col">Handling 100s of Services</th>
<td scope="col"></td>
<td scope="col"></td>
</tr>
<tr>
<td scope="col">Configuration</td>
<td scope="col">Spring Boot Config<br>Spring Cloud Config</td>
<td scope="col">MicroProfile Config</td>
</tr>
<tr>
<td scope="col">Fault Tolerance</td>
<td scope="col">Netflix Hystrix</td>
<td scope="col">MicroProfile Fault Tolerance</td>
</tr>
<tr>
<td scope="col">Security</td>
<td scope="col">Spring Security<br>Spring Cloud Security</td>
<td scope="col">EE Security<br>MicroProfile JWT Propagation</td>
</tr>
<tr>
<th scope="col">Operation Focus</th>
<td scope="col"></td>
<td scope="col"></td>
</tr>
<tr>
<td scope="col">Health Checks</td>
<td scope="col">Spring Boot Actuator</td>
<td scope="col">MicroProfile Health Check</td>
</tr>
<tr>
<td scope="col">Metrics</td>
<td scope="col">Spring Boot Actuator</td>
<td scope="col">MicroProfile Metrics</td>
</tr>
<tr>
<td scope="col">Distributed Tracing</td>
<td scope="col">Spring Cloud Sleuth</td>
<td scope="col">MicroProfile Open Tracing</td>
</tr>
</tbody>
</table>
<p>Let's discuss each category in more detail.</p>
<h3>Develop Cloud Native Microservices</h3>
<p>RESTful API is a well-adopted way to develop Cloud Native Microservices. Let's focus on how to use RESTful APIs and associated technologies to write your loosely-coupled microservices.</p>
<p><strong>REST APIs</strong></p>
<p>REST (Representational State Transfer) is an architectural style for defining communication standards between services, making it easier for systems to communicate with each other. Both MicroProfile and Spring support REST.</p>
<p><em style="color:blue">JAX-RS</em><br>
MicroProfile uses JAX-RS from Java EE. In JAX-RS, you need to define an Application and JAX-RS resource(s). In the following example, the Application <code style="color: grey">CatalogApplication</code> and JAX-RS resources <code style="color: grey">CatalogService</code> were defined, detailed below.</p>
<pre style="background-color:white;">
ApplicationPath("/rest")
public class CatalogApplication extends Application {
}
@Path("/items")
@Produces(MediaType.APPLICATION_JSON)
public class CatalogService {..}
@GET
public List&lt;Item&gt; getInventory() {...}
@GET
@Path("{id}")
public Response getById(@PathParam("id") long id) {...}
</pre>
<p>In the above-mentioned example, an end point of http://${host}:${port}/rest/items will be exposed.</p>
<p>Refer to <a href="https://openliberty.io/guides/rest-intro.html">this</a> Open Liberty to learn more about JAX-RS.</p>
<p><em style="color:blue">Spring</em><br>
In Spring framework, you will need to create a SpringBootApplication and Controller. In the following example, <code style="color: grey">Application</code> and <code style="color: grey">CatalogController</code> were created accordingly.</p>
<pre style="background-color:white;">
@SpringBootApplication
public class Application {
public static void main(String[] args)
SpringApplication.run(Application.class, args);}
}
@RestController
public class CatalogController {..}
@RequestMapping(value = "/items", method = RequestMethod.GET)
@ResponseBody
List&lt;Item&gt; getInventory() {..}
@RequestMapping(value = "/items/{id}", method = RequestMethod.GET)
ResponseEntity&lt;?&gt; getById(@PathVariable long id) {...}
</pre>
<p>In the above-mentioned example, an end point of http://${host}:${port}/rest/items will be exposed.</p>
<p><strong>Dependency Injection</strong></p>
<p>When designing cloud native microservices, the best practice is to create a loosely-coupled microservices. MicroProfile adopts Contexts and Dependency Injection (CDI) from Java EE while Spring uses Spring DI, IoC to achieve the same effect.</p>
<p><em style="color:blue">CDI</em><br>
Below is how CDI can be used for dependency injection</p>
<pre style="background-color:white;">
@ApplicationPath("/rest")
public class JaxrsApplication extends Application {
@Inject
private InventoryRefreshTask refreshTask;
</pre>
<p>The above code snippet will inject an instance of <code style="color: grey">InventoryRefreshTask</code> to <code style="color: grey">refreshTask</code>.</p>
<p>CDI is the centre piece for both Java EE and MicroProfile. It is very important to understand CDI. Refer to <a href="https://openliberty.io/guides/cdi-intro.html">this</a> Open Liberty guide to learn some basics about CDI.</p>
<p><em style="color:blue">Spring DI and IoC</em></p>
<p>Spring uses Dependency Injection, Inversion of Control to achieve loosely coupling. The following code snippet illustrates how to use Spring DI via @Autowired. An instance of <code style="color: grey">InventoryRefreshTask</code> will be injected to the variable <code style="color: grey">refreshTask</code>. By the way, Spring also supports <code style="color: grey">@Inject</code>, which is equivalent to <code style="color: grey">@Autowired</code>.
<pre style="background-color:white;">
@SpringBootApplication
public class Application {
@Autowired
private InventoryRefreshTask refreshTask;
...
}
</pre>
<p><strong>Document APIs</strong></p>
<p>Microservices need to advertise their capabilities so that potential clients can utilise their services. MicroProfile and Spring handle things differently when it comes to documenting APIs.</p>
<p><em style="color:blue">MicroProfile Open API</em></p>
<p>MicroProfile uses <a href="https://github.com/eclipse/microprofile-open-api/releases">MicroProfile Open API</a> to document APIs, which is based on Swagger API. In MicroProfile Open API, any JAX-RS resources are automatically opted in to have their APIs generated. It can also take open API yaml file with the file name of openapi.yaml or openapi.yml or openapi.json under META-INF folder. Below is an example of how to document API responses and Operations.</p>
<pre style="background-color:white;">
@GET
@Produces(MediaType.APPLICATION_JSON)
@APIResponse(
responseCode = "200",
description = "host:properties pairs stored in the inventory.",
content = @Content(mediaType = "application/json",
schema = @Schema(type = SchemaType.OBJECT,
implementation = InventoryList.class)))
@Operation(summary = "List inventory contents.",
description = "Returns the stored host:properties pairs.")
public InventoryList listContents() {
return manager.list();
}
</pre>
<p>In the above-mentioned snippet, an endpoint http://${host.name}:${port}/openapi will be exposed with the following output.</p>
<pre style="background-color:white;">
openapi: 3.0.0
info:
title: Inventory App
description: App for storing JVM system properties of various hosts.
license:
name: Eclipse Public License - v 1.0
url: https://www.eclipse.org/legal/epl-v10.html
version: "1.0"
servers: - url: http://localhost:{port} description: Simple Open Liberty.
variables:
port:
description: Server HTTP port.
default: "9080"
paths:
/inventory/systems:
get:
summary: List inventory contents.
description: Returns the currently stored host:properties pairs in the inventory.
operationId: listContents
responses:
200:
description: host:properties pairs stored in the inventory.
content:
application/json:
schema:
$ref: '#/components/schemas/InventoryList'
... .
</pre>
<p>If you use Open Liberty, the endpoint http://${host.name}:${port.number}/openapi/ui will also be exposed, which allows the end user to directly invoke the individual endpoints.</p>
<p align="center"><img class="img-responsive" width="60%" src="/community/eclipse_newsletter/2019/august/images/mp1.png"></p>
<p>If you are familiar with Swagger API, you will find this familiar.</p>
<p>Refer to <a href="https://openliberty.io/guides/microprofile-openapi.html">this</a> Open Liberty guide to learn more about MicroProfile Open API.</p>
<p><em style="color:blue">Spring Doc</em></p>
<p>Spring uses tests to document APIs and have the ability to generate API doc as part of test running. Here's how to generate a Spring doc.</p>
<p>1. Define the dependencies</p>
<pre style="background-color:white;">
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.restdocs&lt;/groupId&gt;
&lt;artifactId>spring-restdocs-mockmvc&lt;/artifactId&gt;
&lt;scope>test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.restdocs&lt;/groupId&gt;
&lt;artifactId&gt;spring-restdocs-core&lt;/artifactId&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
</pre>
<p>2. Define your Rest service</p>
<pre style="background-color:white;">
@RestController
public class CatalogController {
@RequestMapping("/")
public @ResponseBody String index() {
return "Greetings from Catalog Service!";
}
}
</pre>
<p>3. Define all necessary test classes</p>
<pre style="background-color:white;">
@RunWith(SpringRunner.class)
@SpringBootTest(classes = CatalogController.class)
@WebAppConfiguration
public class CatalogControllerTest {
@Rule public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets");
private MockMvc mockMvc;
@Autowired private WebApplicationContext context;
@Before public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context) .apply(documentationConfiguration(restDocumentation)) .build();
}
</pre>
<p>4. Use <code style="color: grey">alwaysDo(), responseFileds(), requestPayload(), links(), fieldWithPath(), requestParameters(), pathParameters()</code> to document</p>
<pre style="background-color:white;">
@Test
public void crudDeleteExample() throws Exception {
this.mockMvc.perform(delete("/crud/{id}", 10)).andExpect(status().isOk())
.andDo(document("crud-delete-example",
pathParameters(
parameterWithName("id").description("The id of the input to delete")
)));
}
</pre>
<p>When running the tests, API docs will be generated.</p>
<p><strong>Rest Client</strong></p>
<p>Cloud native microservices are not standalone. The microservices interact with each other. One microservice invokes the second microservice, which then invokes the third microservice, and so on. Normally, it is kind of a spiderweb. For instance, in the scenario of microservice A calling microservice B, microservice A behaves as a client. How can you make connections from microservice A to microservice B? Rest client to rescue!</p>
<p><em style="color:blue">MicroProfile Rest Client</em></p>
<p>JAX-RS client can be used to make client server communication, detailed below.</p>
<pre style="background-color:white;">
Client client = ClientBuilder.newClient();
Response res = client.target("http://example.org/hello").
request("text/plain").get();
</pre>
<p>However, it is not a type safe client, hence it is prone to error. The invocation with the wrong parameters passed in results in a runtime error, which is too late.</p>
<p><a href="https://github.com/eclipse/microprofile-rest-client/releases">MicroProfile Rest Client</a> is a type safe Rest Client, which provides a much simpler way to make the client server communication. How does it work? Below are the steps.</p>
<p style="color:blue">Step 1: Register a rest client API</p>
<pre style="background-color:white;">
@Dependent
@RegisterRestClient(baseUri=http://localhost:9080/system)
@RegisterProvider(InventoryResponseExceptionMapper.class)
public interface InventoryServiceClient {
@GET
@Produces(MediaType.APPLICATION_JSON)
List&lt;Item&gt; getAllItems() throws UnknownUrlException,
ServiceNotReadyException;
}
</pre>
<p style="color:blue">Step 2: Inject the client API to the client microservice JAX-RS resource</p>
<pre style="background-color:white;">
@Inject
@RestClient
private InventoryServiceClient invClient;
final List&lt;Item&gt; allItems = invClient.getAllItems();
</pre>
<p style="color:blue">Step 3: Rebind the backend microservice</p>
<p>io.openliberty.guides.inventory.client.SystemClient/mp-rest/url=http://otherhost:8080/system</p>
<p>Using the fully qualified class name appended with <code style="color: grey">/mp-rest/url</code> as the key and the backend service endpoint as the value. When deploying this microservice in the cloud, the backend URL will be different from other environments. Normally, you will need to rebind the backend service in your client's deployment.yaml via Kubernetes ConfigMap.</p>
<p>Refer to <a href="https://openliberty.io/guides/microprofile-rest-client.html">this</a> Open Liberty guide to learn more about MicroProfile Rest Client.</p>
<p><em style="color:blue">Spring</em></p>
<p>Spring uses a similar approach to MicroProfile Rest Client with the corresponding technologies such as FeignClient and Injection.</p>
<p><em style="color:blue">Step 1: Define the client</em></p>
<pre style="background-color:white;">
@FeignClient(name="inventory-service",
url="${inventoryService.url}")
public interface InventoryServiceClient {
@RequestMapping(method=RequestMethod.GET,
value="/micro/inventory", produces={MediaType.APPLICATION_JSON_VALUE})
List&lt;Item&gt; getAllItems();
}
</pre>
<p><em style="color:blue">Step 2: Enable the client and inject the client</em></p>
<pre style="background-color:white;">
@EnableFeignClients
public class Application {
@Autowired
private InventoryServiceClient invClient;
final List&lt;Item&gt; allItems = invClient.getAllItems();
...
}
</pre>
<p><strong>Payload on the wire - JSON</strong></p>
<p>JSON format is the common media type on the wire. JSON-B and JSON-P are popular technologies to help with JSON media type.</p>
<p><em style="color:blue">JSON-P and JSON-B</em></p>
<p>MicroProfile 2.0 and onwards supports both JSON-B and JSON-P, which dramatically simplifies the serialization and deserialization of JSON objects. Below is an example of using JSON-B to serialize <code style="color:grey">artists</code> object.</p>
<pre style="background-color:white;">
public class car {
private String make;
private String model;
private String reg;
...
}
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
Car car = new Car("VW", "TGUAN", "HN19MDZ");
Jsonb jsonb = JsonbBuilder.create();
String json = jsonb.toJson(car);
</pre>
<p>The <code style="color:grey">toJson ()</code> method returns the serialized car object.</p>
<pre style="background-color:white;">
{
"make": "VW",
"model": "TGUAN",
"reg": "HN19MFZ"
}
</pre>
<p>Deserialization with JSON-B is equally simple.</p>
<pre style="background-color:white;">
Car car = Jsonb.fromJson(json, Car.class);
</pre>
<p>In order to transport JSON object on the wire, you simply define a POJO, e.g.</p>
<pre style="background-color:white;">
public class InventoryList {
private List&lt;SystemData&gt; systems;
public InventoryList(List&lt;SystemData&gt; systems) {
this.systems = systems;
}
public List&lt;SystemData&gt; getSystems() {
return systems;
}
public int getTotal() {
return systems.size();
}
}
</pre>
<p>In the JAX-RS resource, you can directly return this type as a JSON object.<br>
...</p>
<pre style="background-color:white;">
@GET
@Produces(MediaType.APPLICATION_JSON)
public InventoryList listContents() {
return manager.list();
}
</pre>
<p>Refer to <a href="https://cloud.ibm.com/docs/java?topic=java-mp-json">this</a> article to learn more about JSON-B.</p>
<p><em style="color:blue">Spring</em></p>
<p>Spring can either directly use Jackson or JSON-B.</p>
<pre style="background-color:white;">
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
final ObjectMapper objMapper = new ObjectMapper();
jsonString = objMapper.writeValueAsString(car);
// or use JSON-B
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
Jsonb jsonb = JsonbBuilder.create();
String result = jsonb.toJson(car);
</pre>
<h3>Handling 100s Microservices</h3>
<p>It is common to have 100s microservices in your Cloud infrastructure. When handling 100s services, you will need to monitor the services, configure the services, secure the services, etc.</p>
<p><strong>Configure Microservices</strong></p>
<p>Cloud Native microservices are configurable, so they can be updated by DevOps. Developers won't have to repackage the microservices just because of the configuration value changes. The design principle is that those configurations can be stored somewhere externally to the microservices and the configurations are made available to the microservices. This is called externalised configuration, one of the factors highlighted by <a href="https://12factor.net/">12-factor APP</a>. Let's take a look at how MicroProfile and Spring help us configure microservices.</p>
<p><em style="color:blue">MicroProfile Config</em></p>
<p><a href="https://github.com/eclipse/microprofile-config/releases">MicroProfile Config</a> enables externalising configurations by placing configuration values in config sources and then microservices can either use injection or programmatically lookup to get the corresponding configuration values.</p>
<p style="color:blue">Step 1: Specify the configuration in a config source, either as system properties, environment variables, microprofile-config.properties or custom config sources.</p>
<pre style="background-color:white;">
# Elasticsearch
elasticsearch_url=http://es-catalog-elasticsearch:9200
elasticsearch_index=micro
elasticsearch_doc_type=items
</pre>
<p style="color:blue">Step 2: Use either programming lookup or inject</p>
<pre style="background-color:white;">
Config config = ConfigProvider.getConfig();
private String url = config.getValue("elasticsearch_url",
String.class);
</pre>
<p>or</p>
<pre style="background-color:white;">
@Inject @ConfigProperty(name="elasticsearch_url") String url;
</pre>
<p>Refer to <a href="https://openliberty.io/guides/microprofile-config.html">this</a> Open Liberty guide to learn more about MicroProfile Config.</p>
<p>Let's see how to do the same thing with Spring framework.</p>
<p><em style="color:blue">Spring config</em></p>
<p>You can use Spring config to achieve the configuration externalisation by using the following steps.</p>
<p style="color:blue">Step 1: Define config in a config source</p>
<pre style="background-color:white;">
# Elasticsearch
elasticsearch:
url: http://localhost:9200
user:
password:
index: micro
doc_type: items
</pre>
<p style="color:blue">Step 2: Inject the config properties to a bean</p>
<pre style="background-color:white;">
@Component("ElasticConfig")
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticsearchConfig {
// Elasticsearch stuff
private String url;
private String user;
...
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
</pre>
<p style="color:blue">Step 3: Inject the config bean to other classes</p>
<pre style="background-color:white;">
@Autowired
private ElasticsearchConfig config;
String url = config.getUrl();
</pre>
<p><strong>Fault Tolerance</strong></p>
<p>Cloud Native microservices need to be fault tolerant as there are too many uncertain factors or moving parts. MicroProfile and Spring both provide a model to achieve fault tolerance.</p>
<p><em style="color:blue">MicroProfile Fault Tolerance</em></p>
<p><a href="https://github.com/eclipse/microprofile-fault-tolerance/releases">MicroProfile Fault Tolerance</a> provides the following capabilities by using the annotations of @Timeout, @Retry, @Fallback, @Bulkhead, @CircuitBreaker:</p>
<p style="padding-left:20px">Timeout: Define a duration for timeout<br>
Retry: Define some criteria on when to retry<br>
Fallback: Provide an alternative solution for a failed execution.<br>
Bulkhead: Isolate failures in part of the system while the rest of the system can still function.<br>
CircuitBreaker: Offer a way of fail fast by automatically failing execution to prevent the system from overloading and indefinite wait or timeout by the clients.</p>
<p>The following code snippet describes the invocation of <code style="color:grey">getInventory()</code> times out after 2s. If the operation fails, the maximum 2 retries will be performed within the overall duration of 2s. For 20 consecutive invocations, if half failures occurr, the circuit will be trapped open. If failure still occurs after the retry, fallback operation <code style="color:grey">fallbackInventory</code> methold will be invoked.</p>
<pre style="background-color:white;">
@Timeout(value = 2, unit = ChronoUnit.SECONDS)
@Retry(maxRetries = 2, maxDuration = 2000)
@CircuitBreaker
@Fallback(fallbackMethod = "fallbackInventory")
@GET
public List&lt;Item&gt; getInventory() {
return items;
}
public List&lt;Item&gt; fallbackInventory() {
//Returns a default fallback
return fallbackitemslist;
}
</pre>
<p>Refer to <a href="https://openliberty.io/guides/retry-timeout.html">this</a> interactive Open Liberty guide to learn more about MicroProfile Fault Tolerance.</p>
<p><em style="color:blue">Spring Fault Tolerance</em></p>
<p>Spring uses Hysterix to achieve fault tolerance, detailed below.</p>
<pre style="background-color:white;">
@Service
public class AppService {
@HystrixCommand(fallbackMethod = "fallback")
public List&lt;Item&gt; getInventory() {
return items;
}
public List&lt;Item&gt; fallback() {
//Returns a default fallback
return fallbackitemslist;
}
}
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker
@SpringBootApplication
@RestController
@EnableCircuitBreaker
public class Application {
...
}
</pre>
<p><strong>Secure Microservices</strong></p>
<p>Cloud native microservices should be secured as they are exposed in the open, prone to be attacked. MicroProfile uses MicroProfile JWT together with Java EE Security while Spring uses Spring security.</p>
<p><em style="color:blue">MicroProfile JWT</em></p>
<p>MicroProfile JWT builds on the top of JWT with a few claims added to JWT to identify user id and user rules. The following code snippet demonstrates that the endpoint /orders can only be accessed by someone with the role of "<code style="color:grey">admin</code>".</p>
<pre style="background-color:white;">
@DeclareRoles({"Admin", "User"})
@RequestScoped
@Path("/orders")
public class OrderService {
@Inject private JsonWebToken jwt;
@GET
@RolesAllowed({ "admin" })
@Produces(MediaType.APPLICATION_JSON)
public InventoryList listContents() {
return manager.list();
}
...
}
</pre>
<p>Refer to this <a href="https://openliberty.io/guides/microprofile-jwt.html">Open Liberty Guide</a> to learn how to use MicroProfile JWT.</p>
<p><em style="color:blue">Spring Security</em></p>
<p>You can secure a Spring microservice by configuring Spring Security. If Spring Security is on the classpath, then Spring Boot automatically secures all HTTP endpoints using basic authentication.</p>
<p>First, you need to specify the dependency on <code style="color:grey">spring-boot-starter-security</code>.<br>
Second, in your microservice, specify the following annotation <code style="color:grey">EnableWebSecurity</code> or <code style="color:grey">EnableResourceServer</code> to secure microservices. See example below.
<pre style="background-color:white;">
@Configuration
@EnableWebSecurity
@EnableResourceServer
public class OAuth2ResourceServerConfig extends
ResourceServerConfigurerAdapter {
@Autowired
Private JWTConfig securityConfig;
.........
.........
}
</pre>
<h4>Microservices Performance</h4>
<p>After you have deployed your microservices in the cloud, it is DevOps's responsibility to monitor microservices' performance. If something goes wrong, DevOps needs some monitoring data to identify the bottleneck or spot any warning from metrics data. The intelligent cloud native microservices should be able to communicate with the cloud infrastructure about its health status on whether it is ready to receive traffic or serve requests etc. Let's see what the programming models have to offer regarding this aspect.</p>
<p><strong>Health Check</strong></p>
<p>A Cloud native microservice should be able to communicate with the cloud infrastructure about its health status. Both MicroProfile and Spring provide this capability. Kubernetes, the most popular microservice orchestrator, can check the container's (running instance of microservices) readiness or liveness status. Liveness means the microservice is not live. A pod restart needs to be performed, such as running out of memory. Readiness means the microservice is not ready for server request, such as unresponsive DB connection, etc.</p>
<p><em style="color:blue">MicroProfile Health</em></p>
<p>MicroProfile Health 2.0 and onwards provides both Readiness and Liveness endpoints. Microservices can provide implementations of <code style="color:grey">HealthCheck</code> with the annotation of <code style="color:grey">@Readiness</code> to config readiness check procedures. The aggregation of all the beans implementations of <code style="color:grey">HealthCheck</code> and the annotation <code style="color:grey">@Readiness</code> configures the endpoint of /ready.</p>
<pre style="background-color:white;">
@Readiness
public class HealthEndpoint implements HealthCheck {
@Override
public HealthCheckResponse call() {...}
}
</pre>
<p>Similarly, microservices can provide implementations of HealthCheck with the annotation of <code style="color:grey">@Liveness</code> to config liveness check procedures. The aggregation of all the beans implementations of <code style="color:grey">HealthCheck</code> with the annotation of <code style="color:grey">@Liveness</code> configures the endpoint of /live.</p>
<pre style="background-color:white;">
@Liveness
public class HealthEndpoint implements HealthCheck {
@Override
public HealthCheckResponse call() {...}
}
</pre>
<p>Kubernetes can query the /health/live or /health/ready endpoints in its liveness or readiness probe correspondingly, as per the code snippet below.</p>
<pre>
livenessProbe:
exec:
command:
- curl
- -f
- http://localhost:8080/health/live
initialDelaySeconds: 120
periodSeconds: 10
readinessProbe:
exec:
command:
- curl
- -f
- http://localhost:8080/health/live
initialDelaySeconds: 120
periodSeconds: 10
</pre>
<p>Refer to this Open Liberty Guide to learn how to use MicroProfile Health.</p>
<p><em style="color:blue">Spring</em></p>
<p>Spring Boot uses Actuator to provide the application's health status. SpringBoot Actuator exposes /health endpoint to indicate the running application's health status, such as Databbase connection, lack of disk space, etc. Applications provide this info via the implementations of <code style="color:grey">HealthIndicator</code>. This health information is collected from all the beans implementing the <code style="color:grey"><em>HealthIndicator</em></code> interface configured in the application context. Below is an example of custom health implementation.</p>
<pre style="background-color:white;">
@Component
public class HealthCheck implements HealthIndicator {
@Override
public Health health() {
int errorCode = check(); // perform some specific health check
if (errorCode != 0) {
return Health.down()
.withDetail("Error Code", errorCode).build();
}
return Health.up().build();
}
public int check() {
// Our logic to check health
return 0;
}
}
</pre>
<p><strong>Metrics</strong></p>
<p>For a running cloud native microservice, it is useful to know how much traffic it is serving, what's the throughput, any sign it might stop working soon. Metrics can help with this.</p>
<p><em style="color:blue">MicroProfile Metrics</em></p>
<p><a href="https://github.com/eclipse/microprofile-metrics/releases">MicroProfile Metrics</a> provides an endpoint <code style="color:grey">/metrics</code> to expose all the metrics info including underline runtime. The endpoint of /metrics displays some base metrics. As an example, Open Liberty provides the following metrics type out of box. The details for each type were omitted in this article.</p>
<pre style="background-color:white;">
# TYPE base:classloader_total_loaded_class_count counter
# TYPE base:gc_global_count counter
# TYPE base:cpu_system_load_average gauge
# TYPE base:thread_count counter
# TYPE base:classloader_current_loaded_class_count counter
# TYPE base:gc_scavenge_time_seconds gauge
# TYPE base:jvm_uptime_seconds gauge
# TYPE base:memory_committed_heap_bytes gauge
# TYPE base:thread_max_count counter
# TYPE base:cpu_available_processors gauge
# TYPE base:thread_daemon_count counter
# TYPE base:gc_scavenge_count counter
# TYPE base:classloader_total_unloaded_class_count counter
# TYPE base:memory_max_heap_bytes gauge
# TYPE base:cpu_process_cpu_load_percent gauge
# TYPE base:memory_used_heap_bytes gauge
# TYPE base:gc_global_time_seconds gauge
...
</pre>
<p>You can add application specific metrics to gather more metrics. Below is an example of how to collect the response time and invocation number etc for the associated endpoint.</p>
<pre style="background-color:white;">
@Timed(name = "Inventory.timer", absolute = true, displayName="Inventory Timer", description = "Time taken by the Inventory", reusable=true)
@Counted(name="Inventory", displayName="Inventory Call count", description="Number of times the Inventory call happened.", monotonic=true, reusable=true)
@Metered(name="InventoryMeter", displayName="Inventory Call Frequency", description="Rate of the calls made to Inventory", reusable=true)
// Get all rows from database
public List&lt;Item&gt; findAll(){ }
</pre>
<p>Refer to this <a href="https://openliberty.io/guides/microprofile-metrics.html">Open Liberty Guide</a> to learn how to use MicroProfile Metrics.</p>
<p><em style="color:blue">Spring Actuator</em></p>
<p>Spring also provides metrics via Spring Actuator. Spring Actuator exposes an endpoint <code style="color:grey">/metrics</code> to display application metrics. In the following code snippet, <code style="color:grey">/metrics</code> displays the number of valid list and invalid list counts. Below is an example of a custom Metrics implementation.</p>
<pre style="background-color:white;">
@Service
public class LoginServiceImpl {
private final CounterService counterService;
public List&lt;Item&gt; findAll (CounterService counterService) {
this.counterService = counterService;
if(list.size()>1)
counterService.increment("counter.list.valid ");
else
counterService.increment("counter.list.invalid");
}
</pre>
<p><strong>Distributed Tracing</strong></p>
<p>In a microservice architecture, it is common that one microservice invokes another microservice and so on. For DevOps, it is important to view the invocation chain. When there is a problem, the faulty service should be pinned down right away. In order to support this, we need a way to create a chain of invocations. Luckily, this is where distributed tracing comes into play. The implementation detail for the distributed tracing is to propagate the correlation id down the invocation chain, so that Zipkin or Jaeger can use this common correlation id to form a chain. Both MicroProfile and Spring have distributed tracing support.</p>
<p><em style="color:blue">MicroProfile Open Tracing</em></p>
<p><a href="https://github.com/eclipse/microprofile-opentracing/releases">MicroProfile Open Tracing</a> defines behaviours and an API for accessing an OpenTracing compliant Tracer object within your JAX-RS application. The behaviours specify how incoming and outgoing requests will have OpenTracing Spans automatically created.</p>
<p>When a request is sent from a traced client, a new Span is created and its SpanContext is injected in the outbound request for propagation downstream. The new Span will be a child of the active Span, if an active Span exists. The new Span will be finished when the outbound request is completed. All JAX-RS and Rest Client invocations automatically have the correlation id propagated.</p>
<p>You can specify non-JAX-RS operations to have correlation id propagated via <code style="color:grey">@Traced</code>, detailed below.</p>
<p>Custom Trace Implementation</p>
<pre style="background-color:white;">
import org.eclipse.microprofile.opentracing.Traced;
import io.opentracing.ActiveSpan;
import io.opentracing.Tracer;
@Traced(value = true, operationName ="getCatalog.list")
public List&lt;Item&gt; getInventory() {
try (ActiveSpan childSpan = tracer.buildSpan("Grabbing
messages from Messaging System").startActive()) {...}
}
</pre>
<p>Visit this Open Liberty <a href="https://openliberty.io/guides/microprofile-opentracing.html">guide</a> to learn more about MicroProfile Open Tracing.</p>
<p><em style="color:blue">Spring Tracing</em></p>
<p>Spring uses Spring Cloud Sleuth for distributed tracing support. If the Spring cloud sleuth is configured in the class path, the trace information will be generated automatically.</p>
<pre style="background-color:white;">
&lt;dependency>
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-sleuth&lt;/artifactId&gt;
&lt;/dependency&gt;
</pre>
<p>By now, you should have some ideas of the capabilities of MicroProfile and Spring, let's get started with creating your cloud-native microservices.</p>
<p><strong>Getting started</strong></p>
<p>Both MicroProfile and Spring have a starter page.</p>
<p><em style="color:blue">MicroProfile starter</em></p>
<p>MicroProfile starter (<a href="https://start.microprofile.io/">https://start.microprofile.io/</a> ) provides a great way for you to create a microservice using MicroProfile and you can choose your favourite runtime, such as <a href="https://openliberty.io/">Open Liberty</a>, <a href="https://thorntail.io/">Thorntail</a>, <a href="https://www.payara.fish/">Payara</a>, <a href="https://tomee.apache.org/">TomEE</a>, <a href="https://ee.kumuluz.com/">KumuluzEE</a>, <a href="https://helidon.io/">Helidon</a>, etc.</p>
<p align="center"><img class="img-responsive" width="80%" src="/community/eclipse_newsletter/2019/august/images/mp2.png"></p>
<p>You can also use a command line to create a microservice using MicroProfile. Refer to <a href="https://microprofile.io/2019/07/08/command-line-interface-for-microprofile-starter-is-available-now/">Karm's blog</a> on how to use the command line tool. We are currently creating a VS Code extension plug-in to allow you to directly create microservices from your IDE. We plan to create extensions for other IDE such as Intellij, Eclipse Che, etc. Stay tuned!</p>
<p><em style="color:blue">Spring Starter</em></p>
<p>Spring has a starter page (<a href="https://start.spring.io/">https://start.spring.io/</a>) to help you create a Spring Boot application.</p>
<p align="center"><img class="img-responsive" width="80%" src="/community/eclipse_newsletter/2019/august/images/mp3.png"></p>
<h3>Differences</h3>
<p>MicroProfile and Spring are comparable from a functional perspective. However, they do have differences, which are summarised below.</p>
<table class="table table-sm">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Spring</th>
<th scope="col">MicroProfile</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="col">APIs</th>
<td scope="col">Open Source<br>Driven by Pivotal<br>Spring way of things</td>
<td scope="col">Open Source<br>Driven by Community<br>Open standards<br>Behaviours in accordance specifications</td>
</tr>
<tr>
<th scope="col">Lines of Code</th>
<td scope="col">More code<br>Do what you want/need with code (and configuration)</td>
<td scope="col">Less code<br>Customize server configuration</td>
</tr>
<tr>
<th scope="col">Libraries/Dependencies</th>
<td scope="col">Find, mix and match what you like<br>Manage your own dependencies</td>
<td scope="col">Runtime provides what is needed per specifications</td>
</tr>
<tr>
<th scope="col">Application Packaging</th>
<td scope="col">Fat JARs</td>
<td scope="col">Thin/Skinny JARs<br>Note: Liberty has optimized support for Spring Boot apps in containers</td>
</tr>
</tbody>
</table>
<h3>Summary</h3>
<p>Both Spring and Eclipse MicroProfile provide facilities for developers to build next-generation cloud native microservices with the following observations.</p>
<p style="padding-left:20px">They share similarities<br>There are differences, too (and sometimes, significant ones)</p>
<p>Spring has been around for quite a few years and has gained a lot of popularity. Eclipse MicroProfile and Java EE (now Jakarta EE) are evolving rapidly (and gaining momentum) as community-driven and standards-based efforts for developing microservices and cloud native applications in enterprise Java.</p>
<p>It is great that developers can now choose what they prefer. Companies should provide developers with platforms that enable innovation and flexibility and are enterprise and production ready. Open Liberty (<a href="https://openliberty.io/">https://openliberty.io/</a>), a fast, small and lightweight runtime, supports both Eclipse MicroProfile/Java EE and Spring.</p>
<h3>Acknowledgements</h3>
<p>This article was heavily influenced by the experiment of converting IBM BlueCompute microservices migrating from Spring to Eclipse MicroProfile. The series of blogs describing the migration can be found <a href="https://www.ibm.com/cloud/blog/migrate-java-microservices-from-spring-to-microprofile-p1">here</a>. Many thanks to my colleague YK Chang for his input into this article.</p>
<h3>References</h3>
<ol>
<li><a href="https://www.ibm.com/blogs/bluemix/2018/09/migrate-java-microservices-from-spring-to-microprofile-p1/">Migrating Bluecompte app from Spring to MicroProfile blogs</a></li>
<li><a href="https://microprofile.io/">MicroProfile sites</a></li>
<li><a href="https://wiki.eclipse.org/MicroProfile/Implementation">MicroProfile implementation runtimes</a></li>
<li><a href="https://start.microprofile.io/">MicroProfile starter page</a></li>
<li><a href="https://microprofile.io/blog/">MicroProfile blogs</a></li>
<li><a href="https://openliberty.io/">Open Liberty site</a></li>
<li><a href="https://www.eclipse.org/community/eclipse_newsletter/2018/september/MicroProfile_istio.php">MicroProfile and Istio ecosystem</a></li>
<li><a href="https://github.com/IBM/cloud-native-starter">Cloud Native Starter with MicroProfile and Istio</a></li>
</ol>
<div class="bottomitem">
<h3>About the Author</h3>
<div class="row">
<div class="col-sm-12">
<div class="row">
<div class="col-sm-8">
<img class="img-responsive"
src="\community\eclipse_newsletter\2019\august\images\emily.png"
alt="Emily Jiang" />
</div>
<div class="col-sm-16">
<p class="author-name"><br />
Emily Jiang<br />
</p>
<ul class="author-link list-inline">
<li><a class="btn btn-small btn-warning" target="_blank" href="https://twitter.com/emilyfhjiang">Twitter</a></li>
<?php echo $og; ?>
</ul>
</div>
</div>
</div>
</div>
</div>