Apache Camel Archives - Piotr's TechBlog https://piotrminkowski.com/tag/apache-camel/ Java, Spring, Kotlin, microservices, Kubernetes, containers Tue, 15 Dec 2020 09:07:15 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.1 https://i0.wp.com/piotrminkowski.com/wp-content/uploads/2020/08/cropped-me-2-tr-x-1.png?fit=32%2C32&ssl=1 Apache Camel Archives - Piotr's TechBlog https://piotrminkowski.com/tag/apache-camel/ 32 32 181738725 Apache Camel K and Quarkus on Kubernetes https://piotrminkowski.com/2020/12/08/apache-camel-k-and-quarkus-on-kubernetes/ https://piotrminkowski.com/2020/12/08/apache-camel-k-and-quarkus-on-kubernetes/#respond Tue, 08 Dec 2020 08:40:48 +0000 https://piotrminkowski.com/?p=9181 Apache Camel K and Quarkus may simplify our development on Kubernetes. They are both relatively new products. Apache Camel K is a lightweight integration framework that runs natively on Kubernetes. It allows us to run code written in Camel DSL on the cloud. We may easily integrate it with the Quarkus framework. As a result, […]

The post Apache Camel K and Quarkus on Kubernetes appeared first on Piotr's TechBlog.

]]>
Apache Camel K and Quarkus may simplify our development on Kubernetes. They are both relatively new products. Apache Camel K is a lightweight integration framework that runs natively on Kubernetes. It allows us to run code written in Camel DSL on the cloud. We may easily integrate it with the Quarkus framework. As a result, we would have a powerful solution that helps us in building serverless or microservices applications.

It is my first article about Apache Camel K. However, you will find many interesting posts about Quarkus on my blog. It is worth reading Guide to Quarkus on Kubernetes before proceeding with this article. If you would like to know more about microservices with Quarkus you should also refer to Quick Guide to Microservices with Quarkus on OpenShift.

In this article, I will show you how to integrate Quarkus with Apache Camel. Consequently, we will use the Camel Quarkus project for that. It provides Quarkus extensions for many of the Camel components. Finally, you will also learn how to install Apache Camel K on Kubernetes and then use it to deploy our Quarkus Camel application there. Ok, let’s do this!

Source code

If you would like to try it by yourself, you may always take a look at my source code. In order to do that you need to clone my repository sample-camel-quarkus. Then you should go to the account-service directory, and just follow my instructions πŸ™‚ If you are interested in more details about Apache Camel you should read its documentation.

Enable integration between Apache Camel and Quarkus

We are going to create a simple application that exposes a REST API and uses an in-memory repository. There are many camel.quarkus.* components that may be used between Apache Camel and Quarkus. In order to build a REST-based application, we need to include three of them: Rest, Jackson, and Platform HTTP. I have also included a well-known Lombok module.

<dependency>
   <groupId>org.apache.camel.quarkus</groupId>
   <artifactId>camel-quarkus-platform-http</artifactId>
</dependency>
<dependency>
   <groupId>org.apache.camel.quarkus</groupId>
   <artifactId>camel-quarkus-rest</artifactId>
</dependency>
<dependency>
   <groupId>org.apache.camel.quarkus</groupId>
   <artifactId>camel-quarkus-jackson</artifactId>
</dependency>
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <version>1.18.16</version>
</dependency>

Then we should add a dependencyManagement section with Camel Quarkus BOM. Apache Camel K ignores this section during deployment on Kubernetes. However, it may be useful for a local run with mvn compile quarkus:dev.

<dependencyManagement>
   <dependencies>
      <dependency>
         <groupId>org.apache.camel.quarkus</groupId>
         <artifactId>camel-quarkus-bom</artifactId>
         <version>${camel-quarkus.version}</version>
         <type>pom</type>
         <scope>import</scope>
      </dependency>
   </dependencies>
</dependencyManagement>

Create Quarkus application using Apache Camel DSL

