Gereksinimler ve Ön Hazırlık
Bu projeyi hayata geçirmek için bilgisayarınızda Python 3.10 veya daha güncel bir sürümün yüklü olması gerekmektedir. İndirme işlemleri için Python'un standart kütüphanesi olan requests modülünü kullanacağız. Eğer sisteminizde yüklü değilse, terminal veya komut satırına pip install requests yazarak kurulumu gerçekleştirebilirsiniz.
Çalışma ortamınızı hazırlarken, bir sanal ortam (virtual environment) kullanmanız bağımlılıkları yönetmek açısından en iyi pratiktir. Proje dizininizde python -m venv venv komutu ile ortamınızı oluşturabilir ve source venv/bin/activate (Windows için venv\Scripts\activate) komutuyla aktif edebilirsiniz.
İş Parçacığı (Threading) Mantığını Anlamak
Threading, bir programın aynı anda birden fazla "iş parçacığı" çalıştırmasına olanak tanıyan bir eş zamanlılık (concurrency) modelidir. Python'da threading modülü, özellikle I/O (giriş/çıkış) odaklı işlemler olan dosya indirme süreçlerinde oldukça etkilidir. İşlemci yoğunluklu işlerin aksine, dosya indirme gibi ağ beklemeli süreçlerde threading, CPU'nun boşta kalmasını engeller.
import threading
import time
def basit_gorev(isim):
print(f"{isim} başladı.")
time.sleep(2)
print(f"{isim} bitti.")
# İş parçacığı oluşturma
t1 = threading.Thread(target=basit_gorev, args=("İş Parçacığı 1",))
t1.start()
t1.join()
Yukarıdaki örnekte, threading.Thread sınıfı ile bir iş parçacığı tanımladık. start() metodu işi başlatırken, join() metodu ana programın bu iş parçacığının bitmesini beklemesini sağlar.
Adım Adım Eş Zamanlı Dosya İndirici Tasarımı
Bir dosya indirici tasarlarken izleyeceğimiz temel mantık, indirilecek dosyaların URL listesini bir kuyruğa (queue) almak ve bu kuyruktaki öğeleri belirli sayıda iş parçacığına paylaştırmaktır. queue.Queue yapısı, iş parçacıkları arasında güvenli veri transferi sağlar.
import threading
import requests
from queue import Queue
def dosya_indir(url, kuyruk):
try:
response = requests.get(url)
dosya_adi = url.split('/')[-1]
with open(dosya_adi, 'wb') as f:
f.write(response.content)
print(f"{dosya_adi} başarıyla indirildi.")
except Exception as e:
print(f"Hata oluştu: {e}")
finally:
kuyruk.task_done()
Bu fonksiyon, tek bir URL'yi alır, indirir ve yerel diske kaydeder. kuyruk.task_done() çağrısı, işin tamamlandığını kuyruk yapısına bildirir.
İş Parçacıklarını Yönetme ve Kuyruk Kullanımı
Şimdi, birden fazla iş parçacığını yönetmek için bir "Worker" (çalışan) yapısı kuralım. Bu yapı, kuyruk boşalana kadar sürekli yeni görevler alacaktır.
def worker(kuyruk):
while True:
url = kuyruk.get()
if url is None:
break
dosya_indir(url, kuyruk)
# URL listesi ve Kuyruk
url_listesi = ["http://ornek.com/dosya1.zip", "http://ornek.com/dosya2.zip"]
kuyruk = Queue()
for url in url_listesi:
kuyruk.put(url)
Burada while True döngüsü, iş parçacığının sürekli canlı kalmasını sağlar. Kuyruktan gelen None değeri, iş parçacığına durması gerektiğini belirten bir sinyaldir.
Eş Zamanlı İndirme Performans Karşılaştırması
Aşağıdaki tablo, threading kullanmanın geleneksel yöntemlere göre avantajlarını özetlemektedir.
| Özellik | Tekil İndirme | Threading İle İndirme |
|---|---|---|
| Hız | Düşük | Yüksek (Eş zamanlılık) |
| CPU Kullanımı | Düşük | Orta |
| Karmaşıklık | Basit | Orta |
| Hata Yönetimi | Kolay | Dikkat gerektirir |
Güvenlik ve Hata Yönetimi Uyarıları
Kritik Uyarı: Ağ üzerinden dosya indirirken her zaman SSL sertifika doğrulaması yapın. Güvenilmeyen kaynaklardan gelen dosyaları doğrudan çalıştırmayın. Ayrıca, çok fazla iş parçacığı açmak sunucu tarafından engellenmenize (IP block) neden olabilir. Üretim ortamlarında
requestskütüphanesinintimeoutparametresini mutlaka kullanın.
Aşağıdaki örnekte, güvenli bir şekilde nasıl zaman aşımı (timeout) ekleyeceğinizi görebilirsiniz:
def guvenli_indir(url):
try:
# 10 saniye zaman aşımı
response = requests.get(url, timeout=10)
response.raise_for_status() # Hatalı HTTP kodlarını yakalar
# ... dosya yazma işlemleri ...
except requests.exceptions.RequestException as e:
print(f"Bağlantı hatası: {e}")
Sıkça Sorulan Sorular
Threading, Python'da neden CPU yoğun işlerde yavaş kalır?
Python'daki GIL (Global Interpreter Lock), aynı anda sadece bir iş parçacığının Python bayt kodunu çalıştırmasına izin verir. Bu nedenle CPU yoğun işlerde multiprocessing modülü tercih edilmelidir.
Kaç tane iş parçacığı (thread) açmalıyım?
Dosya indirme gibi I/O işlemlerinde sisteminizin kaynaklarına bağlı olarak genellikle 5 ile 20 arası iş parçacığı idealdir. Çok yüksek sayılar ağ tıkanıklığına yol açabilir.
İndirilen dosyaların bütünlüğünü nasıl kontrol ederim?
Dosyaları indirdikten sonra hashlib kütüphanesini kullanarak MD5 veya SHA-256 değerlerini hesaplayıp, sunucudaki değerlerle karşılaştırarak dosyanın bozulmadığından emin olabilirsiniz.
Hangi kütüphaneler threading yerine kullanılabilir?
Python 3.5+ ile gelen asyncio kütüphanesi, özellikle yüksek ölçekli ağ işlemleri için modern ve daha verimli bir alternatiftir.
Threading ile GUI (Arayüz) uygulaması yaparken nelere dikkat etmeliyim?
GUI uygulamalarında (Tkinter, PyQt vb.) arayüzü dondurmamak için indirme işlemlerini mutlaka arka plan iş parçacığında yapmalı ve arayüz güncellemelerini ana iş parçacığına (main thread) sinyal göndererek yapmalısınız.
İleri Seviye Optimizasyon: Bağlantı Havuzu (Connection Pooling) Kullanımı
Threading ile çoklu indirme yaparken her bir iş parçacığının yeni bir TCP bağlantısı açması, sunucu tarafında kaynak tüketimini artırır ve performans darboğazlarına yol açar. requests kütüphanesinin Session nesnesini kullanarak bağlantıları yeniden kullanmak, indirme hızınızı önemli ölçüde artıracaktır.
Aşağıdaki örnekte, bir ThreadPoolExecutor içerisinde paylaşılan bir Session nesnesinin nasıl kullanılacağını görebilirsiniz:
import requests
from concurrent.futures import ThreadPoolExecutor
def download_file(session, url):
try:
response = session.get(url, stream=True)
filename = url.split('/')[-1]
with open(filename, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
return f"Başarılı: {filename}"
except Exception as e:
return f"Hata: {e}"
urls = ["http://example.com/file1.zip", "http://example.com/file2.zip"]
with requests.Session() as session:
with ThreadPoolExecutor(max_workers=5) as executor:
results = [executor.submit(download_file, session, url) for url in urls]
for future in results:
print(future.result())
İndirme Süreçlerinde Hata Ayıklama ve Loglama
Eş zamanlı çalışan sistemlerde hataları takip etmek, tekil çalışan scriptlere göre daha zordur. Standart print fonksiyonu yerine Python'un yerleşik logging modülünü kullanmak, hangi iş parçacığının hangi dosyada hata aldığını zaman damgası ile görmenizi sağlar.
Thread-Safe Loglama Yapılandırması
Loglama yaparken her iş parçacığının aynı dosyaya yazması bir çakışmaya neden olmaz, çünkü logging modülü varsayılan olarak thread-safe (iş parçacığı güvenli) çalışır.
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(threadName)s] %(levelname)s: %(message)s',
filename='downloader.log'
)
def secure_download(url):
logging.info(f"İndirme başlatılıyor: {url}")
# İndirme mantığı buraya...
try:
# İndirme işlemi
logging.info(f"Tamamlandı: {url}")
except Exception as e:
logging.error(f"Hata oluştu {url}: {e}")
Performans İzleme Tablosu
İndirme süreçlerinizde darboğazları tespit etmek için aşağıdaki metrikleri takip etmeniz önerilir:
| Metrik | Açıklama | İdeal Durum |
|---|---|---|
| Thread Sayısı | Aktif çalışan iş parçacığı sayısı | CPU çekirdek sayısı * 2 |
| Chunk Size | Belleğe alınan veri parçası | 8KB - 64KB arası |
| Response Time | Sunucunun yanıt süresi | Düşük gecikme |
Bu metrikleri izleyerek, ağ bant genişliğinizin sınırlarını zorlamadan en verimli indirme hızına ulaşabilirsiniz. Özellikle çok sayıda küçük dosya indirirken chunk_size değerini küçük tutmak, bellek kullanımını optimize etmenize yardımcı olacaktır.
Sonuç
Python ile threading modülünü kullanarak eş zamanlı dosya indirici yapmak, ağ programlamanın temellerini anlamak için mükemmel bir projedir. Bu rehberde iş parçacığı oluşturmayı, kuyruk yapısı ile görev dağıtmayı ve temel hata yönetimini öğrendiniz. Bir sonraki adım olarak, asyncio kütüphanesini inceleyerek daha modern ve ölçeklenebilir asenkron indirme yapılarına geçiş yapabilirsiniz.
Yasal Sorumluluk Reddi: Bu makalede paylaşılan kod örnekleri eğitim amaçlıdır. Kullanıcı, indirdiği dosyaların telif haklarından ve güvenliğinden kendisi sorumludur. Yazılım geliştirme süreçlerinde etik kurallara ve hedef sunucunun kullanım şartlarına uyulması zorunludur.

Yorumlar (0)
Yorum Yaz