Monday, April 11, 2016

Build A Concurrent Web Page With A Simple Example

Today, by using a simple example, I am going to demonstrate on how to build a concurrent web page in ADF using Jdeveloper.

From time to time, I see requirements from various contexts with such needs to build concurrencies on web pages. The performance can be greatly benefited since multiple processes are executed at the same time so that the response time is greatly improved. It's also very suitable for the use cases that the processes are not associated with any user interface interactions, such as sending an email to user after performing some action but the web page does not need to know if/when the email is sent. Such processes can be executed in a separate thread from the main thread dealing with the web page rendering and other user interactions. Even if you don't have hard requirement on such concurrency needs, why waste it? Nowadays, all servers are built with multi-core computation capabilities and designed for parallel processing. In years the speed of the CPU is not getting much faster but the number of the processing cores are getting more. If you are not taking advantage of it, it would be your waste.

Implementing such concurrency in Java EE application does not seem to be complicated, especially considering the fact that Java concurrency has been introduced since Java 1.5. But, such implementation seems to be somewhat formidable to the development team from my experience. The reason could be without careful handing and design of the Java concurrency, your expected performance gain could lead to an (much worse) opposite degradation. It could also be because there is a lack of examples to demonstrate such use case and that's definitely the purpose of this post to compensate it.

In this example, I have a page built in ADF with 3 sections - left, central and right section. Each section has their independent content and requires different period of time to process. Let's say the left will take 2 seconds, central will take 3 seconds and right will take 5 seconds to return the data.

I will start with the default approach (no concurrency) followed by two others on how to improve our web page response time.

The default

There is no concurrency here. Everything runs sequentially. My page looks like this:

Since everything runs in sequence, so the response time takes 2 (left) + 3 (central) + 5 (right) = 10 seconds minimal. It actually take 10.59 seconds - not a surprise.

If you are wondering what's the code behind the scene on the content rendering, here it is:

Now it's time to explore the better solution.

The Better

In this approach, I would use ExecutorService to mange the creation and termination of the Java threads to perform the parallel processing. Since there is 3 sections in the page that I need to run in parallel, I will create the thread pool with 3 threads. Then I would need to submit individual tasks to the executor service to perform. The task is basically the work unit that needs to be done for each section. Since our task needs return values, I am going to use Callable interface to implement the task. We can get a reference of the task submission which is the Future object. With the Future object, we can get the output of the task when it completes.

CentralContentTask codes -

As you can see, I only need to put the task code into the call() method that I need to implement which is defined by Callable interface.

In the managed bean to render the web page, here is the logic that used to manage threads by ExecutorService - task submission, termination, output retrievemcent, etc.

Please note: it is very important to shut down the ExecutorService after task submission. Otherwise the memory leak would happen. There is no need to wait until the task complete to shut down. You can also shut down the executor service immediately regardless of task complete status by using shutDownNow() but that's not we are going to pursuit.

After, we get the output of the task by get() on the Future object. This method will make the thread to wait until the task complete and return the output.

Let's run the page and see our response time -

It takes 5.37 seconds to load the page. As we are running 3 processes in parallel, the total time will be the one with the longest processing time, which is 5 seconds.

It's much better. But not enough? Can we push all 3 processes to the back without interfering the page rendering?

The Ultimate

The idea is to separate the main thread which is rendering the web page from the parallel processing the 3 sections. It's fairly simple and we actually have already done that in previous code. The only thing blocking the main thread is using the Future.get() to return the output of the task. We will need to avoid that in the main thread. So the question becomes how could we retrieve the output of the task  that takes a period of time and push back to the web page we already rendered?

The answer is the ADF poll component. ADF poll can be used to various use cases that require pushing data to web pages after rendered. Here is how to leverage it in our use case.

First, we add a <af:poll> on the web page with an interval 1 second and makes all 3 sections listen to this component.

Second, we construct the refresh() method defined in the poll component to retrieve the output of the tasks.

Here we use Future.isDone() to check if the task is complete or not before getting the output, so that the thread is not waiting if the task is not complete yet.

Another note is after all 3 processes complete, we will need to stop the poll event. ADF poll offers a property "timeout" to expire the poll after a period of time. But at this time, this feature doesn't work well in ADF 11g including 11.1.1.9. This is a bug already filed but very likely it will NOT be fixed in ADF 11g. To disable the poll, we will use a workaround here to reset the poll interval to "-1" (negative) which will automatically disable the poll.

Let's take a look how long the page takes to render -


Yes, 407ms!

Now all 3 processes are running behind the page and when they are ready, it's pushed to the page individually. If you watch the page, you will see the left content comes first, then the central content and finally the right content.

In most time, the user perception is everything. In our last approach, we are able to give the user the page rendering with no time and gradually add the content when they are ready. It is an ultimate scenario. In cases that the user doesn't want to see the empty content to start with, we can put the process of the desired content back to the main thread with the page rendering, so that the web page always shows the desired content at the user's first sight. In that case the total response time will be a few hundreds of milliseconds plus whatever the process of the desired content will take.

The sample example demoed in the post can be downloaded here. It's built in Jdeveloper 11.1.1.9.

No comments: