Makale Özeti

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...

Makale

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