JavaEE MicroProfile with KumuluzEE
Preface
Enterprise Java seems to be a step back from the others when it comes to microservices architecture. Some weeks ago I took a part in Code Europe – the programming conference in Warsaw. One of the speakers was Ivar Grimstad who was talking about MicroProfile – an open initiative for optimizing Enterprise Java for a microservices architecture. This idea is very interesting, but at the moment it is rather at the beginning of the road.
However, while I was reading about the microprofile initiative I came across information about JavaEE framework developed by Slovenian company – KumuluzEE. The solution seemed to be interesting enough that I decided to take a closer look on it. Well, we can read on the web site that KumuluzEE is the Java Duke’s Choice Award Winner, so there is still a hope for JavaEE and microservices 🙂
What’s KumuluzEE
Can KumuluzEE be a competitor for the Spring Cloud framework? He is certainly not as popular and advanced in the solutions for microservices like Spring Cloud, but has basic modules for service registration, discovery, distributed configuration propagation, circuit breaking, metrics and support for Docker and Kubernetes. It uses CDI on JBoss Weld container for dependency injection and Jersey as a REST API provider. Modules for configuration and discovery basing on Consul or etcd and they are rather on early stage of development (1.0.0-SNAPSHOT), but let’s try it out.
Preparation
I’ll show you sample application which consists of two independent microservices account-service and customer-service. Both of them exposes REST API and one of customer-service methods invokes method from account-service. Every microservice registers itself in Consul and is able to get configuration properties from Consul. Sample application source code is available on GitHub. Before we begin let’s start Consul instance using Docker container.
[code]
docker run -d –name consul -p 8500:8500 -p 8600:8600 consul
[/code]
We should also add some KumuluzEE dependencies to Maven pom.xml
.
[code language=”xml”]
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-core</artifactId>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-servlet-jetty</artifactId>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-jax-rs-jersey</artifactId>
</dependency>
<dependency>
<groupId>com.kumuluz.ee</groupId>
<artifactId>kumuluzee-cdi-weld</artifactId>
</dependency>
[/code]
Service Registration
To enable service registration we should add one additional dependency to our pom.xml
. I chose Consul as a registration and discovery server, but you can also use etcd (kumuluzee-discovery-consul
).
[code language=”xml”]
<dependency>
<groupId>com.kumuluz.ee.discovery</groupId>
<artifactId>kumuluzee-discovery-consul</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
[/code]
Inside application configuration file we should set discovery properties and server URL. For me it is 192.168.99.100.
[code]
kumuluzee:
service-name: account-service
env: dev
version: 1.0.0
discovery:
consul:
agent: http://192.168.99.100:8500
hosts: http://192.168.99.100:8500
ttl: 20
ping-interval: 15
[/code]
Here’s account microservice main class. As you probably guess annotation @RegisterService
enables registration on server.
[code langugae=”java”]
@RegisterService("account-service")
@ApplicationPath("v1")
public class AccountApplication extends Application {
}
[/code]
We are starting application by running java -cp target/classes;target/dependency/* com.kumuluz.ee.EeApplication
. Remember to override default port by setting environment property PORT. I started two instances of account and one of customer microservice.
Service Discovery
Microservice customer exposes API, but also invokes API method from account-service, so it has to discover and connect this service. Maven dependencies and configuration settings are the same as for account-service. The only difference is the resource class. Here’s CustomerResource fragment where we are invoking enpoint GET /customer/{id}.
[code language=”java”]
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("customers")
@RequestScoped
public class CustomerResource {
private List<Customer> customers;
@Inject
@DiscoverService(value = "account-service", version = "1.0.x", environment = "dev")
private WebTarget target;
…
@GET
@Path("{id}")
@Log(value = LogParams.METRICS, methodCall = true)
public Customer findById(@PathParam("id") Integer id) {
Customer customer = customers.stream().filter(it -> it.getId().intValue() == id.intValue()).findFirst().get();
WebTarget t = target.path("v1/accounts/customer/" + customer.getId());
List<Account> accounts = t.request().buildGet().invoke(List.class);
customer.setAccounts(accounts);
return customer;
}
}
[/code]
There is one pretty cool thing in discovery with KumuluzEE. As you see in the @DiscoverService
we can specify version and environment for account-service instance. Version and environment for microservice is read automatically from config.yml
during registration in discovery server. So we can maintain many versions of single microservice and freely invoke them from other microservices. Requests are automatically load balanced between all microservices matches conditions from annotation @ServiceDiscovery
.
We can also monitor metrics such as response time by declaring @Log(value = LogParams.METRICS, methodCall = true)
on API method. Here’s log fragment for account-service.
[code]
2017-07-28 13:57:01,114 TRACE ENTRY[ METHOD ] Entering method. {class=pl.piomin.services.kumuluz.account.resource.AccountResource, method=findByCustomer, parameters=[1]}
2017-07-28 13:57:01,118 TRACE EXIT[ METHOD ] Exiting method. {class=pl.piomin.services.kumuluz.account.resource.AccountResource, method=findByCustomer, parameters=[1], response-time=3, result=[pl.piomin.services.kumuluz.account.model.Account@1eb26fe3, pl.piomin.services.kumuluz.account.model.Account@2dda41c5]}
[/code]
Distributed configuration
To enable KumuluzEE Config include Consul implementation by adding the following dependency to pom.xml
.
[code language=”xml”]
<dependency>
<groupId>com.kumuluz.ee.config</groupId>
<artifactId>kumuluzee-config-consul</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
[/code]
I do not use Consul agent running on localhost, so I need to override some properties in config.yml
. I also defined one configuration property blacklist
[code]
kumuluzee:
config:
start-retry-delay-ms: 500
max-retry-delay-ms: 900000
consul:
agent: http://192.168.99.100:8500
rest-config:
blacklist:
[/code]
Here’s the class that loads configuration properties and enables dynamically updated on any change in configuration source by declaring @ConfigValue(watch = true)
on property.
[code language=”java”]
@ApplicationScoped
@ConfigBundle("rest-config")
public class AccountConfiguration {
@ConfigValue(watch = true)
private String blacklist;
public String getBlacklist() {
return blacklist;
}
public void setBlacklist(String blacklist) {
this.blacklist = blacklist;
}
}
[/code]
We use configution property blacklist
in the resource class for filtering all accounts by blacklisted ids.
[code language=”java”]
@GET
@Log(value = LogParams.METRICS, methodCall = true)
public List<Account> findAll() {
final String blacklist = ConfigurationUtil.getInstance().get("rest-config.blacklist").orElse("nope");
final String[] ids = blacklist.split(",");
final List<Integer> blacklistIds = Arrays.asList(ids).stream().map(it -> new Integer(it)).collect(Collectors.toList());
return accounts.stream().filter(it -> !blacklistIds.contains(it.getId())).collect(Collectors.toList());
}
[/code]
Configuration property should be defined in Consul UI Dashboard under KEY/VALUE tab. KumuluzEE enforces a certain format of key name. In this case it has to be environments/dev/services/account-service/1.0.0/config/rest-config/blacklist
. You can update property value and test changes by invoking http://localhost:2222/v1/accounts.
Final Words
Creating microservices with KumuluzEE is pretty easy. I showed you the main capabilities of this framework. KumulezEE has also modules for bircuit breaker with Hystrix, streaming with Apache Kafka and security with OAuth2/OpenID. I will keep a close eye on this library and I hope it will continue to be developed.
Leave a ReplyCancel reply