eğitsel

Web Sitesi Performans İyileştirmeleri

Yakın zamanda telvee‘nin biraz daha hızlı yüklenebilmesi için django_compressor kullanarak ve arkaplan resimlerini düzenleyerek iyileştirmeler yaptım. İşin güzel yanı bunları gerçekleştirirken geliştirme ortamında veya tasarımda köklü değişiklikler yapmamın gerekmemesiydi. Detaylardan aşağıda bahsedeceğim. Daha önce konunun teorik kısmına kısaca değinmek istiyorum.

Takip ettiğim ve büyük zevkle okuduğum bloglardan biri Steve Souders’in High Performance Web Sites isimli blogu. İtiraf etmeliyim ki ilk okuduğumda pek aklıma yatmamıştı; yüklenecek resimleri birleştirmek, tarayıcıların kendilerine özgü yükleme davranışları… Bana erken iyileştirme gibi gelmişti. Fakat okumaya devam ettiğimde bu endişelerin yanlış olduğunu anladım. Steve Souders yüksek performanslı web siteleri konusunda bir uzman ve anlattıkları da çoğunlukla testlerle desteklenmiş, gerçekçi teknikler. Eğer takip etmiyorsanız, web uygulamaları geliştirmeseniz bile, RSS okuyucunuza eklemenizi öneririm.

İyileştirme Teknikleri

İyileştirme tekniklerini iki grupta toplayabiliriz:

  1. Yüklenecek verinin boyutunu azaltmaya yönelik teknikler.
    • Minifying: CSS ve JavaScript dosyalarında yorumları ve gerekmeyen boşlukları yok ederek dosya boyutunu azaltmaya dayanıyor. Hatta YUI Compressor ve Closure daha da ileri giderek JavaScript kodunda, işleyişi aynı kalmak kaydıyla, düzenlemeler yapabiliyor.
    • Gzip Sıkıştırma: Web tarayıcıları uzun zamandır gzip ile sıkıştırılmış içeriği kabul edebiliyor. Biraz önce, deneme amacıyla 13 KB’lık bir metin dosyasını sıkıştırdım, sonuç 4 KB oldu. Üçte ikilik sıkıştırma oranı hiç de fena değil.
  2. HTTP isteklerini azaltmaya yönelik teknikler.
    • Birleştirme: CSS ve JavaScript dosyaları, sıraları bozulmamak şartıyla, arka arkaya eklenerek tek bir dosya, dolayısıyla tek bir HTTP isteği haline getirilebilir. Birleştirilmiş dosyalarda gzip sıkıştırma da daha iyi sonuç verebilir. Benzer şekilde arkaplan resimleri de bir dosyada toplanıp, oluşturulan sprite üzerindeki koordinatları kullanarak istenen resim tekrar elde edilebilir.
    • Data URI’ler: Kullanılan resimleri (veya başka tür dosyaları), CSS (veya JavaScript veya HTML) içine gömmek için data: URI‘ler kullanılabilir. Böylece gömülü dosyalar için HTTP isteği yapılması gerekmez.

Tüm bu iyileştirmeleri yapmak güzel ama, JavaScript dosyalarımı birleştirip üzerine bir de minify ettikten sonra bir değişiklik yapmak istediğimde ne olacak diye düşünüyor olabilirsiniz. Yukarıda saydığım teknikleri körü körüne uygulamak yerine mantıklı bir yol izlemekte fayda var:

  • Her şeyden önce otomatikleştirilebilecek her şeyi otomatikleştirmek ilkesi geliyor. Yukarıdaki örnekten hareketle betik dosyaları ayrı ayrı, sıkıştırılmamış olarak düzenlenip uygulama yayınlanacağı zaman birleştirme ve sıkıştırma uygulanabilir. Hatta daha da ileri gidilerek yazılımın değişen dosyaları fark ederek gerekli işlemleri kendi kendine uygulaması sağlanabilir. (django_compressor böyle yapıyor)
  • Arkaplan resmi birleştirmede endişe ettiğim konu tasarımın düzenlenmesi aşamasında işlerin karışma olasılığıydı. Fakat sprite’ların hangi resimlerden oluşturulacağının dikkatlice seçilmesi durumunda, aksine düzenleme yapmanın daha da kolaylaşabileceğini gördüm. İşe aynı tasarım elemanına ait resimleri birleştirmeyle başlayın. Karmaşık yerleştirme düzenlerinden kaçının, mümkün olduğu kadar sadece yan yana veya alt alta yerleştirin. Resim aralarına ve sprite çevresine transparan boşluk koymayı ihmal etmeyin. Mümkün olduğu kadar aynı sayfada yüklenecek resimleri birleştirin, yarısı kullanılacak diye tüm bir sprite yüklenmesin. Tüm resimler sprite olsun diye zorlamayın, zaten bu saydığım noktalara dikkat ederseniz olmayacaktır.
  • İyileştirme yaparken elinizin altındaki araçları kullanmayı ihmal etmeyin. Genel analiz için YSlow‘u, resim birleştirme için SpriteMe‘yi, farklı iyileştirme programlarınının kendi betikleriniz üzerindeki başarısını karşılaştırmak için de CompressorRater‘i kullanabilirsiniz. İlk iki uygulamayı Steve Souders’ın geliştirdiğini de belirteyim.

