ASP.NET ile Threading
Açıklama: ASP.NET ile çoklu email gönderme uygulamasının Threading ile uygulanması.
Thread bir programın bağımsız olarak çalıştırılan parçasına denir. .NET platformu ile çok thread li programlar yazabiliriz. Tabiki bu durum ASP.NET için de geçerli. ASP nin önceki versiyonlarında böyle bir imkan mevcut değildi.
Örneğin makalemizin konusu olan çok fazla sayıda kişiye email göndermek için tüm kullanıcıları bir for döngüsüne alıp tek tek gönderme yaparken gönderilecek kişi sayısı artıkça işlemin alacağı sürede artar. Bu işi ASP3 gibi bir ortamda yapmak istersek asp sayfamız işlemlerin bitmesini bekleyemeden timeout olacak ve işlemimizde kesilecekti. ASP.NET cephesine bakarsak Threading desteği sayesinde bu iş kolaylaşıyor. Threading için yukarıda "bağımsız olarak çalıştırılan program kod parçası" deyimini kullanmıştık. İşte bu sayede email göndermek için gerekli olan kod parçasını (subroutione -sub-) uygulamanın ana threadinden ayrı olarak çalıştırıyoruz ve işlemin arkaplanda devam etmesini sağlıyoruz.
.Net te multi thread programlama "System.Threading" namespace indeki "Thread" sınıfı kullanılarak yapılabilir. Thread sınıfının yapılandırıcısı (constructor) ayrı bir thread olarak çalıştırılacak olan prosedürün adresini parametre olarak alır.
ASP.NET ile Threading kullanarak yazılmış olan bir projede 40-50 bin kişiye email gönderiyorum ve hiç bir sorun yaşamıyorum. İşte bu durumda "Neden ASP.NET?" diyenlere bir cevap daha vermiş olduk. :)
Uygulamamıza geçelim ve asp.net ile threading yapmayı öğrenelim.
Amacımız nedir? Çok sayıda kişiye email göndermek. Bu gönderim sırasında gönderim işleminin uzunluğu dolayısıyla sayfanın hızını kesmemek, işlemlerin arkaplanda yapılmaya devam etmesini sağlmak.
Amacımız doğrultusunda Postaci isminde bir sınıf oluşturalım. Gerekli namespace tanımlamalarını yaptıktan sonra Postaci sınıfını oluşturuyoruz. En üstte Property lerin veri taşıyıcısı olarak kullanılacak olan değişken tanımlamaları yapılıyor. Emailler (email adreslerini tutan dizi değişken), Mesaj (mailmessage örneği), SmtpServer (mail göndermek için kullanılacak sunucu adresi), UysunMu (threadin belli zamanlarda bekleyerek smtp sunucusunun nefes almasına izin vermesini sağlayan özellik), UykuLimiti (kaç gönderimde bir bekleme yapılacağı), UykuSuresi (uyku durumunda iken ne kadar bekleneceği, dakika cinsinden) özellikleri bu private değişkenleri taşıyıcı olarak kullanırlar.
Sınıf new ile türetildiğinde varsayılan ayarların atanması için yapılandırıcı (constructor) oluşturuluyor. Postala metodu ile gönderme işlemi başlatılacak. Burada yeni bir Thread oluşturuluyor, isBackground özelliği ile ana thread ile bağlantısız olarak arkaplanda çalışacağı belirleniyor ve Start ile uygulama start ediliyor. Burada oluşturulan Thread Postalayici metodunu çalıştırıyor.
Postalayici metodunu inceleyelim, işlemin ne kadar sürdüğünü belirlemek için baslangic değişkenine şu anki zaman bilgisi yazılıyor. _strEx, _countEx değişkenleri ise gönderme işlemi içerisindeki hata sayısının ve hataların görüntülenmesini sağlar. smptServer Property si ile gönderme işlemi için kullanılacak olan smtp sunucusu (smtpmail.smtpserver) belirleniyor. Ardından for döngüsü ile Emailler Propertysindeki email adresleri kadar döngü işlemi yapılıyor. Eğer UyusunMu değişkeni True ise ikinci if kontrolüne bakılır. İkinci kontrol ise döngünün ilk basamağında (i=0) olmamasına ve UykuLimitinin gelip gelmediğine bakar. Ardından mailmessage sınıfından türeyen mesaj nesnesinin to özelliği şuanda gönderim yapılacak olan kişiyi belirliyor. SmtpMail.Send ile de gönderim işlemi yapılıyor. Bütün gönderim işlemi Try-Catch içerisinde yapılır ve oluşan hatalar bir değişkende biriktirilir. Döngü işlemini bitirdikten sonra bu hata mesajları FROM adresi ile belirlenen kişiye gönderiliyor.
Imports System.Web.Mail Imports System.Threading Imports System.Text
Public Class Postaci Private _emailler() As String Private _mesaj As MailMessage Private _smtpServer As String Private _uyusunMu As Boolean Private _uykuLimiti As Integer Private _uykuSuresi As Integer Sub New() smtpServer = "localhost" UyusunMu = True UykuLimiti = 100 UykuSuresi = 1 End Sub Public Sub Postala() Dim th As Thread th = New Thread(AddressOf Postalayici) th.IsBackground = True th.Start() End Sub Private Sub Postalayici() Dim baslangic As DateTime baslangic = Now Dim _strEx As String = "" Dim _countEx As Integer = 0 SmtpMail.SmtpServer = smtpServer For i As Integer = 0 To Emailler.Length - 1 tüm emailler için döngü Try If UyusunMu Then If i <> 0 AndAlso i Mod UykuLimiti = 0 Then Thread.CurrentThread.Sleep(New TimeSpan(0, UykuSuresi, 0)) End If End If Mesaj.To = Emailler(i) SmtpMail.Send(Mesaj) Catch ex As Exception _countEx += 1 _strEx += ex.ToString & ControlChars.CrLf & ControlChars.CrLf End Try Next Dim strAdminMsg As New StringBuilder strAdminMsg.Append("Toplu mesaj gönderme işlemi başarılı olarak sonlandı!") strAdminMsg.Append(ControlChars.CrLf) strAdminMsg.Append("Email sayısı : " & Emailler.Length) strAdminMsg.Append(ControlChars.CrLf) strAdminMsg.Append("Başlangıç : " & baslangic.ToString) strAdminMsg.Append(ControlChars.CrLf) strAdminMsg.Append("Bitiş : " & Now.ToString) strAdminMsg.Append(ControlChars.CrLf) strAdminMsg.Append("Geçen Süre : " & DateDiff(DateInterval.Minute, baslangic, Now)) strAdminMsg.Append(ControlChars.CrLf) strAdminMsg.Append(ControlChars.CrLf) strAdminMsg.Append("Oluşan Hatalar : (" & _countEx.ToString & ")") strAdminMsg.Append(ControlChars.CrLf) strAdminMsg.Append(_strEx) SmtpMail.Send(Mesaj.From, Mesaj.From, "Gönderim İşlemi Tamamlandı!", strAdminMsg.ToString) End Sub Public Property Mesaj() As MailMessage Get Return _mesaj End Get Set(ByVal Value As MailMessage) _mesaj = Value End Set End Property Public Property Emailler() As String() Get Return _emailler End Get Set(ByVal Value() As String) _emailler = Value End Set End Property Public Property smtpServer() As String Get Return _smtpServer End Get Set(ByVal Value As String) _smtpServer = Value End Set End Property Public Property UyusunMu() As Boolean Get Return _uyusunMu End Get Set(ByVal Value As Boolean) _uyusunMu = Value End Set End Property Public Property UykuLimiti() As Integer Get Return _uykuLimiti End Get Set(ByVal Value As Integer) _uykuLimiti = Value End Set End Property Public Property UykuSuresi() As Integer Get Return _uykuSuresi End Get Set(ByVal Value As Integer) _uykuSuresi = Value End Set End Property End Class
Yukarıdaki sınıf tasarımını yaptıktan sonra bu sınıfı kullanmak için aşağıdaki gibi bir web form tasarlayınız. Kontrollerin isimleri sırasıyla txtFrom, txtSubject, txtBody, txtSmtpServer ve Button1 dir.
Şekilde görünen webform uygulamasının codebehind dosyasına aşağıdaki kodu ekleyiniz.
Private Function AdresleriAl() As String() Dim adresler(3) As String adresler(0) = "cengiz11@cengizhan.com" adresler(1) = "hancengiz11@hotmail.com" adresler(2) = "cengiz.han11@bigfoot.com" adresler(3) = "cengiz.han11@celiknet.com" Return adresler End Function
Yukarıdaki metod ile email adreslerini dizi değişken olarak geriye döndürüyoruz. Burada sadece dört adet email adresine gönderim yapıldığı için threadinging faydası göz ile görülecek şekilde anlaşılamayabilir. Bu metod ile kendi hazırladığınız bir veritabanında email listesini çekerek çok sayıda kişiye gönderim yapabilirsiniz.
Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click Dim m As New System.web.mail.MailMessage m.From = txtFrom.Text m.Subject = txtSubject.Text m.Body = txtBody.Text m.BodyFormat = Mail.MailFormat.Text m.BodyEncoding = System.Text.Encoding.GetEncoding(1254) Dim x As New Postaci x.smtpServer = txtSmtpServer.Text x.Mesaj = m x.Emailler = AdresleriAl() x.UykuLimiti = 50 50 postada bir bekle x.UykuSuresi = 2 dakika x.UyusunMu = True x.Postala() End Sub
Yukarıdaki kod ile butona tıkladığında gerekli parametreler ile yeni nesneler oluşturulup Postaci sınıfınfan türetilen x nesnesinin Postala metodu çağrılıyor. Bu metodu çağırarak yeni bit thread içerisinde ve arkaplan da email gönderimine başlanır.
Sayfa postback olup tekrar yüklenirken kullanıcı arabirimi normal biçimde görüntülenir. Sayfanın görsel unsurları mail gönderim işleminin bitmesini beklemez çünkü işlem arkaplanda yapılır. (isbackground) Bu durumda 50 bin kişiye email göndermek istediğiniz zaman işlemi başlatmanız yeterli olacaktır. Eğer uygulamayı uzaktaki bir web sunucusundan (hosting firmanızın web sunucusu olabilir) çalıştırıyorsanız, akşam iş yerinden çıkarken işlemi başlatıp, kendi bilgisayarınızı kapatıp gidebilirsiniz. Sabah geldiğinizde email adresinize durum raporunun maillendiğini göreceksiniz.
Bahsedilen şekilde bu uygulamanın tüm müşterilerinize, sitenizin üyelerine mail gönderir hale gelmesi için AdresleriAl metodunun veritabanı bağlantılı olarak tekrar düzenlenmesi gerekebilir.
Bu uygulama ASP.NET ile Threading konusunu anlatmayı amacıyla yazıldı. Daha esnek ve özelleştirilebilir bir uygulama tasarımı yapabilirsiniz. Örneğin mail mesajının içinde kişiye ismi ile hitap eden, kişiye özel bilgiler sunan bir uygulama yapabilirsiniz. Bunlar sizin uygulama tasarımınıza bağlı konular.
Ayrıca HTML formatında mail göndermek için m.BodyFormat = Mail.MailFormat.Html yapmalısınız. Bu durumda m.Body özelliğine ise içinde html etiketleri barındıran bir mesaj metni atamalısınız.
Bu makale ile gerçek hayatta bir çok kişinin ihtiyaç duyduğu bir konu üzerine değinmek istedim. Umarım makalenin birilerine faydası dokunur.
Örnek uygulamayı indirmek için tıklayınız. http://www.cengizhan.com/MailSender.zip
Cengiz HAN cengiz@cengizhan.com www.cengizhan.com