В нашем приложении Spring возникает ситуация, когда мы используем несколько облачных конечных точек REST для извлечения данных, которые объединяются в один объект данных (назовем его Employee.java
), который затем передается в бизнес-логику.
Я бы хотел инкапсулировать все эти вызовы в один метод служебного компонента, примерно так:
@Service
public class EmployeeService {
public Employee retrieveEmployee(String id) {
Employee e = new Employee();
callRESTToFillWithBasicData(e);
callRESTToFillWithExtendedData(e);
callRESTToFillWithAdditionalData(e);
return e;
}
}
Поскольку вызовы REST довольно трудоемки, мы хотим сделать этот метод асинхроннымТаким образом, приложение может делать другие вещи в то же время.Для дополнительной сложности методам в службе необходим доступ к бинам Spring в области запроса.Проблема в том, что Spring по умолчанию не передает контекст запроса потокам, созданным с помощью асинхронных методов.Поэтому мы написали пользовательскую реализацию ThreadPoolTaskExecutor, похожую на то, что предлагается здесь .Асинхронный код выглядит следующим образом:
@Service
public class EmployeeService {
@Async("requestAwareTaskExecutor")
public CompletableFuture<Employee> retrieveEmployee(String id) {
Employee e = new Employee();
callRESTToFillWithBasicData(e);
callRESTToFillWithExtendedData(e);
callRESTToFillWithAdditionalData(e);
return CompletableFuture.completedFuture(e);
}
}
Теперь, чтобы ускорить процесс, мы также хотим распараллелить 3 вызова REST в методе.Это оставляет нам некоторые проблемы, которые нужно решить:
Проблема в том, что мы не можем просто поместить @Async
в методы callRESTToFill...
, потому что вызовы методов из одного объекта в себяне маршрутизируется через Spring-прокси и, следовательно, не выполняется асинхронно.
Мы также не можем просто обернуть вызовы методов в CompletableFuture.runAsync(...)
, потому что тогда потоки, опять же, не смогутbean-объекты в пределах запроса.
В крайнем случае можно было бы добавить компонент Executor с запросом @Autowired
в компонент службы и передать его на все вызовы CompletableFuture
.
Но мы не чувствуем, что это путь весны.Непосредственное использование Executor кажется излишним, учитывая, что мы уже указали bean-компонент executor в аннотации @Async
.
Что было бы лучшим вариантом здесь?