The most comfortable way to deploy the application on Kubernetes with Apache Camel K is by setting a single file name in the running command. So, we should have the logic closed inside a single source code file. Although it is a standard for serverless applications, it is not comfortable for microservices. Let’s take a look at the model class. Both model and repository classes will be nested inside the single AccountRoute class.

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Account {
   private Integer id;
   private String number;
   private int amount;
   private Integer customerId;
}

Here’s our in-memory repository implementation.

public class AccountService {

   private List<Account> accounts = new ArrayList<>();

   AccountService() {
      accounts.add(new Account(1, "1234567890", 5000, 1));
      accounts.add(new Account(2, "1234567891", 12000, 1));
      accounts.add(new Account(3, "1234567892", 30000, 2));
   }

   public Optional<Account> findById(Integer id) {
      return accounts.stream()
            .filter(it -> it.getId().equals(id))
            .findFirst();
   }

   public List<Account> findByCustomerId(Integer customerId) {
      return accounts.stream()
            .filter(it -> it.getCustomerId().equals(customerId))
            .collect(Collectors.toList());
   }

   public List<Account> findAll() {
      return accounts;
   }

   public Account add(Account account) {
      account.setId(accounts.size() + 1);
      accounts.add(account);
      return account;
   }

}

Finally, we may proceed to the AccountRoute implementation. It extends a Camel RouteBuilder base class. It has to override the configure method to define Camel routes using the REST component. Firstly, we need to set a global JSON binding mode for all the routes. Then we may define REST endpoints using rest() DSL method. There are four HTTP endpoints as shown below:

  • GET /accounts/{id} – find a single account object by its id.
  • GET /accounts/customer/{customerId} – find a list of account objects by the customer id.
  • POST /accounts – add a new account.
  • GET /accounts – list all available accounts.
@ApplicationScoped
public class AccountRoute extends RouteBuilder {

   AccountService accountService = new AccountService();

   @Override
   public void configure() throws Exception {
      restConfiguration().bindingMode(RestBindingMode.json);

      rest("/accounts")
            .get("/{id}")
               .route().bean(accountService, "findById(${header.id})").endRest()
            .get("/customer/{customerId}")
               .route().bean(accountService, "findByCustomerId(${header.customerId})").endRest()
            .get().route().bean(accountService, "findAll").endRest()
            .post("/")
               .consumes("application/json").type(Account.class)
               .route().bean(accountService, "add(${body})").endRest();
   }

   // model and repository implementations ...
}

Install Apache Camel K on Kubernetes

Our Quarkus application is ready. Now, we need to deploy it on Kubernetes. And here comes Apache Camel K. With this solution, we can easily deploy Camel routes directly from the source code by executing command kamel run $SOURCE_FILE_LOCATION. After that, the integration code immediately runs in the cloud. Of course, to achieve that we first need to install Apache Camel K on Kubernetes. Let’s take a closer look at the installation process.

The first information is not very positive. Apache Camel K supports the default registry for OpenShift (including local Minishift or CRC) and Minikube. I’m using Kubernetes on Docker Desktop… Ok, it doesn’t put me off. We just need to add some additional parameters in the installation command. Let’s set the docker.io repository. Of course, you need to have an account on Docker Hub as shown below.

$ kamel install --registry docker.io --organization your-user-id --registry-auth-username your-user-id --registry-auth-password your-password

This command will create custom resource definitions on the cluster and install the operator on the current namespace. Let’s verify it.

Deploy Quarkus on Kubernetes using Apache Camel K

Then, we should go to the account-service directory. In order, to deploy the Quarkus application with Apache Camel K we need to execute the following command.

$ kamel run --name account --dev \
   src/main/java/pl/piomin/samples/quarkus/account/route/AccountRoute.java \
   --save

Unfortunately, Apache Camel K is not able to detect all the dependencies declared in Maven pom.xml. Therefore, we will declare them inside the comment on the AccountRoute class. They must be preceded by a keyword camel-k as shown below.

// camel-k: dependency=mvn:org.apache.camel.quarkus:camel-quarkus-jackson
// camel-k: dependency=mvn:org.projectlombok:lombok:1.18.16

@ApplicationScoped
public class AccountRoute extends RouteBuilder {
   ...
}

If everything goes fine, our Quarkus Camel application successfully starts on Kubernetes. So, you should see similar logs after executing the kamel run command.

apache-camel-k-quarkus-on-kubernetes-quarkus-logs

Then, we may take a look at a list of deployments once again. Apache Camel K has created a new deployment with the name account. We had set such a name in kamel run command using the name parameter.

In the background, Apache Camel K creates the CRD Integration. So, if you are interested in how it works, it is worth to print details about the Integration object.

$ kubectl get integration account -o yaml

Final testing

Since our application is running on Kubernetes, we may proceed to the tests. Firstly, let’s see a list of Kubernetes services. Fortunately, Apache Camel K automatically creates the service with a NodePort type. In my case, the account service is exposed at the port 30860.

apache-camel-k-quarkus-on-kubernetes-svc

Finally, let’s just send some test requests.

$ curl http://localhost:30860/accounts
[{"id":1,"number":"1234567890","amount":5000,"customerId":1},
 {"id":2,"number":"1234567891","amount":12000,"customerId":1},
 {"id":3,"number":"1234567892","amount":30000,"customerId":2}]
$ curl http://localhost:30860/accounts/1
{"id":1,"number":"1234567890","amount":5000,"customerId":1}
$ curl http://localhost:30860/accounts/2
{"id":2,"number":"1234567891","amount":12000,"customerId":1}
$ curl http://localhost:30860/accounts/customer/1
[{"id":1,"number":"1234567890","amount":5000,"customerId":1},
 {"id":2,"number":"1234567891","amount":12000,"customerId":1}]

And the last cool feature at the end. Let’s assume we will perform some changes in the Account.java file. The command kamel run is still running and watching for changes in the main Camel file. The latest version of the application with changes is immediately deployed on Kubernetes.

Conclusion

Apache Camel K fits perfectly to the serverless applications, where the whole code is encapsulated in a single file. However, we may also use it to deploy microservices on Kubernetes. We may take advantage of the built-in integration between Apache Camel K and Quarkus. The only constraint on using it is the necessity to have the whole code not directly related to Camel routes in a single file or in an external library. Other than that, Apache Camel K seems promising. Unfortunately, it doesn’t have the same level of support for Spring Boot as for Quarkus. I hope it will be improved in the near future. I’ll definitely keep an eye on the development of this framework.

The post Apache Camel K and Quarkus on Kubernetes appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2020/12/08/apache-camel-k-and-quarkus-on-kubernetes/feed/ 0 9181
Circuit Breaker, Fallback and Load Balancing with Apache Camel https://piotrminkowski.com/2017/06/01/circuit-breaker-fallback-and-load-balancing-with-apache-camel/ https://piotrminkowski.com/2017/06/01/circuit-breaker-fallback-and-load-balancing-with-apache-camel/#respond Thu, 01 Jun 2017 08:29:36 +0000 https://piotrminkowski.wordpress.com/?p=3372 Apache Camel has just released a new version of their framework – 2.19. In one of my previous articles on DZone, I described details about microservices support which was released in the Camel 2.18 version. There are some new features in ServiceCall EIP component, which is responsible for microservice calls. You can see example source […]

The post Circuit Breaker, Fallback and Load Balancing with Apache Camel appeared first on Piotr's TechBlog.

]]>
Apache Camel has just released a new version of their framework – 2.19. In one of my previous articles on DZone, I described details about microservices support which was released in the Camel 2.18 version. There are some new features in ServiceCall EIP component, which is responsible for microservice calls. You can see example source code which is based on the sample from my article on DZone. It is available on GitHub under new branch fallback.

In the code fragment below you can see the DLS route’s configuration with support for Hystrix circuit breaker, Ribbon load balancer, and Consul service discovery and registration. As a service discovery in the route definition you can also use some other solutions instead of Consul like etcd (etcServiceDiscovery) or Kubernetes (kubernetesServiceDiscovery).

from("direct:account")
   .to("bean:customerService?method=findById(${header.id})")
   .log("Msg: ${body}").enrich("direct:acc", new AggregationStrategyImpl());

from("direct:acc").setBody().constant(null)
   .hystrix()
      .hystrixConfiguration()
         .executionTimeoutInMilliseconds(2000)
      .end()
      .serviceCall()
         .name("account//account")
         .component("netty4-http")
         .ribbonLoadBalancer("ribbon-1")
         .consulServiceDiscovery("http://192.168.99.100:8500")
      .end()
      .unmarshal(format)
   .endHystrix()
   .onFallback()
   .to("bean:accountFallback?method=getAccounts");

We can easily configure all Hystrix’s parameters just by calling hystrixConfiguration method. In the sample above Hystrix waits max 2 seconds for the response from remote service. In case of timeout fallback @Bean is called. Fallback @Bean implementation is really simple – it return empty list.

@Service
public class AccountFallback {

   public List<Account> getAccounts() {
      return new ArrayList<>();
   }

}

Alternatively, the configuration can be implemented using object declarations. Here is a service call configuration with Ribbon and Consul. Additionally, we can provide some parameters to Ribbon like client read timeout or max retry attempts. Unfortunately, it seems they don’t work in this version of Apache Camel πŸ™‚ (you can try to test it by yourself). I hope this will be corrected soon.

ServiceCallConfigurationDefinition def = new ServiceCallConfigurationDefinition();

ConsulConfiguration config = new ConsulConfiguration();
config.setUrl("http://192.168.99.100:8500");
config.setComponent("netty4-http");
ConsulServiceDiscovery discovery = new ConsulServiceDiscovery(config);

RibbonConfiguration c = new RibbonConfiguration();
c.addProperty("MaxAutoRetries", "0");
c.addProperty("MaxAutoRetriesNextServer", "1");
c.addProperty("ReadTimeout", "1000");
c.setClientName("ribbon-1");
RibbonServiceLoadBalancer lb = new RibbonServiceLoadBalancer(c);
lb.setServiceDiscovery(discovery);

def.setComponent("netty4-http");
def.setLoadBalancer(lb);
def.setServiceDiscovery(discovery);
context.setServiceCallConfiguration(def);

I described a similar case for Spring Cloud and Netflix OSS in one of my previous article. Just like in the example presented there, I also set here a delay inside the account-service, which depends on the port on which the microservice was started.

@Value("${port}")
private int port;

public List<Account> findByCustomerId(Integer customerId) {
   List<Account> l = new ArrayList<>();
   l.add(new Account(1, "1234567890", 4321, customerId));
   l.add(new Account(2, "1234567891", 12346, customerId));
   if (port%2 == 0) {
      try {
         Thread.sleep(5000);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
   return l;
}

Results for the Spring Cloud sample were much more satisfying. The introduced configuration parameters such as read timeout for Ribbon worked and in addition Hystrix was able to automatically redirect a much smaller number of requests to slow service – only 2% of the rest to the non-blocking thread instance for 5 seconds. This shows that Apache Camel still has a few things to improve if wants to compete in microservice’s support with the Sprint Cloud framework.

The post Circuit Breaker, Fallback and Load Balancing with Apache Camel appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2017/06/01/circuit-breaker-fallback-and-load-balancing-with-apache-camel/feed/ 0 3372
Advanced Microservices with Apache Camel https://piotrminkowski.com/2017/04/04/advanced-microservices-with-apache-camel/ https://piotrminkowski.com/2017/04/04/advanced-microservices-with-apache-camel/#respond Tue, 04 Apr 2017 18:18:13 +0000 https://piotrminkowski.wordpress.com/?p=2259 This post is a continuation of my previous microservices sample with Apache Camel described in the post Microservices with Apache Camel. In the picture below you can see the architecture of the proposed solution. All the services will be available behind the API gateway, which is created using the Camel Rest DSL component. There is […]

The post Advanced Microservices with Apache Camel appeared first on Piotr's TechBlog.

]]>
This post is a continuation of my previous microservices sample with Apache Camel described in the post Microservices with Apache Camel. In the picture below you can see the architecture of the proposed solution. All the services will be available behind the API gateway, which is created using the Camel Rest DSL component. There is also API documentation available under api-doc context path on gateway. It is created using the Swagger framework.

