Yann Berthaux
Yann Berthaux

Reputation: 15

How to synchronize parallel processes into a web service?

I need to develop a web service operation with CXF 3 hosted by Tomcat 7. Our model layer is Spring 3. This operation calls 16 other web services hosted by distant servers. We need to wait all responses in order to construct the response of our own operation. We currently call each distant operations sequentially. Of course, we have response time issue. I think we should parallelize our operation inner calls and synchronize the different responses.

What kind of multithreading implementation can be safe? What can we do to make it better?

Upvotes: 0

Views: 425

Answers (2)

Thiamath
Thiamath

Reputation: 71

I would use a traditional approach using join() to wait for the threads to finish instead of polling (I don't like polling pattern too much).

Kind of this for a generic thread to replicate:

public class ThreadedWebServiceRetrieval extends Thread {

    private List<ResultObject> resultList;
    private GenericWebServiceStub stub;

    public ThreadedWebServiceRetrieval (List<ResultObject> resultList, GenericWebServiceStub stub) {
        this.resultList = resultList;
        this.stub = stub;
    }

    public void run() {
        resultList.add(stub.retrieveData());
    }
}

And this for the parallel retrieval code:

// ... Controller/Service stuff

List<ResultObject> resultList = new LinkedList<>();//Diamond operator
List<Thread> webServiceList = new LinkedList<>();

webServiceList.add(new ThreadedWebServiceRetrieval(resultList, stub1));
//...
webServiceList.add(new ThreadedWebServiceRetrieval(resultList, stubN));

for (Thread thread : webServiceList) {
    thread.start();
}
for (Thread thread : webServiceList) {
    thread.join();
}

// resultList is fulfilled

Time of this approach should be +/- longest retrieval. I made the code VERY generic (overall in the Thread implementation) but it's intentional to fit most cases.

Enjoy!

Upvotes: 0

Dariusz
Dariusz

Reputation: 22241

I'd use Java's generic Futures and a Spring's @Async methods in a @Service.

In short, you call the services sequentially and get all results as Futures, and then you simply check whether all the futures have finished proccessing. You can also do some work with partial data if there is such possibility.

Here's a simple example on how to do it. A sample service from the link:

@Service
public class GitHubLookupService {

    RestTemplate restTemplate = new RestTemplate();

    @Async
    public Future<User> findUser(String user) throws InterruptedException {
        System.out.println("Looking up " + user);
        User results = restTemplate.getForObject("https://api.github.com/users/" + user, User.class);
        // Artificial delay of 1s for demonstration purposes
        Thread.sleep(1000L);
        return new AsyncResult<User>(results);
    }

}

And a method using it:

@Override
public void run(String... args) throws Exception {
    // Start the clock
    long start = System.currentTimeMillis();

    // Kick of multiple, asynchronous lookups
    Future<User> page1 = gitHubLookupService.findUser("PivotalSoftware");
    Future<User> page2 = gitHubLookupService.findUser("CloudFoundry");
    Future<User> page3 = gitHubLookupService.findUser("Spring-Projects");

    // Wait until they are all done
    while (!(page1.isDone() && page2.isDone() && page3.isDone())) {
        Thread.sleep(10); //10-millisecond pause between each check
    }

    // Print results, including elapsed time
    System.out.println("Elapsed time: " + (System.currentTimeMillis() - start));
    System.out.println(page1.get());
    System.out.println(page2.get());
    System.out.println(page3.get());
}

Upvotes: 1

Related Questions