मेल क्लाइंट्स में मल्टी-थ्रेडिंग समर्थन

मेल क्लाइंट्स जैसे ImapClient और Pop3Client उपयोगकर्ताओं को मल्टी-थ्रेडिंग वातावरण में उनका उपयोग करने की अनुमति देता है। मेल क्लाइंट्स सर्वर के साथ एक या अधिक कनेक्शन रखने की सुविधा प्रदान करते हैं। क्लाइंट्स के भीतर कनेक्शन के सेट को प्रबंधित करने के लिए कनेक्शन पूल का उपयोग किया जाता है। एक ही समय में बनाए और उपयोग किए जा सकने वाले कनेक्शन की संख्या CredentialsByHostClient.MaxConnectionsPerServer प्रॉपर्टी द्वारा सीमित होती है। यह प्रॉपर्टी 1 या अधिक मान पर सेट की जा सकती है। डिफ़ॉल्ट रूप से यह प्रॉपर्टी 10 के बराबर होती है। मल्टी-थ्रेडिंग ऑपरेशन्स को सपोर्ट करने के लिए कनेक्शन में कमांड कतार लागू की गई है। कमांड्स प्रोटोकॉल में परिभाषित सरलतम ऑपरेशन्स को लागू करते हैं, जैसे Noop, Authenticate आदि। उपयोगकर्ता उपलब्ध कनेक्शन की संख्या से अधिक कमांड्स को शुरू कर सकता है, लेकिन वे तभी निष्पादित होंगे जब क्लाइंट ऑपरेशन के लिए कनेक्शन बनाने में सक्षम होगा।

मल्टी-थ्रेडिंग वातावरण में मेल क्लाइंट्स के उपयोग के तरीके

ईमेल क्लाइंट्स का निम्नलिखित व्यवहार होता है:

1. जब MaxConnectionsPerServer = 1 हो, क्लाइंट केवल 1 कनेक्शन बनाता है, प्रमाणीकरण और प्राधिकरण करता है। यह कनेक्शन क्लाइंट डिस्पोज़ होने तक कार्य स्थिति में रहता है। विभिन्न थ्रेडों से सभी ऑपरेशन्स मुख्य कनेक्शन में रखी एक कमांड कतार में निर्देशित होते हैं।

2. जब MaxConnectionsPerServer > 1 हो, क्लाइंट आवश्यक संख्या में कनेक्शन बनाता है, प्रत्येक कनेक्शन के लिए प्रमाणीकरण और प्राधिकरण करता है। एक कनेक्शन मुख्य कनेक्शन के रूप में आरक्षित रहता है। यह कनेक्शन क्लाइंट डिस्पोज़ होने तक कार्य स्थिती में समर्थित रहता है। अन्य सभी कनेक्शन मांग पर बनाए और डिस्पोज़ किए जाते हैं। ऐसी कनेक्शन की अधिकतम संख्या MaxConnectionsPerServer प्रॉपर्टी द्वारा परिभाषित होती है, उदाहरण के लिए यदि MaxConnectionsPerServer = 2 है, तो एक मुख्य कनेक्शन के रूप में आरक्षित रहता है, और दूसरा कनेक्शन अतिरिक्त के रूप में उन ऑपरेशनों के लिए उपयोग होता है जो अन्य थ्रेडों में चलाए जाते हैं। इसी प्रकार यदि MaxConnectionsPerServer = 3 है, तो पहला कनेक्शन मुख्य कनेक्शन रहता है, और दो अन्य कनेक्शन अतिरिक्त के रूप में अन्य थ्रेडों में चलाए जाने वाले ऑपरेशनों के लिए उपयोग होते हैं। यदि नए थ्रेड से कनेक्शन का अगला अनुरोध आता है, और सभी कनेक्शन पहले से उपयोग में हैं, तो क्लाइंट तब तक प्रतीक्षा करता है जब तक उपयोग किए गए कनेक्शन की संख्या कम नहीं हो जाती। यह बहुत महत्वपूर्ण बिंदु है जो स्पष्ट करता है कि कनेक्शन का सही रूप से डिस्पोज़ करना क्यों आवश्यक है।

उदाहरण

उपयोगकर्ता विभिन्न थ्रेडों में ऑपरेशन्स कई तरीकों से निष्पादित कर सकता है। इन्हें दो प्रकार में विभाजित किया जा सकता है:

1. उपयोगकर्ता क्लाइंट में परिभाषित असिंक्रोनस (Begin/End) मेथड्स का उपयोग करता है। इस स्थिति में मेल क्लाइंट आवश्यकता के अनुसार नई थ्रेड लॉन्च करता है। क्लाइंट में टास्क कतार लागू की गई है (कृपया, कनेक्शन की कमांड कतार से भ्रमित न हों)। टास्क तब निष्पादित किया जा सकता है जब कनेक्शन उपलब्ध हो। एक बार उपयोग किए गए कनेक्शन की संख्या सीमा मान से कम हो जाने पर, क्लाइंट नई कनेक्शन बनाता है, वर्तमान टास्क के लिए थ्रेड बनाता है और इस टास्क को चलाता है। असिंक्रोनस ऑपरेशन्स के उपयोग का उदाहरण:

// Create an imapclient with host, user and password
ImapClient client = new ImapClient();
client.setHost("domain.com");
client.setUsername("username");
client.setPassword("password");
client.selectFolder("InBox");

ImapMessageInfoCollection messages = client.listMessages();
IAsyncResult res1 = client.beginFetchMessage(messages.get_Item(0).getUniqueId());
IAsyncResult res2 = client.beginFetchMessage(messages.get_Item(1).getUniqueId());
MailMessage msg1 = client.endFetchMessage(res1);
MailMessage msg2 = client.endFetchMessage(res2);

