21 Reactive Camel

published book

This chapters covers

  • Using Reactive Streams
  • Camel Reactive Streams
  • Vert.x and Camel

If you have been reading this book in chronological order then it has been a very long journey. And you may be pleased to know that this chapter is our last from the hands of Claus and Jonathan and this time we will keep this chapter short. We have only two topics we wanted to bring to your attention here at the end.

Apache Camel is a well established project and has been around for over a decade. A decade in the IT industry is like half a life-time for humans. Its only recently that reactive design patterns have started to gain more interest, especially since Java 8 added support for java.util.stream in its streaming API. Further interest may be spurred with the upcoming Spring Framework 5 which includes a new reactive module.

In regards to Apache Camel then the current architecture of Camel 2.x is based on a hybrid routing engine that executes both blocking and non-blocking processing depending on which EIP patterns and components are being used. The upcoming Camel 3.x architecture is intended to be a dual engine comprised of the current hybrid engine, and then a new reactive engine based on a reactive event bus.

In the first half of this chapter we will take a brief look at the new camel-reactive-streams component that is introduced in Camel 2.19 which allows users to take advantage of the reactive streaming APIs and work with any of the 200+ Camel components. The second half of this chapter will be a personal story from Claus whom built a Vert.x application using Camel to simulate the old days with a football drinking game.

livebook features:
highlight, annotate, and bookmark
Select a piece of text and click the appropriate icon to annotate, bookmark, or highlight (you can also use keyboard shortcuts - h to highlight, b to bookmark, n to create a note).

You can automatically highlight by performing the text selection while keeping the alt/ key pressed.
highlights
join today to enjoy all our content. all the time.
 

21.1  Using Reactive Streams with Camel