camel_micro

Service discovery and registration were created using Consul. The gateway is interacting with the discovery server using ServiceCall EIP Camel component. Each microservice is registering itself during startup. There is no out of the box mechanisms for service registration in Apache Camel, so that I had to provide custom implementation using EventNotifierSupport class. Service Call EIP is also used inside customer service for discovering and calling account service to enrich returned customer object with its accounts. Microservices communicate with Zipkin to store timing statistics of calling their endpoints.

The sample application source code is available on GitHub. If you are interested in detailed description of introduced solution read my article on DZone. It was also published on Apache Camel site in the Articles section here.

The post Advanced Microservices with Apache Camel appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2017/04/04/advanced-microservices-with-apache-camel/feed/ 0 2259
Microservices with Apache Camel https://piotrminkowski.com/2017/03/03/microservices-with-apache-camel/ https://piotrminkowski.com/2017/03/03/microservices-with-apache-camel/#respond Fri, 03 Mar 2017 10:00:48 +0000 https://piotrminkowski.wordpress.com/?p=1367 Apache Camel, as usual, is a step backward in comparison with the Spring framework and there is no difference in the case of microservices architecture. However, Camel have introduced new set of components for building microservices some months ago. In its newest version 2.18 there is a support for load balancing with Netflix Ribbon, circuit […]

The post Microservices with Apache Camel appeared first on Piotr's TechBlog.

]]>
Apache Camel, as usual, is a step backward in comparison with the Spring framework and there is no difference in the case of microservices architecture. However, Camel have introduced new set of components for building microservices some months ago. In its newest version 2.18 there is a support for load balancing with Netflix Ribbon, circuit breaking with Netflix Hystrix, distributed tracing with Zipkin and service registration and discovery with Consul. The new key component for microservices support on Camel is ServiceCall EIP which allows to call a remote service in a distributed system where the service is looked up from a service registry. There are four tools which can be used as service registry for Apache Camel microservices: etcd, Kubernetes, Ribbon, and Consul. Release 2.18 also comes with a much-improved Spring Boot support.

In this articale I’m going to show you how to develop Apache Camel microservices with its support for Spring Boot, REST DSL and Consul. Sample application is available on GitHub. Below you see a picture with our application architecture.

camel-arch

To enable Spring Boot support in Camel application we need to add following dependency to pom.xml. After that we have to annotate our main class with @SpringBootApplication and set property camel.springboot.main-run-controller=true in application configuration file (application.properties or application.yml).

<dependency>
   <groupId>org.apache.camel</groupId>
   <artifactId>camel-spring-boot-starter</artifactId>
   <version>${camel.version}</version>
</dependency>

Then we just have to create Spring @Component extending Camel’s RouteBuilder. Inside route builder configuration we declare REST endpoint using Camel REST DSL. It’s really simple and intuitive. In the code visible below I exposed four REST endpoints: three for GET method and an single one for POST. We are using netty4-http component as a web container for exposing REST endpoints and JSON binding. We also have to add to dependencies to pom.xml: camel-netty4-http for Netty framework and camel-jackson library for enabling consuming and producing JSON data. All routes are forwarding input requests to different methods inside Spring service @Component.

@Component
public class AccountRoute extends RouteBuilder {

   @Value("${port}")
   private int port;

   @Override
   public void configure() throws Exception {
      restConfiguration()
         .component("netty4-http")
         .bindingMode(RestBindingMode.json)
         .port(port);

      rest("/account")
         .get("/{id}")
         .to("bean:accountService?method=findById(${header.id})")
         .get("/customer/{customerId}")
         .to("bean:accountService?method=findByCustomerId(${header.customerId})")
         .get("/")
         .to("bean:accountService?method=findAll")
         .post("/").consumes("application/json").type(Account.class)
         .to("bean:accountService?method=add(${body})");
   }

}

Next element in our architecture is service registry component. We decided to use Consul. The simplest way to run it locally is to pull its docker image and run using docker command below. Consul provides UI management console and REST API for registering and searching services and key/value objects. REST API is available under v1 path and is well documented here.

