Как правильно работать с ThreadPoolExecutor в Spring Boot Application - PullRequest
0 голосов
/ 20 марта 2019

У меня есть Spring Boot Application.

Мое приложение отправляет запросы другим приложениям, используя restTemplate.

Мне нужно отправить запрос в сотню разных приложений (на разных серверах). Я использую:

publi class Service {
    private RestClient restClient;
    private List<String> urls;
    private ThreadPoolExecutor executor;

    public Service(RestClient restClient, List<String> urls, ThreadPoolExecutor executor){
       this.restClient = restClient;
       this.urls = urls;
       this.executor = executor;
    }

    public void sendPost(Entity entity){
         for (String url: urls){
             executor.execute(() -> restClient.create(url, entity);
         }
    }

}

Я пытаюсь использовать ThreadPoolExecutor(fixedSizeThreadPool), но у меня есть несколько вопросов.

1 . Я прочитал, что threadPoolExecutor является потокобезопасным. Означает ли это, что я могу одновременно вызывать execute() из разных потоков, и он будет работать правильно?
2 . Если в * 1020 нет свободных потоков, это замедлит работу приложения, и я должен выбрать рациональный размер пула, верно?
3 . Например, мне нужно написать исполняемые URL в ArrayList:

public void sendPost(Entity entity){
         List<String> executedUrls = new ArrayList<>();  
         for (String url: urls){
             executor.execute(() -> restClient.create(url, entity, executedUrls);
         }
}

RestClient отправляет запрос и, если он успешно выполнен, будет добавлен в ArrayList.

Я ожидаю, что ArrayList будет иметь список успешно выполненных URL, если у меня возникнет исключение в каком-либо потоке из threadPool.
Будет ли это работать, как я ожидаю, или у меня может быть что-то вроде потерянное обновление ?

1 Ответ

1 голос
/ 21 марта 2019

Что вы можете сделать, это использовать ExecutorService.
Например, создайте новый ExecutorService (лучше кэшированный ThreadPoolExecutor)

private final ExecutorService executorService = Executors.newCachedThreadPool();

Создание пользовательской Runnable реализации

static class RestRunnable implements Runnable {
    private final String url;
    private final RestTemplate restTemplate;
    private final Collection<? super String> executedUrls;

    RestRunnable(
            final String url,
            final RestTemplate restTemplate,
            final Collection<? super String> executedUrls) {
        this.url = url;
        this.restTemplate = restTemplate;
        this.executedUrls = executedUrls;
    }

    @Override
    public void run() {
        final ResponseEntity<?> response = restTemplate.exchange(...);

        if (response.getStatusCode() == HttpStatus.OK) {
            executedUrls.add(url);
        }
    }
}

И для каждого URL отправьте новое задание на ExecutorService

final Collection<String> urls = new ArrayList<>();
final Collection<String> executedUrls = new CopyOnWriteArrayList<>();

...

for (final String url : urls) {
    // The RestTemplate instance is retrieved via Spring Bean, or created manually 
    executorService.submit(new RestRunnable(url, restTemplate, executedUrls));
}

RestRunnable вставит URL в executedUrls потокобезопасный CopyOnWriteArrayList, если он был успешно вызван.


Помните, что ExecutorService должен быть отключен, когда он больше не нужен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...