مقدمة
لقد سبق أن شاركت مقالة حول استخدام ASYNC لتنفيذ المهام غير المتزامنة والتحكم في تجمع مؤشرات الترابط في SPRING BOOT: "يستخدم SPRING BOOT ASYNC لتنفيذ المكالمات غير المتزامنة: تجمع مؤشرات الترابط المخصص". منذ أن اكتشفت مؤخرًا العديد من المشكلات الناجمة عن عدم التعامل مع المهام غير المتزامنة بشكل صحيح ، سأستمر في الحديث عن الإغلاق الأنيق لمسبح الخيوط ، وخاصةً لتجمعات خيوط ThreadPooltasscheduler.
ظاهرة المشكلة
في المثال ، الفصل 4-1-3 من المقالة السابقة ، حددنا تجمع مؤشرات الترابط ، ثم كتبنا 3 مهام باستخدام التعليق التوضيحي ASYNC ، وقمنا بتحديد تجمع مؤشرات الترابط المستخدمة في هذه المهام للتنفيذ. في اختبار الوحدة أعلاه ، لم نتحدث على وجه التحديد عن القضايا المتعلقة بالإغلاق. دعنا نحاكي مشكلة ونتوصل إليها في الموقع.
الخطوة 1: كما كان من قبل ، نحدد تجمع مؤشر ترابط ThreadPooltasscheduler:
springbootapplicationpublicpublic application {public static void main (string [] args) {springapplication.run (application.class ، args) ؛ } enableAsync @class taskpoolConfig {bean ("taskexecutor") المنفذ العام taskexecutor () {threadpooltaskscheduler evelicedor = new threadpooltaskscheduler () ؛ Executor.SetPoolSize (20) ؛ Executor.SetThReadNamePrefix ("taskexecutor-") ؛ إرجاع المنفذ ؛ }}}الخطوة 2: إصلاح المهمة غير المتزامنة السابقة وجعلها تعتمد على مورد خارجي ، مثل redis
@slf4j @componentpublic class task {Autowired private stringRedIstemplate stringRedIstemplate ؛ async ("taskexecutor") public void dotaskone () يلقي الاستثناء {log.info ("Start Task One") ؛ بدء طويل = system.currentTimeMillis () ؛ log.info (stringRediStemplate.RandomKey ()) ؛ End Long = System.CurrentTimeMillis () ؛ log.info ("المهمة الكاملة الأولى ، الوقت المستغرق:" + (end - start) + "milliseconds") ؛ } async ("taskexecutor") public void dotasktwo () يلقي الاستثناء {log.info ("start task 2") ؛ بدء طويل = system.currentTimeMillis () ؛ log.info (stringRediStemplate.RandomKey ()) ؛ End Long = System.CurrentTimeMillis () ؛ log.info ("المهمة الكاملة 2 ، الوقت المستغرق:" + (end - start) + "milliseconds") ؛ } async ("taskexecutor") public void dotaskthree () يلقي الاستثناء {log.info ("Start Task Three") ؛ بدء طويل = system.currentTimeMillis () ؛ log.info (stringRediStemplate.RandomKey ()) ؛ End Long = System.CurrentTimeMillis () ؛ log.info ("المهمة الكاملة الثالثة ، تستهلك الوقت:" + (end - start) + "milliseconds") ؛ }}ملاحظة: تم حذف الخطوات لتقديم التبعيات وتكوين redis في pom.xml هنا
الخطوة 3: تعديل اختبار الوحدة ومحاكاة وضع الإغلاق في التزامن العالي:
Runwith (SpringJunit4ClassRunner.Class) springBootTestpublic Class ApplicationTests {autowired private Task Task ؛ testSTSneakyThrows 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 ؛ الاستثناء المتداخل هو redis.clients.jedis.exceptions.jedisconnectionException: لا يمكن الحصول على مورد من التجمع في org.springframework.data.connection.jedis.jedisconnectionfactory.fetchjedisconnect 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] at org.springframework.data.core.core. ~ [spring-data-redis-1.8.10.release.jar: na] at org.springframework.data.redis.core.redistemplate.execute (redistemplate.java:169) org.springframework.data.redis.core.redistemplate.randomkey (redistemplate.java:781) ~ [spring-data-redis-1.8.10.release.jar: na] at com.didispace.async.task.dotaskone (task.java:26) ~ com.didispace.async.task $$ fassclassbyspringcglib $$ ca3ff9d6.invoke (<sender>) ~ [classes/: na] at org.springframework.cglib.proxy.methodproxy.invoke (methodproxy.java:204) ~ [spring-core-4.3.14.release.jar: 4.3.14.release] في org.springframework.aop.framework.cglibaopproxy $ cglibmethodinvocation.invokejoinpoint (cglibaoproxy.java:738) org.springframework.aop.framework.reflectivemethodinvocation.proceed (refcerivemethodinvocation.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-444.Release.jar: 4.3.14.release] [NA: 1.8.0_151] في java.util.concurrent.scheduledThreadPoolExecutor $ SecrledFutureTask.Access $ 201 java.util.concurrent.scheduledthreadpoolexecutor $ ScheduleDfutureTask.Run (SecretultHreadPoolexecutor.java:293) [NA: 1.8.0_151] في java.util.concurrent.threadpoolexecutor $ worker.run (threadpoolexecutor.java:624) redis.clients.jedis.exceptions.jedisconnectionexception: لا يمكن الحصول على مورد من التجمع في redis.clients.util.pool.getResource (pool.java:53) ~ [jedis-2.9.0.jar: na] - org.springframework.data.redis.connection.jedis.jedisconnectionfactory.fetchjedisconnector (jedisconnectionfactory.java:194) java.util.concurrent.locks.abstractqueuedsynchronizer $ inteBject.ReportInterruptAfterwait (AbstractQueuedSynchronizer.java:2014) ~ [Na: 1.8.0_151] في java.util.concurrent.locks.abstractqueuedsynchronizer $ intevicoBject.awaitnanos (agrectiqueuedsynchronizer.java:2088) ~ [na: 1.8.0_151] في org.apache.commons.pool2.impl.InkedBlockingDeafe ~ [commons-pool2-2.4.3.jar: 2.4.3] at org.apache.commons.pool2.impl.GenericObjectPool.BorrowObject org.apache.commons.pool2.impl.genericoBjectPool.BorrowObject (genericObjectPool.java:361) ~ [commons-pool2-2.4.3.jar: 2.4.3 at redis.clients.util.pool.getResource (pool.java:49)
كيفية حلها
تحليل السبب
من息JedisConnectionException: Could not get a resource from the pool ، فمن السهل علينا أن نعتقد أن المهمة غير المتزامنة لا تزال تنفذ عند إغلاق الطلب. نظرًا لأن تجمع اتصال redis يتم تدميره أولاً ، يتم الإبلاغ عن الخطأ أعلاه في المهمة غير المتزامنة للوصول إلى redis. لذلك ، خلصنا إلى أن طريقة التنفيذ أعلاه ليست أنيقة عند إغلاق التطبيق ، فماذا يجب أن نفعل؟
حل
من السهل جدًا حل المشكلة أعلاه. يوفر لنا ThreadPooltasscheduler من الربيع تكوينات ذات صلة ، فقط أضف الإعدادات التالية:
bean ("taskexecutor") المنفذ العام taskexecutor () {threadpooltasskscheduler executor = new threadpooltaskscheduler () ؛ Executor.SetPoolSize (20) ؛ Executor.SetThReadNamePrefix ("taskexecutor-") ؛ Executor.setWaitFortaskStocpleteOnshutdown (True) ؛ Executor.SetAwaitTerminationseConds (60) ؛ إرجاع المنفذ ؛} ملاحظة: setWaitForTasksToCompleteOnShutdown(true) هذه الطريقة هي المفتاح هنا. يتم استخدامه لتعيين تجمع الخيوط لانتظار جميع المهام لإكمالها قبل الاستمرار في تدمير الفاصوليا الأخرى. وبهذه الطريقة ، سيسبق تدمير هذه المهام غير المتزامنة تدمير تجمع خيوط Redis. في الوقت نفسه ، تم تعيين setawaitterminationseConds (60) هنا أيضًا. يتم استخدام هذه الطريقة لتعيين وقت انتظار المهام في تجمع الخيوط. إذا تجاوز هذا الوقت ، فسيتم إجباره على تدميره قبل تدميره لضمان إغلاق التطبيق في النهاية بدلاً من حظره.
مثال كامل:
يمكن للقراء اختيار المستودعين التاليين لعرض مشاريع الفصل 4-1-4 وفقًا لتفضيلاتهم:
Github: https://github.com/dyc87112/springboot-learning/
Gite: https://gitee.com/diidispace/springboot-learning/
تنزيل محلي: http://xiazai.vevb.com/201805/yuanma/springboot-learning(vevb.com).rar
لخص
ما سبق هو المحتوى الكامل لهذه المقالة. آمل أن يكون لمحتوى هذه المقالة قيمة مرجعية معينة لدراسة أو عمل الجميع. إذا كان لديك أي أسئلة ، فيمكنك ترك رسالة للتواصل. شكرا لك على دعمك إلى wulin.com.