Dukungan Multithreading dan Connection Pool pada Klien Email
Klien email seperti ImapClient, Pop3Client, dan SmtpClient dapat digunakan dalam lingkungan multithreaded. Klien dapat mempertahankan satu atau lebih koneksi dengan server. Untuk mengelola sekumpulan koneksi di dalam klien, connection pool digunakan. Jumlah koneksi yang dapat dibuat dan digunakan secara bersamaan dibatasi oleh CredentialsByHostClient.MaxConnectionsPerServer properti. Properti ini dapat diatur ke 1 atau nilai yang lebih besar. Secara default, nilainya adalah 10.
Antrian perintah diimplementasikan untuk setiap koneksi untuk mendukung operasi multithreading. Perintah mengimplementasikan operasi paling sederhana yang didefinisikan dalam protokol, seperti Noop, Authenticate, dan seterusnya. Pengguna dapat memulai eksekusi lebih banyak perintah daripada koneksi yang tersedia, namun perintah tersebut hanya akan dijalankan ketika klien dapat membuat koneksi untuk operasi tersebut.
Bagaimana Klien Email Berperilaku dalam Lingkungan Multithreaded
Klien email memiliki perilaku berikut:
-
Ketika
MaxConnectionsPerServer = 1, klien membuat satu koneksi dan melakukan otentikasi serta otorisasi. Koneksi ini dipertahankan dalam keadaan kerja sampai klien dibuang. Semua operasi dari thread berbeda diarahkan ke satu antrian perintah yang ditempatkan di koneksi utama. -
Ketika
MaxConnectionsPerServer > 1, klien membuat jumlah koneksi yang dibutuhkan dan melakukan otentikasi serta otorisasi untuk setiap koneksi. Satu koneksi dicadangkan sebagai koneksi utama. Koneksi ini dipertahankan dalam keadaan kerja sampai klien dibuang. Semua koneksi lain dibuat dan dibuang sesuai kebutuhan. Jumlah maksimum koneksi semacam itu ditentukan olehMaxConnectionsPerServerproperti. Misalnya, jikaMaxConnectionsPerServer = 2, kemudian satu koneksi dicadangkan sebagai koneksi utama, dan koneksi kedua digunakan sebagai tambahan untuk operasi yang dijalankan di thread lain. Oleh karena itu, jikaMaxConnectionsPerServer = 3, kemudian koneksi pertama dicadangkan sebagai koneksi utama, dan dua koneksi lain digunakan sebagai tambahan untuk operasi yang dijalankan di thread lain. Ketika permintaan koneksi datang dari thread baru dan semua koneksi sudah digunakan, klien menunggu sampai jumlah koneksi yang dipakai berkurang. Ini adalah momen penting yang menjelaskan mengapa membuang koneksi secara benar sangat penting.
Contoh Penggunaan Klien Email dalam Lingkungan Multithreaded
Pengguna dapat mengeksekusi operasi dalam thread yang berbeda dengan beberapa cara. Mereka dapat dibagi menjadi dua tipe.
Menggunakan Metode Asinkron (Begin/End)
Pengguna memakai metode asinkron (Begin/End) yang didefinisikan dalam klien. Dalam hal ini, klien email meluncurkan thread baru bila diperlukan. Antrian tugas diimplementasikan dalam klien (jangan bingung dengan antrian perintah dalam koneksi). Sebuah tugas dapat dijalankan jika ada koneksi yang tersedia. Setelah jumlah koneksi yang dipakai menjadi kurang dari nilai limit, klien membuat koneksi baru, membuat thread untuk tugas tersebut, dan mengeksekusi tugas ini. Contoh penggunaan operasi asinkron:
// Create an imapclient with host, user and password
ImapClient client = new ImapClient();
client.Host = "domain.com";
client.Username = "username";
client.Password = "password";
client.SelectFolder("InBox");
ImapMessageInfoCollection messages = client.ListMessages();
IAsyncResult res1 = client.BeginFetchMessage(messages[0].UniqueId);
IAsyncResult res2 = client.BeginFetchMessage(messages[1].UniqueId);
MailMessage msg1 = client.EndFetchMessage(res1);
MailMessage msg2 = client.EndFetchMessage(res2);
Menggunakan Thread Buatan Pengguna
Pengguna dapat membuat thread menggunakan objek seperti Thread, ThreadPool, Task, atau objek lain yang dimaksudkan untuk tujuan ini. Pengguna juga dapat menggunakan thread yang dibuat dalam kode pihak ketiga. Dalam kasus ini, klien memiliki dua model perilaku.
a. Jika pengguna tidak menyiapkan pembuatan koneksi tambahan untuk operasi dalam thread, semua operasi untuk thread ini akan dikirim ke antrian perintah koneksi utama. Berikut contoh operasi dalam thread tambahan tanpa membuat koneksi baru — semua transaksi dilakukan melalui koneksi utama:
List<MailMessage> List = new List<MailMessage>();
ThreadPool.QueueUserWorkItem(delegate(object o)
{
client.SelectFolder("folderName");
ImapMessageInfoCollection messageInfoCol = client.ListMessages();
foreach (ImapMessageInfo messageInfo in messageInfoCol)
{
List.Add(client.FetchMessage(messageInfo.UniqueId));
}
});
b. Ketika pengguna menjalankan metode untuk membuat koneksi baru bagi thread tambahan, thread ini diblokir sampai nilai kuota untuk koneksi baru berubah sehingga mengizinkan koneksi baru. Kemudian koneksi baru dibuat. Koneksi ini ditetapkan sebagai koneksi default untuk semua operasi dalam thread ini. Setelah semua operasi dalam thread selesai, koneksi harus dibuang. Untuk membuat koneksi baru, gunakan CredentialsByHostClient.CreateConnection metode. Metode ini mengembalikan objek yang mengimplementasikan IDisposable interface. Untuk melepaskan koneksi, the Dispose metode harus dipanggil. Membuat dan membuang koneksi harus dijalankan di dalam thread tempat operasi email dijalankan. Upaya membuat koneksi baru di thread tempat klien email dibuat menyebabkan error, karena thread ini tidak dapat digunakan untuk membuat koneksi baru pada saat itu. Membuat koneksi baru juga tidak memungkinkan ketika MaxConnectionsPerServer = 1. Contoh kode membuat koneksi baru dalam thread tambahan:
List<MailMessage> List1 = new List<MailMessage>();
ThreadPool.QueueUserWorkItem(delegate(object o)
{
using (IDisposable connection = client.CreateConnection())
{
client.SelectFolder("FolderName");
ImapMessageInfoCollection messageInfoCol = client.ListMessages();
foreach (ImapMessageInfo messageInfo in messageInfoCol)
List1.Add(client.FetchMessage(messageInfo.UniqueId));
}
});
Connection Pool
Mulai dari Aspose.Email 19.3, connection pool direfaktor. The EmailClient kelas diperkenalkan, yang pada akhirnya menggantikan CredentialsByHostClient kelas. The EmailClient kelas menyediakan sebuah ConnectionAsgmtMode properti yang menentukan mode alokasi koneksi dalam lingkungan multithread. EmailClient.ConnectionAsgmtMode diatur menggunakan ConnectionAsgmtType enumerasi.
Tipe Koneksi
Ada tiga tipe koneksi:
- Koneksi Utama. Ini adalah koneksi yang dibuat dan dibuang bersama dengan klien email. Tidak dapat dibuat atau dibuang secara manual.
- Koneksi Default. Pengguna dapat membuat koneksi default untuk thread dengan CreateConnection metode. Jika koneksi default ada, semua metode klien email yang dijalankan dalam sebuah thread akan secara implisit menggunakan koneksi ini. Hanya satu koneksi default yang dapat ada per thread. Itu dapat dibuat secara manual atau otomatis, tergantung pada
EmailClient.ConnectionAsgmtModeproperti. Koneksi ini dapat dibuat secara manual denganEmailClient.CreateConnection(createAsDefaultConnection = true)metode. Jika koneksi default tidak digunakan (tergantung pada mode alokasi koneksi), maka koneksi utama akan digunakan secara implisit sebagai gantinya. - Koneksi Independen. Ini adalah koneksi yang tidak terhubung dengan thread. Mereka dapat dibuat secara manual dan harus digunakan secara eksplisit sebagai parameter metode. Koneksi ini dapat dibuat secara manual dengan
EmailClient.CreateConnection()metode atau theEmailClient.CreateConnection(createAsDefaultConnection = false)metode.
Tipe Alokasi Koneksi
Untuk mengatur EmailClient.ConnectionAsgmtMode properti, the ConnectionAsgmtType enumerasi ini digunakan. Tipe alokasi yang disediakannya tercantum di bawah.
-
ConnectionAsgmtType.UseMainOrDefault Mode ini digunakan secara default pada klien email. Klien email menggunakan koneksi utama untuk semua operasi dari multiple thread jika koneksi default belum dibuat, atau jika koneksi tidak diberikan sebagai parameter metode secara eksplisit. Koneksi utama dibuat bersamaan dengan klien email. Pengguna dapat membuat koneksi default untuk thread dengan
CreateConnectionmetode. Jika koneksi default untuk sebuah thread dibuat, maka digunakan secara implisit untuk semua metode klien email yang dipanggil dalam thread tersebut. Jika koneksi default untuk sebuah thread tidak dibuat, koneksi utama digunakan untuk semua metode yang dipanggil dalam thread tersebut. Pengguna juga dapat membuat koneksi yang tidak terkait dengan thread (bukan koneksi default) denganCreateConnectionmetode. Untuk menggunakan koneksi lain (bukan utama dan bukan default), pengguna harus melewatkan koneksi secara eksplisit sebagai parameter metode. Pengguna juga dapat membuat jumlah koneksi berapapun. Hanya satu koneksi default yang dapat ada per thread. Harap perhatikan bahwa koneksi default bekerja dengan benar jika pengguna menggunakanThreadobjek untuk pemrograman multitasking. Jika pengguna menggunakan connection pool atauTaskobjek untuk multitasking, mode ini dapat menyebabkan perilaku yang tidak tepat. Untuk menghindari masalah ini, pengguna harus secara manual membuang koneksi default (jika digunakan) di akhir eksekusi kode. -
ConnectionAsgmtType.UseMain Klien email menggunakan koneksi utama untuk semua operasi dari multiple thread. Koneksi utama dibuat bersamaan dengan klien email. Pengguna tidak dapat membuat koneksi default, tetapi dapat membuat koneksi yang tidak terkait dengan thread dengan
CreateConnectionmetode. Untuk menggunakan koneksi lain, pengguna harus melewatkannya secara eksplisit sebagai parameter metode. -
ConnectionAsgmtType.UseDefault Klien email menggunakan hanya koneksi default secara implisit untuk semua operasi dari multiple thread. Koneksi utama tidak digunakan dalam mode ini. Jika koneksi default belum dibuat untuk sebuah thread (pada pemanggilan pertama metode klien email), klien email membuat koneksi default secara implisit untuk thread sebelum operasi pertama dijalankan. Pengguna tidak dapat membuat koneksi default untuk thread dengan
CreateConnectionmetode karena mereka dibuat secara otomatis. Pengguna juga dapat membuat koneksi yang tidak terkait dengan thread menggunakanCreateConnectionmetode. Untuk menggunakan koneksi lain, pengguna harus melewatkannya secara eksplisit sebagai parameter metode. Pengguna juga dapat membuat jumlah koneksi berapapun. Hanya satu koneksi default yang dapat digunakan per thread. Harap perhatikan bahwa koneksi default bekerja dengan benar jika pengguna menggunakanThreadobjek untuk pemrograman multitasking. Jika pengguna menggunakan connection pool atauTaskobjek untuk multitasking, mode ini dapat menyebabkan perilaku yang tidak tepat. Untuk menghindari masalah ini, pengguna harus secara manual membuang koneksi default di akhir eksekusi kode.
Rekomendasi
Jika pengguna mengirim semua perintah ke koneksi utama, situasi dapat terjadi di mana perintah dari thread yang berbeda tercampur. Pengguna harus memahami perintah mana yang bergantung pada urutannya dan mengambil langkah untuk menyinkronkan perintah tersebut. Juga perlu mempertimbangkan kemungkinan mengeksekusi perintah dalam sesi yang berbeda (IMAP/POP3). Operasi yang paling memakan waktu adalah FetchMessage, AppendMessage, dan Send. Mungkin masuk akal untuk melakukan operasi ini dengan thread baru dan koneksi baru. Operasi cepat seperti Delete masuk akal untuk melakukan dengan koneksi utama. Harap perhatikan bahwa inisialisasi koneksi baru adalah operasi yang cukup memakan waktu.