2. उपयोगकर्ता Thread, ExecutorService या इस उद्देश्य के लिए निर्धारित किसी भी अन्य ऑब्जेक्ट्स का उपयोग करके थ्रेड बना सकता है। साथ ही उपयोगकर्ता थर्ड-पार्टी कोड में बनाए गए थ्रेड का भी उपयोग कर सकता है। इस दौरान क्लाइंट के दो व्यवहार मॉडल होते हैं

a. यदि उपयोगकर्ता ने थ्रेड में ऑपरेशनों के लिए अतिरिक्त कनेक्शन बनाने को नहीं चुना है, तो इस थ्रेड के सभी ऑपरेशन मुख्य कनेक्शन की कमांड कतार में भेजे जाएंगे। नई कनेक्शन बनाए बिना अतिरिक्त थ्रेड में ऑपरेशनों का एक उदाहरण, सभी लेनदेन मुख्य कनेक्शन के माध्यम से किए जाते हैं:

/**
 * Creates an executor service with a fixed pool size, that will time 
 * out after a certain period of inactivity.
 * 
 * @param poolSize The core- and maximum pool size
 * @param keepAliveTime The keep alive time
 * @param timeUnit The time unit
 * @return The executor service
 */
private static ExecutorService createFixedTimeoutExecutorService(
    int poolSize, long keepAliveTime, TimeUnit timeUnit)
{
    ThreadPoolExecutor e = 
        new ThreadPoolExecutor(poolSize, poolSize,
            keepAliveTime, timeUnit, new LinkedBlockingQueue<Runnable>());
    e.allowCoreThreadTimeOut(true);
    return e;
}

final List<MailMessage> list = new ArrayList<MailMessage>();

ExecutorService executor = createFixedTimeoutExecutorService(1, 1000, TimeUnit.MILLISECONDS);

executor.execute(new Runnable() {
    public void run() {
        client.selectFolder("folderName");
        ImapMessageInfoCollection messageInfoCol = client.listMessages();
        for (ImapMessageInfo messageInfo : messageInfoCol) {
            list.add(client.fetchMessage(messageInfo.getUniqueId()));
        }
    }
});

b. जब उपयोगकर्ता अतिरिक्त थ्रेड के लिए नई कनेक्शन बनाने के लिए मेथड चलाता है, तो यह थ्रेड तब तक अवरुद्ध रहता है जब तक नई कनेक्शन की कोटा मान बदल नहीं जाता जिससे नई कनेक्शन की अनुमति मिल सके। फिर नई कनेक्शन बनाई जाती है। यह कनेक्शन इस थ्रेड में सभी ऑपरेशनों के लिए डिफ़ॉल्ट कनेक्शन के रूप में सेट की जाती है। इस थ्रेड में सभी ऑपरेशन पूरा होने के बाद कनेक्शन को डिस्पोज़ किया जाना चाहिए। नई कनेक्शन बनाने के लिए CredentialsByHostClient.CreateConnection मेथड का उपयोग करें। यह मेथड एक ऑब्जेक्ट लौटाता है जो IDisposable इंटरफ़ेस को लागू करता है। कनेक्शन को रिलीज़ करने के लिए Dispose मेथड को बुलाना आवश्यक है। कनेक्शन का निर्माण और डिस्पोज़ करना वही थ्रेड के भीतर किया जाना चाहिए जहाँ मेल ऑपरेशन्स चलाते हैं। उस थ्रेड में नई कनेक्शन बनाने का प्रयास जहाँ मेल क्लाइंट बनाया गया है, त्रुटि उत्पन्न करता है, क्योंकि इस क्षण वह थ्रेड नई कनेक्शन निर्माण के लिए उपयोग नहीं किया जा सकता। साथ ही जब MaxConnectionsPerServer = 1 हो तो नई कनेक्शन बनाना संभव नहीं है। अतिरिक्त थ्रेड में नई कनेक्शन बनाने का कोड उदाहरण:

final List<MailMessage> list = new ArrayList<MailMessage>();

ExecutorService executor = createFixedTimeoutExecutorService(1, 1000, TimeUnit.MILLISECONDS);

executor.execute(new Runnable() {
    public void run() {
        IConnection connection = client.createConnection();
        try {
            client.selectFolder(connection, "FolderName");
            ImapMessageInfoCollection messageInfoCol = client.listMessages(connection);
            for (ImapMessageInfo messageInfo : messageInfoCol)
                list.add(client.fetchMessage(connection, messageInfo.getUniqueId()));
        } finally {
            connection.dispose();
        }
    }
});

सिफारिशें

ध्यान देने योग्य बात यह है कि यदि उपयोगकर्ता सभी कमांड मुख्य कनेक्शन को भेजता है, तो विभिन्न थ्रेडों से आने वाले कमांड मिश्रित हो सकते हैं। उपयोगकर्ता को समझना चाहिए कि कौन से कमांड उनकी क्रमबद्धता पर निर्भर हैं, और ऐसे कमांडों के सिंक्रनाइज़ेशन के उपाय करने चाहिए। यह भी आवश्यक है कि विभिन्न सत्रों (IMAP/POP3) में कमांडों को निष्पादित करने की संभावना पर विचार किया जाए। सबसे अधिक समय लेनी वाली ऑपरेशन्स, जैसे FetchMessage, AppendMessage और Send, संभवतः नए थ्रेड और नई कनेक्शन के साथ करने का उपयुक्त होगा। लेकिन Delete जैसी त्वरित ऑपरेशन्स को मुख्य कनेक्शन के साथ करना उचित है। कृपया ध्यान दें कि नई कनेक्शन का प्रारम्भिककरण स्वयं ही पर्याप्त समय लेने वाला ऑपरेशन है।