Reputation: 15
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
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
Reputation: 22241
I'd use Java's generic Future
s and a Spring's @Async
methods in a @Service
.
In short, you call the services sequentially and get all results as Future
s, 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