Telvee Deneyi

Telvee için başlangıçta performansı hiç düşünmemiştim. Çok sayıda CSS dosyası ve yine çok sayıda resim yükleniyordu. İyileştirmelerden önce aldığım ölçümler aşağıdaki gibiydi:

 İstek #Veri boyutu (KB)
Anasayfa25~85
Fincan Detay48~80

Daha sonra projeye django_compressor‘u ekledim ve gerekli ayarlamaları yaptım. Hem JavaScript hem de CSS için YUI Compressor’u kullandım. Sprite’ları elle hazırlayıp, CSS dosyalarında gerekli değişiklikleri yaptım1. Daha sonra değişiklikleri sunucuya yükleyip tekrar ölçtüm:

 İstek #Veri boyutu (KB)
Anasayfa12~70 (~160)
Fincan Detay14~64

Anasayfanın veri boyutu kolonunda yazan parantez içindeki değer gerçek sonuç. Ancak bu güncellemeyle anasayfanın tasarımı da değişti ve daha önce yüklenmeyen 90 KB’lık bir dosya eklendi. Bu nedenle sonucu 70 KB olarak kabul etmek yanlış olmayacaktır. İyileştirmelerin sonucu aşağıdaki gibi:

 İstek #Veri boyutu (KB)
Anasayfa%52%17
Fincan Detay%70%19

Django_compressor ve Data URI’ler

Christian Metts tarafından geliştirilen django_compressor yukarıda bahsettiğim iyileştirmeleri Django projelerinize kolayca uygulamanızı sağlıyor. Birkaç değişik çatalı birleştirip ufak eklemeler yaptığım depoya buradan ulaşabilirsiniz.

Bu depoda data-uri branşındaki compressor.filters.datauri.CssDataUriFilter filtresini kullanarak bağlantılı dosyalarınızı CSS dosyalarınızın içine gömebilirsiniz. Varsayılan ayarlara göre sadece 1024 Byte (1 KB) veya daha küçük boyuttaki dosyalar gömülecek, diğerleri bağlantılı olarak kalacaktır. COMPRESS_DATA_URI_MIN_SIZE ayarını kullanarak gömülecek dosyaların sınırını değiştirebilirsiniz.

Dosyalarınızı data: URI‘lere dönüştürürken dikkat etmeniz gereken birkaç nokta var. Her şeyden önce dosya içeriği base64 kodlanıyor, yani veri boyutu yaklaşık üçte bir oranında artacaktır. Bant genişliği ile HTTP istek adedi arasındaki dengeyi düşünerek karar vermek size kalıyor2. Sorun yaratabilecek bir diğer şey; aynı dosyaya birden fazla kereler atıfta bulunulursa tüm dosya her seferinde tekrar tekrar gömülecektir. Bu sorunun çözümü tüm atıfları birleştirip tek bir bağlantıda toplamak3 ancak bunun da CSS dosyalarınızı tasnif ettiğiniz sistemi bozma ihtimali var.

Django_compressor’un Data: URI desteğini test edip, görüş ve önerilerinizi bana iletirseniz sevinirim. Eğer uygulamadıysanız yazıda belirttiğim teknikleri sitelerinize uygulamanızı tavsiye ederim. Özellikle, django_compressor sayesinde, Django projelerinde bu iyileştirmeleri yapmak çok basit. Sonuçta, teknoloji meraklıları olarak, performans artışının hoşunuza gideceğinden de eminim.


1: İleride django_compressor’a sprite’ları otomatik oluşturma özelliğini eklemeyi düşünüyorum.

2: Modern Internet bağlantıları söz konusu olduğunda 1~2 KB’lık artışın karşılığında HTTP isteklerini bir azaltmak daha avantajlı görünüyor.

3: http://meiert.com/en/blog/20090401/why-css-needs-no-variables/

Etiketler: , , , ,

Cuma, Şubat 19th, 2010 Internet Sektörü, Yazılım Yorumlar kapalı

Django: Bir Güncelleme Hikayesi

Internet üzerinde MVC web çerçeveleri kullanılarak sıfırdan uygulamalar geliştirmek üzerine bir çok makale var. Ben bu yazıda Django ile geliştirilmiş ve çevrimiçi olan bir siteye yeni özellik eklemeyi detaylı olarak anlatmak istiyorum. Amacım Django’nun mevcut uygulamalar üzerinde değişiklik yaparken sağladığı esneklik hakkında fikir vermek. Bir yandan da, küçük de olsa, gerçek hayatta kullanılan bir parça kodu göstermek.

Nakliyeci Rehberi, nakliye sektörüne hizmet veren bir firma rehberi. Ayrıca söktörel haberler ve ilanlara da yer veriliyor. Fakat ana özellik firma rehberi. Aşağıda anlatacağım güncellemede firmaların listelendiği sayfaların altına, arama motoru optimizasyonu için, açıklayıcı yazılar yerleştirilmek istendi. Bu yazıların dinamik olarak, sadece istenen şehirler için, eklenip çıkarılabilmesi gerekiyordu. Bir örnek için İstanbul Evden Eve Nakliyat Firmaları sayfasına bakabilirsiniz.

Plan basitçe iki maddede özetlenebilir:

  • Girdi: tanıtıcı yazı için bir model oluşturulması ve admin uygulamasıyla entegrasyonu.
  • Çıktı: bir şablon komutu (template tag) ile istenen tanıtıcı yazının çağrılması.

Model ve Admin

Modelimiz oldukça basit. Her bir şehir ve kategori için yalnızca bir tane tanıtım yazısı yaratılabilsin diye unique_together kullandım.

import datetime
from django.db import models


CATEGORY_CHOICES = (
    ('domestic', u'Evden Eve Nakliyat'),
    ('commercial', u'Nakliye ve Lojistik'),
)


class IntroductoryText(models.Model):
    city = models.CharField(u'Şehir', blank=False, unique=True)
    category = models.CharField(u'Kategori',
                                choices=CATEGORY_CHOICES,
                                max_length=20)
    introduction = models.TextField(u'Tanıtım Yazısı', blank=False)
    creation_timestamp = models.DateTimeField(u'Yaratılma Tarihi',
                                default=datetime.datetime.now, editable=False)
    class Meta:
        verbose_name = u'Şehir Tanıtımı'
        verbose_name_plural = u'Şehir Tanıtımları'
        unique_together = (('city', 'category'),)
        ordering = ('city',)

    def __unicode__(self):
        return u'%s %s tanıtımı' % (self.get_city_display(),
                                    self.get_category_display())

    @models.permalink
    def get_absolute_url(self):
        return ('catalogue-city-category',
                (),
                {'city_slug': slugify(self.get_city_display()),
                 'category_slug': slugify(self.get_category_display())})

Burada Türkçe karakterleri de destekleyen kendi slugify fonksiyonumu kullanıyorum.

Daha sonra admin.py içine IntroductoryText için gerekli tanımları ekledim:

from django.contrib import admin
from models import IntroductoryText


class IntroductoryTextAdmin(admin.ModelAdmin):
    list_display = ('city', 'category', 'creation_timestamp')
    list_filter= ('category', 'city')
    radio_fields = {'category': admin.VERTICAL}
admin.site.register(IntroductoryText, IntroductoryTextAdmin)

Böylelikle güncellemenin girdi kısmı tamamlandı.

Şablon Düzenlemeleri

Tanıtım yazılarını göstermek için çok baist bir şablon kumutu tanımlam yeterli oldu:

from django import template
from django.contrib.markup.templatetags.markup import markdown
from catalogue.models import Company, IntroductoryText


register = template.Library()


@register.simple_tag
def city_introduction(city_id, category_id):
    try:
        i = IntroductoryText.objects.get(city=city_id, category=category_id)
    except IntroductoryText.DoesNotExist:
        return u''
    return markdown(i.introduction)

Eğer verilen parametreler için tanıtım yazısı bulunamazsa, ki daha önce belirttiğim gibi her şehir için tanıtım yazısı olmayabilir, şablona boş metin döndürülüyor.

Tanıtım yazılarını görüntülemek için catalogue/company_list.html şablonuna aşağıdaki kodu ekledim:

<div>{% city_introduction city_id category_id %}</div>

Sonuç

Bu çok basit güncellemeyi mevcut kodlar üzerinde hiçbir değişiklik yapmadan sadece yeni kodları ekleyerek gerçekleştirdim. Bu yeni kodları sunucuya yükledikten sonra veritabanında IntroductoryText ile ilgili tablonun yaratılması için son olarak syncdb komutunu çalıştırdım.

./manage.py syncdb

Bu kadarı yeni özelliğin eklenmesi için yeterli olsa da önemli bir eksiklik var; en azından city_introduction şablon komutu için testlerin yazılması gerekiyor. Django ile geliştirme diğer antik geliştirme ortamlarına göre o kadar çevik ki emin olun testleri hazırlamak kazanılan zamanın çok ufak bir kısmını alacaktır.

Umarım bu yazı Django öğrenenlere veya öğrenmek isteyenlere yardımcı olur.

Etiketler: , ,

Çarşamba, Ekim 21st, 2009 Yazılım Yorumlar kapalı