जावा असिंक्रोनस प्रोसेसिंग
स्प्रिंग @Async को देखने से पहले, सिंक्रोनस, असिंक्रोनस और मल्टी-थ्रेडिंग की अवधारणाएँ आवश्यक हैं। इन अवधारणाओं को जानने की मान्यता के साथ, आइए कोड के माध्यम से शुद्ध जावा असिंक्रोनस प्रोसेसिंग विधि को देखें। यदि आप जावा थ्रेड से परिचित हैं, तो आप इस अध्याय को छोड़ सकते हैं।
यदि आप सिंक्रोनस तरीके से संदेश प्राप्त करते हैं और केवल आउटपुट करते हैं, तो आप ऊपर दिए गए कोड की तरह कोड लिख सकते हैं। इसे मल्टी-थ्रेडिंग असिंक्रोनस विधि में बदलने पर, आप स्रोत कोड को इस प्रकार लिख सकते हैं।
लेकिन इस पद्धति में थ्रेड का प्रबंधन नहीं किया जा सकता है, जो इसे बहुत खतरनाक बनाता है। उदाहरण के लिए, यदि 10,000 कॉल एक साथ किए जाते हैं, तो थ्रेड को बहुत कम समय में 10,000 बार बनाया जाना चाहिए। थ्रेड बनाने की लागत कम नहीं होती है, इसलिए यह प्रोग्राम के प्रदर्शन को नकारात्मक रूप से प्रभावित कर सकता है, और इससे OOM त्रुटि भी हो सकती है। इसलिए, थ्रेड को प्रबंधित करने के लिए, थ्रेड पूल को लागू किया जाना चाहिए, और जावा ExecutorService क्लास प्रदान करता है।
हमने थ्रेड की कुल संख्या को 10 तक सीमित कर दिया है, और हम सही ढंग से मल्टी-थ्रेडिंग असिंक्रोनस प्रोसेसिंग भी कर सकते हैं। हालाँकि, उपरोक्त विधि में प्रत्येक विधि पर ExecutorService की submit() विधि को लागू करने की आवश्यकता होती है जिसे हम असिंक्रोनस रूप से संसाधित करना चाहते हैं, इसलिए हमें बार-बार संशोधन कार्य करने की आवश्यकता है। दूसरे शब्दों में, यदि आप शुरू में सिंक्रोनस तर्क के साथ एक विधि लिखते हैं और इसे असिंक्रोनस में बदलना चाहते हैं, तो विधि के तर्क को ही बदलना होगा।
स्प्रिंग @Async
सरल तरीका
एप्लीकेशन क्लास के ऊपर @EnableAsync एनोटेशन लगाकर और सिंक्रोनस लॉजिक की उस विधि के ऊपर @Async एनोटेशन लगाकर जिसे हम असिंक्रोनस रूप से संसाधित करना चाहते हैं, यह हो जाता है। लेकिन ऊपर दी गई विधि में थ्रेड का प्रबंधन नहीं किया जाता है। ऐसा इसलिए है क्योंकि @Async की डिफ़ॉल्ट सेटिंग SimpleAsyncTaskExecutor का उपयोग करने के लिए है, जो थ्रेड पूल नहीं है, बल्कि केवल थ्रेड बनाने का काम करता है।
थ्रेड पूल का उपयोग करने का तरीका
सबसे पहले, Application क्लास से @EnableAsync को हटा दें। Application क्लास में @EnableAutoConfiguration या @SpringBootApplication सेटिंग होने पर, रनटाइम पर SpringAsyncConfig क्लास (जिसे हम नीचे बनाएंगे) में कॉन्फ़िगर किए गए threadPoolTaskExecutor बीन की जानकारी पढ़ी जाती है।
आप कोर और अधिकतम आकार सेट कर सकते हैं। इस समय, यह अनुमान लगाया जा सकता है कि यह शुरू में कोर आकार के साथ काम करेगा, और यदि यह उससे अधिक कार्य नहीं संभाल सकता है, तो यह अधिकतम आकार तक बढ़ जाएगा, लेकिन ऐसा नहीं है।
आंतरिक रूप से, यह Integer.MAX_VALUE आकार का LinkedBlockingQueue बनाता है, और यदि कोर आकार के थ्रेड कार्य नहीं संभाल सकते हैं, तो वे कतार में प्रतीक्षा करते हैं। जब कतार भर जाती है, तो यह अधिकतम आकार तक थ्रेड बनाता है और कार्य संभालता है।
यदि आप Integer.MAX_VALUE पर कतार का आकार निर्धारित करने में असहज हैं, तो आप queueCapacity निर्धारित कर सकते हैं। यदि आप इसे ऊपर की तरह सेट करते हैं, तो यह शुरू में 3 थ्रेड के साथ काम करेगा, और यदि प्रसंस्करण गति धीमी हो जाती है, तो यह 100 आकार की कतार में कार्य रखेगा, और यदि इससे अधिक अनुरोध आते हैं, तो यह अधिकतम 30 थ्रेड बनाएगा और कार्य संभालेगा।
थ्रेड पूल सेटिंग पूरी हो जाने के बाद, आपको @Async एनोटेशन वाली विधि में ऊपर दिए गए बीन का नाम जोड़ना होगा।
यदि आप कई प्रकार के थ्रेड पूल सेट करना चाहते हैं, तो आप threadPoolTaskExecutor() जैसी कई बीन बनाने वाली विधियाँ बना सकते हैं, और जब आप @Async सेट करते हैं, तो आप उस थ्रेड पूल बीन को डाल सकते हैं जिसे आप उपयोग करना चाहते हैं।
रिटर्न प्रकार के अनुसार लौटाया गया स्वरूप
रिटर्न मान नहीं होने पर
यह वह स्थिति है जहां असिंक्रोनस रूप से संसाधित की जाने वाली विधि को परिणाम वापस करने की आवश्यकता नहीं है। इस स्थिति में, @Async एनोटेशन का रिटर्न प्रकार void पर सेट किया जाना चाहिए।
रिटर्न मान होने पर
Future, ListenableFuture, CompletableFuture प्रकार को रिटर्न प्रकार के रूप में उपयोग किया जा सकता है। असिंक्रोनस विधि के रिटर्न स्वरूप को new AsyncResult() से बांधें।
[Future]
future.get() अनुरोध के परिणाम के आने तक प्रतीक्षा करने के लिए ब्लॉकिंग का उपयोग करता है। इसलिए, यह असिंक्रोनस ब्लॉकिंग विधि बन जाती है, जिसके कारण प्रदर्शन खराब होता है। आमतौर पर Future का उपयोग नहीं किया जाता है।
[ListenableFuture]
ListenableFuture कॉलबैक के माध्यम से कार्य को गैर-अवरोधी तरीके से संसाधित करने की अनुमति देता है। addCallback() विधि का पहला पैरामीटर कार्य पूर्णता कॉलबैक विधि है, और दूसरा पैरामीटर कार्य विफलता कॉलबैक विधि है। संदर्भ के लिए, चूँकि थ्रेड पूल का कोर थ्रेड 3 पर सेट है, आप देख सकते हैं कि "Task Start" संदेश पहले 3 बार प्रिंट होता है।
[CompletableFuture]
हालांकि ListenableFuture अकेले गैर-अवरोधी तर्क को लागू कर सकता है, यदि कॉलबैक के अंदर एक कॉलबैक की आवश्यकता होती है, तो यह बहुत जटिल कोड उत्पन्न करता है जिसे कॉलबैक नर्क कहा जाता है।
<span class="image-inline ck-widget" contenteditable="false"><img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F9f152db8-c015-43cd-bf13-85c594f5f218%2FUntitled.png?table=block&id=268ac0bc-ca7b-4bcb-b11b-ac611a5038b2&spaceId=b453bd85-cb15-44b5-bf2e-580aeda8074e&width=2000&userId=80352c12-65a4-4562-9a36-2179ed0dfffb&cache=v2" alt="Untitled" style="aspect-ratio:2000/1455;" width="2000" height="1455"></span>
बेशक, हम इस समय CompletableFuture पर विस्तार से चर्चा नहीं करेंगे, इसलिए यदि आप जटिल कॉलबैक को संभालने वाले कोड में रुचि रखते हैं, तो कृपया नीचे दिए गए स्रोत लिंक को देखें।
ListenableFuture में कॉलबैक परिभाषा की तुलना में इसमें बेहतर पठनीयता है, और यह गैर-अवरोधी कार्यक्षमता को पूरी तरह से निष्पादित करता है। इसलिए, जब आप @Async का उपयोग करते हैं और रिटर्न मान की आवश्यकता होती है, तो CompletableFuture का उपयोग करने की अनुशंसा की जाती है।
@Async के लाभ
डेवलपर सिंक्रोनस तरीके से विधियों को लिख सकते हैं, और यदि उन्हें असिंक्रोनस तरीके से संसाधित करने की आवश्यकता होती है, तो वे बस विधि के ऊपर @Async एनोटेशन लगा सकते हैं। इसलिए, आप सिंक्रोनस और असिंक्रोनस के लिए अच्छी तरह से बनाए रखने योग्य कोड बना सकते हैं।
@Async के लिए सावधानियां
- यदि आप private विधि पर @Async लगाते हैं, तो AOP काम नहीं करेगा।
- यदि एक ही ऑब्जेक्ट के अंदर की विधियों के बीच कॉल किया जाता है, तो AOP काम नहीं करेगा।
टिप्पणियाँ0