In this section we will briefly cover the new camel-reactive-streams component which you can use to integrate Camel with the Reactive Streams API (http://www.reactive-streams.org). This API is small standard specification for asynchronous stream processing with non-blocking back pressure.

Reactive Streams API

The API is comprised of the following four interfaces:

  • Publisher - A Publisher is a provider of a potentially unbounded number of sequenced elements, publishing them according to the demand received from its Subscribers.
  • Subscriber - A Subscriber receive events from a Publisher.
  • Processor - A Processor represents a processing stage which is both a Subscriber and a Publisher and obeys the contracts of both.
  • Subscription - A Subscription represents a one-to-one lifecycle of a Subscriber subscribing to a Publisher.

Because the Reactive Streams is a specification then you would need to use a library which implement this specification. The two most popular implementation in Java are RX-Java and Reactor Core. In this book we will show you examples using those two libraries.

Another main objective of the Reactive Streams specification is to define a model for back pressure, a way to ensure a fast publisher does not overwhelm a slow subscriber. Back pressure provides resilience by ensuring that all participants in a stream-based system participate in flow control to ensure steady state of operation and graceful degradation.

21.1.1   Reactive flow control with back pressure

In an ideal situation a publisher is able to push data to a subscriber as fast as possible while the subscriber is able to keep up. This is not the case in the real world we live in, and its easy to imagine situations like where the subscriber cannot keep up and would become flooded with data. We could try to deal with such situation by having a buffer at the consumer side with a reasonable capacity to store new data while the consumer is busy processing. However this would often only mitigate small bumps on the road. If the situation continues and the producer keeps being faster than the consumer then the buffer would eventually run out of capacity.

Okay we could then chose a strategy to start dropping data if the buffer is full. For example we could chose to drop the oldest or the newest data. However this will result in data loss which often is not desirable.

What we need is a bi-directional flow of data. Data flows downstream from the publisher to the subscriber, and the subscriber sends a signal upstream to demand more data. Figure 21.1 illustrates this principle.

Figure 21.1 - The Publisher sends data to the Subscriber. And the Subscriber requests the Publisher for more data. Both events occur asynchronously.

When the publisher receives a demand from the subscriber, it’s free to publish new data up till the number of elements requested. The bi-directional flow between the publisher and the subscriber is asynchronous and guarantees the best possible flow control.

The back pressure does not terminate at the first publisher as it may cascade further up to upstream publishers. This follows the principle from the Reactive Manifesto:

Back-pressure may cascade all the way up to the user, at which point responsiveness may degrade, but this mechanism will ensure that the system is resilient under load, and will provide information that may allow the system itself to apply other resources to help distribute the load.

To better understand how the flow model between the Publisher and Subscriber works then lets look at figure 21.2.

Figure 21.2 - The Subscriber subscribes to a Publisher, which creates a Subscription. The Subscriber will be signaled by the onSubscribe callback when it has been successfully subscribed and the Producer is ready to send data. The Subscriber then requests how much data to receive. The Subscriber receives each data message per onNext callback up till the number of elements requested.

The most interesting aspect from figure 21.2 is how the Subscriber can requests more data using the request(limit) method. Then the Publisher sends back data to the Subscriber up till that limit. At any time the Subscriber can request more data by calling the request(limit) method again.

You should also know that a Subscriber should only subscribe to only one Publisher, whereas a Publisher can have one ore more Subscribers.

Okay enough talk lets get down to a bit of action.

21.1.2   First steps with Reactive Streams

To understand the gist of how you can build reactive flows we will start with a simple example where we want to take a set of words, apply a function and then log each word. To make the example as simple as possible we will use a fixed set of words (not a continued stream) and the reactive engine supports this by making it possible to create a Producer that can just do that. Listing 21.1 shows how this can be coded using RX-Java2 as the reactive engine.

Listing 21.1 - Reactive flow which takes the words which are upper cased and logged

The main entry to using the reactive streams with RX-Java2 is the io.reactivex.Flowable ❶   class where you can create a Publisher to stream just from an array. The subscriber is created from the io.reactivex.subscribers.DefaultSubscriber ❷   class that allows to define three callbacks (methods) to receive items and notifications from the Publisher. The onNext ❸   callback will be called once per every item of the stream. The onError ❹   callback will be called if an error occurs upstream. The onComplete ❺   callback is called when the stream completes normally (for bounded streams, like the one used in this example), to signal that no more items will be pushed downstream. In case of error, only the onError callback will be called, while the onComplete will be skipped. The DefaultSubscriber class takes care of handling the back pressure mechanism with the Publisher, so that users can concentrate on the flow logic.

In RX-Java2, subscribers should not be created explicitly using the DefaultSubscriber class. Instead, callback functions and transformations are usually attached to a stream using a flow DSL, as shown in listing 21.2.

Listing 21.2 - Reactive flow using flow DSL

The Publisher in listing 21.2 is created the same way as in listing 21.1 from Flowable ❶   to publish just the given words from the array. The Subscriber is created using Flowable ❷   where you specify the Publisher to be used. We then want to upper case each word, which is done by the map function where we can use Java 8 lambda style to call the toUpperCase method ❸  . The flow then continues where we want to run some code which can log the word as done in the doNoNext method ❹  . And finally we call the subscribe method ❺   and the flow gets going.

When defining reactive streams using this kind of flow style, it may be more common to setup the entire flow directly on one Flowable. The same code from listing 21.2 can be compacted to just 4 lines of code:

You can find this example with the source code in chapter21/rx-java2 which you can try using Maven:

You can also find an equivalent example using the Reactive Core engine instead in the chapter21/reactive-streams directory which you can try with:

The two examples are almost identical. When using Reactive Core you would use reactor.core.publisher.Flux instead of io.reactivex.Flowable.

This was a good first test, but lets move on to see how you can use Camel together with reactive streams.

21.1.3   Using Camel with Reactive Streams

The reactive-streams component is a Camel component that allows you to use Camel as a publisher or subscriber with your reactive streams. In this section we will add Camel to the previous example and then let Camel acts as a publisher to send in the words to the reactive streams. Listing 21.3 show how this can be done.

Listing 21.3 Using Camel as Publisher to send in words to the reactive flow

To bridge Camel with Reactive Streams you should get an instance of CamelReativeStreamsService ❶   which whose lifecycle is controlled by the Camel context. Camel can be used as Publisher from any of its 200+ components that can act as a consumer. At first this may sound confusing that a Reactive Publisher is using a Camel Consumer. The Publisher is the data sink where new data comes in, which is a consumer in EIP terms and hence a Camel Consumer.

In this example we want to receive data from the seda:words Camel endpoint ❸  . Notice how we specify the type to String which ensures the Flowable ❹   builder is able to use this as Java generic, so that the compiler can accept the lambda code which calls the toUpperCase method, because it knows its a String type. The last part of the example is to use Camel to send the words to the seda:words endpoint ❺   which then triggers the reactive streams.

You can find this example with the source code in chapter21/rx-java2 which you can try using Maven goal:

Using Camel route as Reactive Stream Publisher

When using Camel you very often use Camel routes so lets take a look at how we can let a Camel route be the data sink for a reactive flow. This time lets try numbers instead of words and use a Camel route that has a continued stream of data as show below:

The Camel route is trivial which starts from a timer that triggers once per second. We then generate a random number between 0 and 9 which is sent to the reactive-streams endpoint with the name numbers ❶  . This is a Camel endpoint from the camel-reactive-streams component which you can use as bridge between Camel and your reactive flows. Listing 21.4 shows the source code with the reactive flow.

Listing 21.4 Reactive flow with a Publisher as data sink from Camel route

To use Camel with reactive flows we need to get an instance of CamelReativeStreamsService ❶   which we use to create a Publisher from the stream with the name numbers of type Integer ❷  . Pay attention that the name numbers is the same name used in the Camel route where data is being sent by:

The reactive flow starts from the Publisher we created . This time we want to apply a filter which drops the low numbers so we only carry on big numbers . Each of these big numbers is then logged . And finally we start this flow by calling the subscribe method .

We have provided this example in the chapter21/rx-java2 directory which you can try with:

Using Camel route as Reactive Stream Subscriber

Now lets try the opposite, where we let a reactive flow publish streams and Camel act as the Consumer by receiving the data as input to a Camel route. The Camel route is merely two lines of code:

The reactive flow source code is shown in listing 21.5.

Listing 21.5 - Reactive flow with Camel as a Subscriber

Yes you have read it before, when you use Camel with Reactive Streams you need to get an instance of CamelReactiveStreamsService ❶  . To keep this example simple we just let RX-Java2 create a Publisher with just 5 numbers ❷  . Because the number are un-ordered we can apply a sort function ❸  . Then we create a Camel Subscriber with the name numbers ❹  . The name of the stream is the name of the endpoint used in the Camel route above, eg:

You can try this example from the chapter21/rx-java2 directory by executing the following Maven goal:

So far all the integration between Camel routes and reactive flows has been using the reactive-streams component. However regular Camel endpoints can also be used.

Using regular Camel components in Reactive flow

The last example we want to show you is using regular Camel endpoints in the reactive flow. For example using the Camel file component as sink for a reactive flow and a Camel route as the subscriber. Listing 21.6 shows the source code:

Listing 21.6 - Reactive flow with regular Camel endpoints and routes

This time the reactive flow seems more complicated the first couple of times you read it. We start by using reactive Camel to create a Publisher from a regular Camel file endpoint ❶  . From the reactive flow we can make calls into Camel routes from within the doOnNext function ❷   where we can leverage Reactive Camel to publish to the Camel route ❺  . Then we use the filter function ❸   to only include files which contain the text Camel. Finally we let Camel be the Subscriber ❹   by routing the data to the specified Camel route ❻  .

You can find this example in the chapter21/rx-java2 directory and try by running:

Tip 

In section 21.1.1 we mentioned that one of the main goals of reactive streams specification is to define a model for back pressure. In the following two sections we will show you how you can configure and control back pressure from a Camel reactive producer and consumer point of view.

21.1.4   Controlling back pressure from producer side

When routing messages using Camel to a external subscriber then back pressure is by default handled by an internal buffer that caches the Camel messages before delivering them to the reactive subscriber. If the subscriber is slower than than the rate of messages, the internal buffer may fill up and become too big. Such situation must be avoided.

For example suppose we have the following Camel route:

If the JMS queue contains a lot of messages and the reactive subscriber is too slow to process the messages, then pending messages will keep piling up in an internal buffer by reactive streams. This can potential degrade the performance in the JVM by taking up memory, or in worst case cause an out of memory error in the JVM and cause the application to crash.

We have provided an example, shown in listing 21.6, with the source code demonstrating a situation where the subscriber is too slow and pending messages are pilling up in the internal reactive buffer.

Listing 21.6 - Reactive flow without back pressure causing messages to fill up buffer

This example has been constructed so us humans can follow what happens while it runs, and therefore the reactive flow takes 1 second ❶   to process each message. Instead of sending in hundred of thousand of messages we use a low limit of 200. This is enough to just prove the point that those messages will stack up at the reactive buffer ❹  . The Camel route also has a little delay ❸   to allow humans better to follow from the console output what is happening.

You can run the example by executing the following Maven goal:

While the example runs it outputs to the console what is happening as shown below:

The interesting part of the output has been highlighted in bold. Here we can see that Camel has routed all 200 (0..199) messages  to the reactive stream channel. And at this time the reactive flow is processing message number 21, which means there is 200 - 21 - 1 = 178 messages pending in the reactive buffer. Now suppose we send 2000 messages instead of 200 what would the situation be?

This time there are 2000 - 205 - 1 = 1794 messages pending in the buffer. As expected the subscriber cannot keep pace with the publisher and we could potentially cause the JVM to become unstable with out of memory errors or degrade in performance.

Tip 

Runtime statistics about the Camel reactive streams is available from JMX. Under the services folder you can find the DefaultCamelReactiveStreamsService MBean which has JMX operations that returns the statistics in tabular format.

Adding back pressure

To avoid such problems we can use back pressure to prevent dequeueing too many messages from the JMS queue and instead try to keep a pace that are more aligned with the subscriber.

The strategy for back pressure we can use in this example is to use Camel's ThrottlingInflightRoutePolicy in the Camel route as shown in listing 21.7:

Listing 21.7 - Reactive flow with back pressure using Camel route policy

To make the reactive flow and the Camel route listed in listing 21.7 to flow with similar pace we can use Camel's route policy to suspend/resume the route to keep a maximum number of inflight messages. Therefore we create the  ThrottlingInflightRoutePolicy ❶   which is configured to limit at most 20 inflight messages ❷   and resume again at 25% of the maximum (= 5 messages). In other words the rate will be between 5 and 20 inflight messages. To use the policy we must remember to configure it on the route ❸  .

Tip 

You can find more information about Camel route policy in chapter 15, section 15.2.

You can run this example by executing the following Maven command:

The output below is captured at similar moment when we ran the example without back pressure.

This time we can see that when the 200 messages has been routed by Camel then the reactive flow is currently processing message 184. That means there is only 16 (200 - 184 - 1 = 15) pending messages in the reactive buffer. This is because of the back pressure in use.

The following two outputs represents when the back pressure is in use by first suspending the route and a little while later resuming the route.

And when we run the example with 2000 messages we can see at the end of the test that the back pressure works as there are only 19 pending messages in the reactive buffer.

If a certain amount of data loss is acceptable then you can configure Camel to use a different strategy than buffering every message.

Using alternative back pressure strategies

Table 12.1 lists the back pressure strategies supported by Camel reactive streams.

Strategy

Description

BUFFER

Buffers all messages in an unbounded buffer. This is the default strategy.

LATEST

Keeps only the latest message and drops all the others.

OLDEST

Keeps only the oldest message and drops all the others.

So far all the examples we have run with reactive streams has been using the default back pressure strategy which is the first item in table 12.1. This strategy buffers the messages which ensures that there is no data loss. However we learned about the potential danger if the publisher produces messages faster than the downstream subscribers can process, which can cause the JVM to consume more and more memory, degrade in performance or eventually run out of memory. Camel supports alternative strategies if message loss can be accepted by discarding messages from the buffer. When using the LATEST or OLDEST strategy the discarded messages will be removed from the buffer and an ReactiveStreamsDiscardedException exception is thrown for each message. By throwing the exception you can use Camel to react using its error handling to route the message to a dead letter channel, or to log the message, or silently ignore it.

Let’s see an example where we only want to process the latest message and silently ignore the discarded messages. You can run this example by:

The interesting output from running the example is highlighted below:

As you can see the reactive flow is processing the latest message that was sent to the buffer. All the other messages were discarded. To avoid causing every discarded message to fail and have its stacktrace logged we can use Camel's error handler to handle the ReactiveStreamsDiscardedException exception by adding the following line to the Camel route:

You can also use back pressure from the other side, the consumer side.

21.1.5   Controlling back pressure from consumer side

When Camel consume messages from a reactive publisher it has back pressure enabled out of the box. The consumer allows by default at most 128 inflight messages which is used to determine how many messages to request from the publisher when requesting for more data. In other words Camel will at most request up till 128 messages from the publisher.

You can configure the maximum number of inflight messages as an option on the endpoint such as:

Listing 21.8 shows the source code of an example using back pressure on the consumer side.

Listing 21.8 - Camel reactive consumer with back pressure

The example uses a reactive flow that publishes 100 messages ❶  . To be able to see when Camel requests more data, we use doOnRequest to log how many messages was requested ❷  . The flow is then using Camel as the subscriber ❸   on the channel named inbox. You can then see how we can create a Camel route that consumes from this channel ❹  . The consumer has been configured with a maximum of 5 inflight messages. To speedup processing we have turned on concurrent consumers with a value of also 5. This makes Camel create a thread pool of 5 threads that each act as a reactive subscriber and will independently process the messages from the reactive channel. However they will collectively act together under the limitations of the maximum inflight messages. Because those values are the same then each consumer will not exceed the maximum number of inflight message.

The example is provided with the source code which runs using the following command:

When running the example you would notice the following from the output:

As you can see the output shows that Camel will request the maximum number of inflight messages at first, and then as it runs the request drops down to 3 or 1 messages. In fact very often only 1 message is requested. The reason is that each consumer thread will refill the buffer when they have processed the message. And because the thread has just completed its own message, then the buffer often only has room for one more message.

Tip 

Camel will allow to use watermarks in a future release so request for more data can be deferred if the buffer still has at least N > watermark number of messages.

That is all we could squeeze into this book about Camel, reactive streams and the Camel reactive component. This is fairly new additions to the Apache Camel project which is expected to take up by more Camel users when reactive systems starts to become more in use. We will come back to some more thoughts on this in the chapter summary.

At this point we move on to the second half of this chapter which is devoted to Vert.X.

livebook features:
discuss
Ask a question, share an example, or respond to another reader. Start a thread by selecting any piece of text and clicking the discussion icon.
discussions
Get Camel in Action
add to cart

21.2  Using Vert.x with Camel

On the Eclipse Vert.x website the project describes itself as Vert.x is a toolkit for building reactive applications on the JVM. There are three important points in this description.

As a toolkit Vert.x is not an application server, or a framework. Vert.x is just a JAR file (vertx-core), so a Vert.x application is an application that uses this jar file.

Secondly Vert.x is reactive and adheres to the reactive manifesto (http://reactivemanifesto.org/) which is highlighted in the following four bullets:

  • Responsive - A reactive system needs to handle requests in a reasonable time.
  • Resilient - A reactive system must stay responsive in the face of failures. So it must be designed with failure in mind.
  • Elastic - A reactive system must stay responsive under various loads. Therefore it must be scalable.
  • Message Driven - Reactive systems rely on asynchronous message passing between its components. This establishes a boundary between components that ensures loose coupling, isolation and location transparency.

Finally Vert.x applications runs on the JVM which means Vert.x applications can be developed using any the JVM languages such as Java, Groovy, Scala, Kotlin, Celyon, JavaScript etc. The polyglot nature of Vert.x allows you to use the most appropriate language for the task.

Tip 

If you are new to Vert.x then we recommend the free book Building Reactive Microservices in Java by Clement Escoffier which you can download from: https://developers.redhat.com/promotions/building-reactive-microservices-in-java

The scope of this book is to cover all about Apache Camel. However we want to give room for Vert.x in this book as we think its an awesome toolkit that has great potential together with Camel.

21.2.1   Building a dong simulator using Vert.x

What follows next is a true event that happened in the life of Claus Ibsen (the author). I am hosting a christmas party with seven of my old friends in the second weekend of December 2017.

When we were younger we would have these football weekends, where we would watch english football. It was back in the late 90's and up to mid 00's. During a football weekend the state lottery company would issue a football pools coupon with 13 games. One of these games was the TV game and out of the remainder 12 games each of us would select a game. We would then watch the TV game and the other games was played at the same time. Whenever a goal was scored it would be announced in the TV with a dong sound - and hence why it was known as dong bold (bold = football). The rules were simple. If a goal was scored in the TV game everyone would drink. If a goal was scored in your game, you would drink (bottoms up).

Fast forward to today. Now the football games are much more scattered during a game week and its hard to find a number of games play at the exact same time. So I wanted to build a goal simulator and use a good old classic game from the 90's or 00's. However it was harder to find a full length football game on the internet going back so many years. However I managed to find a great local derby between Manchester United and City from February 2004. As a bonus the sides have players in the lineup which we remember such as Ryan Giggs, Rio Ferdinand, Gary Neville, Paul Scholes, Christiano Ronaldo, Ole Gunnar Solskjaer, and Ruud van Nistelrooy. Today I can hardly remember anyone from the Manchester United side besides Zlatan Ibrahimovic.

I then researched and found which other games were playing on that same day and build my own football pools coupon with nine games; one TV game and a game for each of the eight of us. I then played the game in its entirety with the goal simulator running which then would flash and play the dong sound for each goal. This was then recorded which allowed me to easily playback the video clip during the party. Figure 21.3 shows a screenshot of the game in action with the goal simulator on the right hand side.

Figure 21.3 - Dong simulator playing the TV game with live goal scorer updates on the right hand side.

We had a great weekend and playing dong again was a blast. Manchester United won 4-2 with goals from Scholes, 2 x Nistelrooy and Ronaldo.

That was the fun part, lets talk about how to implement this using Vert.x.

The Architecture

Figure 21.4 illustrates the key components in the architecture.

Figure 21.4 - In the backend the Vert.x applications loads the games and goals from disk. The frontend is a HTML web application with embedded JavaScript which uses websocket to communicate with the Vert.x backend.

The backend is implemented as a Vert.x application using Java. Information about the games and the goal scorers are stored in plain CSV text files which is loaded into the Vert.x application upon startup. The frontend is a plain HTML file with embedded JavaScript. The JavaScript uses a Vert.x JavaScript client which handles all communication to the backend using websocket. The entire application is packed together as a single fat jar, which can run as a Java application on the JVM.

The source code of the book contains this example in the chapter21/vertx directory. You can try this by running the following Maven goal:

... and then open a web browser on http://localhost:8080

The application is built using Java and HTML code which the following intriguing parts.

The Java code

You develop your Vert.x application in Java code in what is called a verticle which are deployed and running in the Vert.x instance. The verticle is just an abstract class which has start and stop methods and easy access to the Vert.x instance itself.

Listing 21.9 shows the verticle for the dong simulator.

Listing 21.9 - Vert.x verticle for the dong simulator

The dong simulator code from listing 21.9 shows how to build a Vert.x application as a verticle. The LiveScoreVertice class extends io.vertx.core.AbstractVerticle ❶  . In the start method we have the necessary code to startup, such as creating a Vert.x router for HTTP and websocket ❷  . Then we setup allowed inbound and outbound communication ❸   to the router with the following four eventbus addresses: control, clock, games and goals. The communication between the backend and frontend is using websocket which we add to the router ❹  . Whenever a new client is connected the SOCKET_CREATED event is emitted to the backend which triggers initialization of the game list ❺  . The HTTP router is also used to service static content such as HTML files ❻   and is started by listening to port 8080 ❼  . And lastly the game controls and the goal score stream is started ❽  .

The LiveScoreVerticle class has more code to initialize the game list, react upon game control buttons pushed, game clock advances and the actual stream of goals. For example the list of games is initialized as shown in listing 21.10.

Listing 21.10 - Initializing the list of games

The list of games is stored in a CSV file which is loaded ❶  . Each line in the CSV file is a game which gets published to the Vert.x eventbus at the games address ❷  . In addition the game clock state is published as well ❸  .

As you can see from listing 21.10 its very easy to send messages to the Vert.x eventbus using the one liner code with the publish method ❷  . This is similar to Camel's ProducerTemplate which also makes it easy in one line of code to send a message to any Camel endpoint. But what if you want to consume a message instead, how can you do that from Vert.x?

The dong simulator has buttons in the front end which controls the game clock, so the user can start and stop the clock. Each time the user clicks those buttons, a message is sent from the frontend to the backend using websocket on the Vert.x eventbus. The following code is all it takes in the backend to setup the consumer:

From the Vert.x eventbus we setup a local consumer to listen on address control ❶   and upon each message received it triggers the handler (using java 8 lambda style). A Vert.x message also consists of a message body and headers, just like a Camel message. The message body contains what button the user clicked ❷   and we react accordingly to either start or stop the game clock ❸  . We then publish the new state back so the web page can react and update its display ❹  .

This was the key points from the Java code, lets switch over to the wild west of front end programming with web frameworks and JavaScript. Oh well its actually fairly simple, certainly with the Vert.x JavaScript client.

The HTML code

Vert.x allows to embed web resources such as HTML and JavaScript files in the src/main/resources/webroot folder. We have the following files in this folder:

The dong.m4r file is the dong audio which is played when a goal is scored. The index.html file is the HTML file which we will dive into in a moment. And the vertx-eventbus.js file is the Vert.x JavaScript client.

The index.html file is a plain HTML file with embedded CSS styles and JavaScript. In the <head> section you setup Vert.x as follows:

In this example we are using the popular JQuery JavaScript library ❶  . For websocket communication Vert.x uses SockJS ❷   which provides fallbacks to a simulated websocket communication if the web browser does not support native websocket. The last script is to include Vert.x itself ❸  .

In the <script> section we setup the frontend to connect to the Vert.x eventbus as shown in listing 21.11.

Listing 21.11 - Using Vert.x in the frontend to handle events from the eventbus

To use Vert.x from JavaScript you need to create a new eventbus with the URL to the backend ❶  . Then the onopen method allows you to register handlers which react when messages are sent to eventbus addresses. In this example there are three addresses the frontend uses. When the game clock is updated ❷  . When the list of games is initialized ❸   and of course when a goal is scored ❹  .

For example when the game clock is updated ❷   then the frontend updates the HTML page by setting the innerHTML to the message body. The game clock is a HTML <div> element as shown below:

The source code in listing 21.11 has been abbreviated to not show the JavaScript code that manipulates the HTML elements to update the website. This is regular JavaScript and HTML code which the authors of this book are not ninja master, and therefore don't want to show our embarrassing skills in print. You are of course very much welcome to take at look at the source code, and any CSS and HTML ninjas are welcome to teach us how this can be styled and done better.

The source code is located in chapter21/vertx directory. You can try running the example using the following Maven goal:

.. and then from a web browser open http://localhost:8080.

The example has been speeded up to run faster, so you don't have to wait full 90 minutes for the football game to end.

If you have been sitting back and relaxed for a while as the goals pouring in, you may have been enlightened and noticed the goal simulator is not using Camel at all. Vert.x is surely a very awesome toolkit for building small reactive microservices. But this book is titled Camel in Action so lets update the simulator to use Camel together with Vert.x.

21.2.2   Using Camel together with Vert.x

Vert.x and Camel are both small and light-weigh toolkits that work very well together. Figure 21.5 illustrates this principle with Vert.x and Camel working together in the same Vert.x application.

Figure 21.5 - Camel and Vert.x working together in the same Vert.x application in the backend. The other parts of the architecture are unchanged.

To use Camel with Vert.x you need to add camel-core and camel-vertx dependencies to the Maven pom.xml file. In the Vert.x application you add Camel to the verticle class as shown in listing 21.12.

Listing 21.12 - Adding Camel to a Vert.x application

The code to add Camel should be familiar to you. At first a CamelContext is created ❷   then routes is added ❷  . Then we create a ProducerTemplate ❸   which will be used by Vert.x to trigger a Camel route. Camel is then started ❹   and the following code is setting up Vert.x router ❺   and what else which is similar to the previous example from listing 21.9. When the Vert.x application is stopping then we must remember to stop Camel ❻   as well.

Calling Camel from Vert.x

When a new client connects to the backend Vert.x will trigger the SOCKET_CREATED event which we use to obtain the list of games and send to the frontend so the website can be updated accordingly. In the previous example we used Java code to load the game list from a CSV file and send the information using Vert.x. This time we are using Camel, so the SOCKET_CREATED event is using the ProducerTemplate to trigger a Camel route by sending an empty message to the direct:init-game endpoint ❶   as shown:

As you can see calling Camel from Vert.x is very simple, you just use regular Camel APIs such as a ProducerTemplate. But what about the other way around, how do you make Camel call Vert.x?

Calling Vert.x from Camel

Camel provides the camel-vertx component which is used to route messages to/from the Vert.x eventbus and Camel. Listing 21.13 shows how this is done.

Listing 21.13 - Using Camel routes to stream live goal scores to Vert.x eventbus

The LiveScoreRouteBuilder class is injected with the Vert.x instance ❶   in the constructor because we need to configure the Camel vertx component to use this instance ❷  .

Then follows three Camel routes. The first route is used to initialize the list of games ❸  . The route calls the goal component ❹   which is responsible for loading the list from the file system. The frontend except one message per game, and hence we need to split the game list before sending to the vertx eventbus on the games address ❺  .

Note 

To hide the complexity of loading the game list and streaming live goal scores we have built a Camel component named goal.

The second route is responsible for streaming game clock and goal score updates ❻   which is routed using a Content Based Router EIP to either the clock ❼   or goals ❽   address on the Vert.x eventbus. Pay attention to the fact that the route has been configured to not auto start. The reason is that we want the user to click the start button in the frontend, which is controlled by the last route ❾  . The controlbus component is capable of starting and stopping routes. Notice how we refer to the livescore route using the routeId parameter on the controlbus endpoint.

Tip 

Chapter 16 covers much more about the controlbus component with the topic about managing and monitoring Camel

The action parameter tells Camel what to do such as start, stop, suspend, or resume the route. This action is triggered from the frontend using the following JavaScript functions:

The rest of the code for this example is the goal component which hides the logic to read the CSV files and stream game clock and goal updates.

We encourage you to take a moment to look at this example and pay attention to how Vert.x and Camel are loosely coupled and clearly separated.

The only touch point between them are exchanging messages using the Vert.x eventbus using the camel-vertx component. Figure 21.6 illustrates this principle.

Figure 21.6 - Camel and Vert.x exchange messages using the Vert.x eventbus. Camel facilitates this using the camel-vertx component.

Trying the example

You can find the source code in the chapter21/vertx-camel directory which you can try using the following Maven goal:

... and then from a web browser open http://localhost:8080.

Okay lets end the drinking game and conclude our Vert.x coverage in this book with some final words.

21.2.3   Summary of using Camel with Vert.x for microservices

Building the dong football simulator has been a fun ride with using Vert.x, and then later adding Camel to the mix. This kind of application runs really well with Vert.x which has great support for HTML and JavaScript clients. Notice how easy it is to exchange data between the Java backend and the HTML frontend using the Vert.x eventbus. The web frontend is a modern HTML5 single page application which reacts to live stream of events. This is where Vert.x shines really well.

So how does this compare to Camel then? Well Vert.x is focused on reactive applications, and Camel is focused on messaging and integration. It's the combination of the two that gives synergy (2 + 2 = 5).

There is a lot of great to say about Vert.x and we recommend you take a look at the project and keep an eye on in for the future. Vert.x is reactive, asynchronous and non-blocking it puts the burden on the developer to understand its APIs really well. This takes time to master and grasp. Camel on the other hand hides a lot of that complexity and as a developer you can get very far with Camel routes and configuring Camel components and endpoints. Therefore we recommend you study the Vert.x APIs and programming model if you take up using Vert.x. A good place to start is with the Building Reactive Microservices in Java book by Clement Escoffier which you can freely download from https://developers.redhat.com/promotions/building-reactive-microservices-in-java.

livebook features:
settings
Update your profile, view your dashboard, tweak the text size, or turn on dark mode.
settings
Sign in for more free preview time

21.3  Summary and best practices

We are ending this book on a high note with coverage of a complex but interesting topic of reactive systems. Despite reactive principles and frameworks has been around for many years its only recently they have gained momentum and attention from developers (we are not talking about the ninja developers whom jump from hipster to hipster technology).

The addition of lambdas and streaming API in Java 8 has also helped Java developers get exposed and more familiar with the streaming style of programming. Another popular framework that would push in this direction is Spring Framework which is expected to include more reactive APIs in the upcoming version 5.

As you have seen with this chapter Apache Camel is also taking its strides into the reactive world with the reactive streams component. The Camel team has designs for the Camel 3.x architecture to offer two routing engines and APIs:

  • Classic Routing Engine - The classic routing engine as today.
  • Reactive Routing Engine - A reactive engine based on reactive streams API and pluggable runtime reactive library such as RX-Java, Vert.X, or Reactive Spring.

By having both routing engines side by side, allows Camel users to pick and chose what suites the best in their situations. This also allows amble time for the new reactive engine to be developed and matured over the years with valuable feedback and influence from the community.

We do want to say that reactive streaming APIs and reactive flows can be difficult to learn and understand. If you decide to use this for serious work then make sure to take extra time to learn and experiment.

Being the last chapter you should not be robbed from a bullet list the highlights:

  • Reactive Systems is complex - Learning, using and developing reactive applications is more complex and harder then regular applications. Especially the RX-Java library has a lot of greatness but is also more complex to get working and understand when you get past the beginner stage. If you get more serious then try to find a good online material or a book. We highly recommend the book Reactive Programming with RxJava by Tomasz Nurkiewicz and Ben Christensen published by O'Reilly.
  • Vert.X has potential - Keep an eye on the Vert.X project as its good and has potential. It is a small and lightweight tooling to build reactive applications. There is a camel-vertx component so you can easily use any of the 200+ Camel components in your Vert.X applications. TODO: camel-vertx vs vertx-camel-bridge
  • New Reactive Routing Engine on the way - The Camel 3.x architecture is planned to come with a new reactive routing engine and API which allows Camel users to transition their Camel applications gradually and also pick and chose between the existing classic engine and the new. As anything new it would take time, maybe years to mature and become rock solid.
  • Reactive, Reactive, Reactive - When Docker came out, it was Docker, Docker, Docker. You may hear more and more about reactive systems, streams and programming. It's not a game changer where you must drop everything and re-write your applications to these new systems, frameworks, and toolkits.

Okay we admit it, we got a little bit carried away in our thoughts and what we have said in this summary. This summary is biased and based on our experience in the software industry, so take this advice under consideration.

Goodbye from Claus and Jonathan

With that we've come to the end of our not so little book on Apache Camel. In this second edition we really tried to cover as much of Camel's core features as possible with the addition of how to use Camel in more modern settings, such as a framework for microservices, deployed in the cloud with Docker and Kubernetes, or even as a reactive engine as we discussed in this chapter. Believe it or not, there is still a lot more material! For that though, we defer you to the Camel website as that has a full reference of all 200+ components. Besides, reading (and writing) a book about all 200 components wouldn't be the most exciting thing in the world.

One final note about Camel is that it is a very active project and changes quickly. We suggest keeping up with Camel via it's community, which we discuss in appendix B, and keep up on new features. Maybe you'll even find the inspiration to contribute to Apache Camel yourself!

We hope you've enjoyed the Camel in Action II ride!

Cheers,

Claus and Jonathan

PS: If you like Apache Camel then we appreciate if you would give Camel a star on GitHub (https://github.com/apache/camel/)

sitemap

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage