Предисловие
Ранее я поделился статьей об использовании @Async для реализации асинхронных задач и управления пулом потоков в Spring Boot: «Spring Boot использует @Async для реализации асинхронных вызовов: пользовательский пул потоков». С тех пор, как я недавно обнаружил много проблем, вызванных неправильной обработкой асинхронных задач, я буду продолжать говорить об элегантном закрытии бассейнов ниток, в основном для бассейнов потоков Threadpooltaskscheduler.
Проблема феномен
В примере главы 4-1-3 предыдущей статьи мы определили пул потоков, а затем написали 3 задачи, используя аннотацию @Async, и указали пул потоков, используемый этими задачами для выполнения. В приведенном выше модульном тесте мы не говорили о проблемах, связанных с закрытием. Давайте смоделируем проблему и придумаем ее на месте.
Шаг 1: Как и прежде, мы определяем пул ниток Threadpooltaskscheduler:
@SpringBootApplicationPublic Class Application {public static void main (String [] args) {SpringApplication.Run (Application.Class, Args); } @Enableasync @configuration class taskpoolconfig {@bean ("taskexecutor") public executor taskexecutor () {threadpooltaskscheduler executor = new Threatpooltaskscheduler (); Исполнитель.setPoolsize (20); Исполнитель.setThreadNamePrefix ("taskexeCutor-"); вернуть исполнитель; }}}Шаг 2: реформировать предыдущую асинхронную задачу и заставить ее полагаться на внешний ресурс, такой как Redis
@Slf4j @componentpublic class task {@autowired private stringredistemplate stringredistemplate; @Async ("taskexecutor") public void dotaskone () бросает исключение {log.info ("start anding One"); long start = System.currentTimeMillis (); log.info (stringredistemplate.randomkey ()); Long End = System.CurrentTimeMillis (); log.info («Полная задача первая, время:« + (end - start) + "миллисекунд"); } @Async ("taskexecutor") public void dotasktwo () бросает exception {log.info ("start task 2"); long start = System.currentTimeMillis (); log.info (stringredistemplate.randomkey ()); Long End = System.CurrentTimeMillis (); log.info («Полная задача 2, время взято:« + (end - start) + "миллисекунд"); } @Async ("taskexecutor") public void dotaskThree () бросает exception {log.info ("start task three"); long start = System.currentTimeMillis (); log.info (stringredistemplate.randomkey ()); Long End = System.CurrentTimeMillis (); log.info («Полная задача третья, трудоемкая:« + (end - start) + "миллисекунд"); }}ПРИМЕЧАНИЕ. Шаги по внедрению зависимостей и настройки Redis в pom.xml опущены здесь
Шаг 3: Измените модульный тест и смоделируйте ситуацию выключения в высокой параллелистике:
@Runwith (springjunit4classrunner.class) @springboottestpublic class applicationtests {@autowired частная задача; @Test @sneakythrows public void test () {for (int i = 0; i <10000; i ++) {task.dotaskone (); task.dotasktwo (); task.dotaskthree (); if (i == 9999) {System.Exit (0); }}}}ПРИМЕЧАНИЕ. Протягивая цикл, вы отправляете задачи в пул потоков, определенные выше. Поскольку он выполняется асинхронно, во время выполнения используйте System.Exit (0), чтобы закрыть программу. В настоящее время, поскольку выполняются задачи, вы можете наблюдать, безопасно ли уничтожение этих асинхронных задач и порядок других ресурсов в пружинном контейнере.
Шаг 4: Запустите модульный тест выше, и мы столкнемся с следующим содержимым исключением.
org.springframework.data.redis.redisconnectionFailureException: не может получить Jedis Connection; Вложенное исключение-redis.clients.jedis.exceptions.jedisconnectionException: не удалось получить ресурс из пула по адресу org.springframework.data.redis.connection.jedis.jedisconnectionFactory.fetchJedisconnector (jedisconnectionFactory.java:204) ~ [Spring-data-Redis-110.10.10.10.10.10.10.10.10.10.10.10.10. org.springframework.data.redis.connection.jedis.jedisconnectionFactory.getConnection (jedisconnectionFactory.java:348) ~ [Spring-Data-REDIS-1.8.10.Release.jar: Na] AT org.springframework.data.redis.core.redisconnectionutils.dogetconnection (redisconnectionutils.java:129) ~ [Spring-Data-Redis-1.8.10.release.jar: Na] AT org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:92) ~[spring-data-redis-1.8.10.RELEASE.jar:na] at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:79) ~ [Spring-data-redis-1.8.10.release.jar: na] на org.springframework.data.redis.core.redistemplate.execute (redistemplate.java:194) ~ [Spring-Data-Redis-1.8.10.Release.jar: na] в org.springframework.data.redis.core.redistemplate.execute (redistemplate.java:169) ~ [Spring-Data-Redis-1.8.10.Release.jar: Na] At org.springframework.data.redis.corestemplate.randomkey (redisteplatework.jredis.redis.redistemplate.randomkey (redisteplate.jred.redis.redistemplate.randomkey (redisteplate.jred.redis.redistemplate.randomkey. ~ [Spring-Data-Redis-1.8.10.Release.jar: na] в com.didispace.async.task.dotaskone (task.java:26) ~ [classes/: na] at com.didispace.async.task $$ fassclassbyspringcglib $$. org.springframework.cglib.proxy.methodproxy.invoke (MethodProxy.java:204) ~ [Spring-Core-4.3.14.Release.jar: 4.3.14.Release] AT org.springframework.aop.framework.cglibaopproxy $ cglibmethodinvocation.invokejoinpoint (cglibaopproxy.java:738) ~ [Spring-Aop-4.3.14.Release.jar: 4.3.14.release] at org.springframework.aop.framework.reflectivemethodinvocation.proceed (Refertivemethodinvocation.java:157) ~ [Spring-Aop-4.3.14.Release.jar: 4.3.14.Release] AT org.springframework.aop.interceptor.asyncexecutionInterceptor $ 1.call (asyncexecutionInterceptor.java:115) ~ [Spring-aop-4.3.14. [NA: 1.8.0_151] по адресу java.util.concurrent.scheduledthreadpoolexecutor $ warduldfuturetask.access $ 201 (cheduledthreadpoolexecutor.java:180) [NA: 1.8.0_151] java.util.concurrent.scheduledThreadPoolexeCutor $ preseedFutureTask.run (PredulleDThreadPoolexeCutor.java:293) [Na: 1.8.0_151] at java.util.concurrent.threadpoolexecutor.runworker (rathoulexececececececececececececececececececececececececececececececececececem. [NA: 1.8.0_151] на java.util.concurrent.threadpoolexecutor $ abler.run (threadpoolexecutor.java:624) [Na: 1.8.0_151] на java.lang.thread.run (thread.java:748) [Na: 1,8.0_151], потому что: «nath.java:748) [Na: 1,8.0_151]. redis.clients.jedis.exceptions.jedisconnectionexception: не удалось получить ресурс из бассейна at redis.clients.util.pool.getresource (pool.java:53) ~ [jedis-2.9.0.jar: na] atis.clients.jedis.jedispool.getresource (jedispool.java.266). ~ [jedis-2.9.0.jar: na] at redis.clients.jedis.jedispool.getresource (jedispool.java:16) ~ [jedis-2.9.0.jar: na] org.springframework.data.redis.connection.jedis.jedisconnectionFactory.fetchJedisconnector (jedisconnectionFactory.java:194) ~ [Spring-Data-Redis-1.8.10.Release.jar: Na] ... 19 Обычные кадры, опущенные: Java.lang.Intertex java.util.concurrent.locks.abstractqueudsynchronizer $ condityObject.ReportIntertafterWait (AbstractQueuedSynchronizer.java:2014) ~ [NA: 1.8.0_151] AT java.util.concurrent.locks.abstractqueudsynchronizer $ condityObject.awaitnanos (AbstractQueuedSynchronicherizer.java:2088) ~ [NA: 1.8.0_151] at org.apache.commons.pool2.impl.linkedbrockecdeque.pollfirst (linkedblockingdequ. ~ [commons-pool2-2.4.3.jar: 2.4.3] at org.apache.commons.pool2 org.apache.commons.pool2.impl.genericobjectpool.borrowobject (geneicobjectpool.java:361) ~ [commons-pool2-2.4.3.jar: 2.4.3] at redis.clients.util.pool.getresource (pool.java:49) ~ [Jedis-2.0.
Как это решить
Причина анализа
Из исключения息JedisConnectionException: Could not get a resource from the pool , нам легко думать, что асинхронная задача все еще выполняется, когда приложение закрыто. Поскольку в первую очередь разрушен пул подключений Redis, приведена выше, в асинхронной задаче сообщается о приведенной мере, чтобы получить доступ к Redis. Итак, мы пришли к выводу, что приведенный выше метод реализации не является элегантным, когда приложение закрыто, так что мы должны делать?
Решение
Очень просто решить вышеуказанную проблему. Spring's Threadpooltaskscheduler предоставляет нам соответствующие конфигурации, просто добавьте следующие настройки:
@Bean ("taskexecutor") публичный исполнитель taskexecutor () {threadpooltaskscheduler executor = new Threadpooltaskscheduler (); Исполнитель.setPoolsize (20); Исполнитель.setThreadNamePrefix ("taskexeCutor-"); Исполнитель.setWaitFortAskStoCOmpleteeOnShutdown (true); Исполнитель.setawaitTerminationseconds (60); return Executor;} Примечание. setWaitForTasksToCompleteOnShutdown(true) Этот метод является ключом здесь. Он используется для установки пула потоков, чтобы дождаться выполнения всех задач, прежде чем продолжить уничтожать другие бобы. Таким образом, разрушению этих асинхронных задач будет предшествовать разрушение бассейна ниток Redis. В то же время, SetawaitTerminationseconds (60) также установлены здесь. Этот метод используется для установки времени ожидания задач в пуле потоков. Если он превышает на этот раз, он будет вынужден уничтожить его, прежде чем он будет уничтожен, чтобы убедиться, что приложение может быть закрыто в конце, а не заблокировано.
Полный пример:
Читатели могут выбрать следующие два репозиторию для просмотра проектов главы4-1-4 в соответствии с их предпочтениями:
Github: https://github.com/dyc87112/springboot-learning/
Gitee: https://gitee.com/diidispace/springboot-learning/
Локальная загрузка: http://xiazai.vevb.com/201805/yuanma/springboot-learning(vevb.com).rar
Суммировать
Вышеуказанное - все содержание этой статьи. Я надеюсь, что содержание этой статьи имеет определенную справочную ценность для каждого обучения или работы. Если у вас есть какие -либо вопросы, вы можете оставить сообщение для общения. Спасибо за поддержку Wulin.com.