Zastanawiam się, jakie są obecnie najlepsze sposoby na odpalenie kilku zapytań http (używam rest templatki) równolegle ? Załóżmy, że muszę pobrać dane z kilku różnych serwisów. Wiem, że request w springu tworzy osobny wątek i są jakieś problemy z tym, że wyjście poza niego powoduje utratę danych i jest to spowodowane działaniem ThreadLocal. Czy mogę użyć jakiś ExecutorServiców lub zrobić metody z @Async zwracające Future<T> czy jakie są teraz dobre praktyki w normalnych springu ?
A czy te operacje są tak długotrwałe że musze być zrównoleglone?
Niekoniecznie, ale dlaczego tego nie odpalić równolegle ?
Ja uważam że jeśli nie ma takiej potrzeby, tzn operacje są bardzo krótkie to nie warto, po prostu więcej męczenia się z synchronizacją, a raczej to że poszczególne taski się skończą. Jeżeli całość w kolejności jedno po drugim trwa bardzo krótko to ja bym to tak zostawił, nie ma sensu wyciągac armaty na muchę :)
Tu masz przykłady https://github.com/setblackWs/springFibb/blob/master/src/main/java/pl/setblack/fb/Example.java
Generalnie możesz nawet w klasycznym springu odpalać asynchroniczne requesty.
Albo chamsko zablokujesz jak juzprzyjdą odpowiedzi od wszystkich:
if (n < 2) {
return 1;
} else {
CompletionStage<Long> fib_n_1 = requestFibb(n - 1);
CompletionStage<Long> fib_n_2 = requestFibb(n - 2);
try {
return fib_n_1.thenCombine(fib_n_2, (n_1, n_2) -> n_1 + n_2).toCompletableFuture().get();
} catch (Exception ie) {
throw new IllegalStateException(ie);
}
}
//....
private CompletionStage<Long> requestFibb(long n) {
AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient();
CompletableFuture<Response> f = asyncHttpClient.prepareGet("http://localhost:8080/fibb?n=" + n).execute()
.toCompletableFuture();
return f.thenApply(resp -> {
try {
asyncHttpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
return Long.parseLong(resp.getResponseBody());
});
}
albo nawet zwrócisz Promise
z serwisu
@RequestMapping("/fibba")
public DeferredResult<Long> executeSlowTask(@RequestParam(value = "n", defaultValue = "1") long n) {
DeferredResult<Long> deferredResult = new DeferredResult<>();
//System.out.println("getting fibba(" + n + ") THREADS: " + java.lang.Thread.activeCount());
if (n < 2) {
deferredResult.setResult(1L);
} else {
CompletionStage<Long> fib_n_1 = requestFibba(n - 1);
CompletionStage<Long> fib_n_2 = requestFibba(n - 2);
fib_n_1.thenCombine(fib_n_2, (n_1, n_2) -> n_1 + n_2).thenAccept((res)->deferredResult.setResult(res));
}
return deferredResult;
}
//....
private CompletionStage<Long> requestFibba(long n) {
CompletableFuture<Response> f = asyncHttpClient.prepareGet("http://localhost:8080/fibba?n=" + n).execute()
.toCompletableFuture();
return f.thenApply(resp -> {
return Long.parseLong(resp.getResponseBody());
});
}
(btw. to są przykłady na problemy w klasycznym springu -rekursywny http. kod nie jest sensowny, ani optymalny, ale ogarniesz co trzeba.
W nowym spring WebFlux jest o klasę ładniej. Ale tam to nawet nie mam request per thread, nie ma beanów itp. więc to inna bajka.
@jarekr000000: A czy gdyby problem był taki że jest bardzo dużo użytkowników naraz i apka zamula, to możnaby się pokusić o bardziej hardwerowe rozwiązanie - więcej serwerów i load balancer?