$ docker run -d --name consul -p 8500:8500 -p 8600:8600 consul

Well, we have account microservice implemented and running Consul instance, so we would like to register our service there. And here we’ve got a problem. There is no mechanisms out of the box in Camel for service registration, there is only component for searching service. To be more precise I didn’t find any description about such a mechanism in Camel documentation… However, it may exists… somewhere. Maybe, you know how to find it? Here’s interesting solution for Camel Consul registry, but I didn’t check it out. I decided to rather simpler solution implemented by myself. I added two next routes to AccountRoute class.

from("direct:start").marshal().json(JsonLibrary.Jackson)
   .setHeader(Exchange.HTTP_METHOD, constant("PUT"))
   .setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
   .to("http://192.168.99.100:8500/v1/agent/service/register");
from("direct:stop").shutdownRunningTask(ShutdownRunningTask.CompleteAllTasks)
   .toD("http://192.168.99.100:8500/v1/agent/service/deregister/${header.id}");

Route direct:start is running after Camel context startup and direct:stop before shutdown. Here’s EventNotifierSupport implementation for calling routes during startup and shutdown process. You can also try with camel-consul component, but in my opinion it is not well described in Camel documentation. List of services registered on Consul is available here: http://192.168.99.100:8500/v1/agent/services. I launch my account service with VM argument -Dport and it should be registered on Consul with account${port} ID.

@Component
public class EventNotifier extends EventNotifierSupport {

   @Value("${port}")
   private int port;

   @Override
   public void notify(EventObject event) throws Exception {
      if (event instanceof CamelContextStartedEvent) {
         CamelContext context = ((CamelContextStartedEvent) event).getContext();
         ProducerTemplate t = context.createProducerTemplate();
         t.sendBody("direct:start", new Register("account" + port, "account", "127.0.0.1", port));
      }
      if (event instanceof CamelContextStoppingEvent) {
         CamelContext context = ((CamelContextStoppingEvent) event).getContext();
         ProducerTemplate t = context.createProducerTemplate();
         t.sendBodyAndHeader("direct:stop", null, "id", "account" + port);
      }
   }

   @Override
   public boolean isEnabled(EventObject event) {
      return (event instanceof CamelContextStartedEvent || event instanceof CamelContextStoppingEvent);
   }

}

The last (but not least) element of our architecture is gateway. We also use netty for exposing REST services on port 8000.

restConfiguration()
   .component("netty4-http")
   .bindingMode(RestBindingMode.json)
   .port(8000);

We also have to provide configuration for connection with Consul registry and set it on CamelContext calling setServiceCallConfiguration method.

ConsulConfigurationDefinition config = new ConsulConfigurationDefinition();
config.setComponent("netty4-http");
config.setUrl("http://192.168.99.100:8500");
context.setServiceCallConfiguration(config);

Finally, we are defining routes which are mapping paths set on gateway to services registered on Consul using ServiceCall EIP. Now you call in your web browser one of those URLs, for example http://localhost:8000/account/1. If you would like to map path also while serviceCall EIP you need to put ‘//‘ instead of sinle slash ‘/‘ described in the Camel documentation. For example from(“rest:get:account”).serviceCall(“account//all”), not serviceCall(“account/all”).

from("rest:get:account:/{id}").serviceCall("account");
from("rest:get:account:/customer/{customerId}").serviceCall("account");
from("rest:get:account:/").serviceCall("account");
from("rest:post:account:/").serviceCall("account");

Conclusion

I was positively surprised by Camel. Before I started working on the sample described in this post I didn’t expect that Camel has such many features for building microservice solutions and working with them will be simple and fast. Of course, I can also find some disadvantages like inaccuracies or errors in documentation, only a short description of some new components in the developer guide, or no registration process in discovery server like Consul. In these areas, I see an advantage of the Spring Framework. But on the other hand, Camel has support for some useful tools like etcd, or Kubernetes which is not available in Spring. In conclusion, I’m looking forward to the further improvements in Camel components for building microservices.

The post Microservices with Apache Camel appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2017/03/03/microservices-with-apache-camel/feed/ 0 1367