Windows’ta IIS Performans İzleme ve Optimizasyon
Yıllar içinde onlarca IIS sunucusu yönetmiş biri olarak şunu rahatlıkla söyleyebilirim: IIS performans sorunlarının büyük çoğunluğu ani gelmez. Sinsi sinsi birikir. Bir gün sabahın dördünde telefon çalar, “site çalışmıyor” duyarsın ve o an geriye dönüp bakarsın ki aslında haftalardır tüm işaretler ortadaydı. Bu yüzden izleme ve optimizasyon benim için reaktif bir iş değil, proaktif bir alışkanlık haline geldi.
Bu yazıda Windows üzerinde IIS performansını hem izlemek hem de optimize etmek için kullandığım yöntemleri paylaşacağım. Teori değil, gerçek hayatta işe yarayan şeyler.
IIS Performans İzlemenin Temelleri
IIS’i izlemenin birden fazla katmanı var. Sadece “site açılıyor mu açılmıyor mu” düzeyinde bakmak yetmez. HTTP durum kodları, uygulama havuzu sağlığı, bellek kullanımı, thread sayısı, istek kuyruğu uzunluğu… Bunların hepsini birlikte değerlendirmen gerekiyor.
Windows’un yerleşik araçları bu iş için oldukça yeterli. Ek bir şey kurmadan bile çok şey yapabilirsin. Ama doğru metrikleri takip etmiyorsan, elindeki araçların önemi yok.
Performance Monitor ile Temel Metrikleri İzleme
perfmon.exe hâlâ en güçlü araçlardan biri. GUI üzerinden da kullanabilirsin ama ben genellikle PowerShell ile otomatize etmeyi tercih ederim.
IIS’e özel izlemek istediğin başlıca sayaçlar şunlar:
- Web ServiceCurrent Connections: Anlık bağlantı sayısı
- Web ServiceTotal Method Requests/sec: Saniyedeki toplam istek sayısı
- ASP.NETRequests Queued: Sırada bekleyen istek sayısı (bu kritik!)
- ASP.NETRequest Execution Time: İstek işleme süresi
- W3WPPrivate Bytes: Worker process bellek kullanımı
- W3WP% Processor Time: CPU kullanımı
Bu sayaçları otomatik toplayıp bir dosyaya yazmak için şunu kullanabilirsin:
# Performance counter verilerini 60 saniye boyunca topla
logman create counter IIS_Monitor -cf "C:Monitoringcounters.txt" -f csv -si 5 -o "C:Monitoringiis_perf.csv" -max 50
# Başlat
logman start IIS_Monitor
# 60 saniye sonra durdur
Start-Sleep -Seconds 60
logman stop IIS_Monitor
counters.txt dosyasının içeriği şöyle olmalı:
Web Service(_Total)Current Connections
Web Service(_Total)Get Requests/sec
ASP.NETRequests Queued
ASP.NETRequest Execution Time
Process(w3wp*)Private Bytes
Process(w3wp*)% Processor Time
IIS Loglarını Düzgün Analiz Etmek
IIS logları varsayılan olarak C:inetpublogsLogFiles altında tutuluyor. Ama çoğu yönetici bu loglara sadece bir şeyler patladığında bakıyor. Oysa düzenli analiz çok şey anlatıyor.
Log formatını W3C Extended olarak tutuyorsun umarım. Değilse hemen değiştir. Şu alanların loglarda olduğundan emin ol:
time-taken: Her isteğin ne kadar sürdüğü (ms cinsinden)sc-bytesvecs-bytes: Transfer edilen veri miktarıcs-uri-stemvecs-uri-query: Hangi URL’e istek geldiğisc-statusvesc-substatus: HTTP durum kodları
PowerShell ile logları analiz etmek için basit ama etkili bir yaklaşım:
# Son 1 saatin loglarını analiz et, 5 saniyeden uzun süren istekleri bul
$logPath = "C:inetpublogsLogFilesW3SVC1"
$latestLog = Get-ChildItem $logPath -Filter "*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
Get-Content $latestLog.FullName | Where-Object { $_ -notlike "#*" } | ForEach-Object {
$fields = $_ -split " "
if ($fields.Count -ge 16) {
$timeTaken = [int]$fields[14]
$uri = $fields[4]
$status = $fields[11]
if ($timeTaken -gt 5000) {
[PSCustomObject]@{
URI = $uri
Status = $status
TimeTaken_ms = $timeTaken
}
}
}
} | Sort-Object TimeTaken_ms -Descending | Select-Object -First 20 | Format-Table -AutoSize
Bu script sana o günkü en yavaş 20 isteği verecek. Hangi endpoint’in sorun çıkardığını bir bakışta görürsün.
Uygulama Havuzu Optimizasyonu
Uygulama havuzları IIS’in kalbinde yer alıyor. Yanlış yapılandırılmış bir uygulama havuzu, iyi yazılmış bir uygulamayı bile yere serer.
Recycle Ayarlarını Optimize Etme
Varsayılan olarak IIS uygulama havuzlarını her 1740 dakikada (29 saat) bir recycle eder. Bu değer çoğu ortam için mantıklı olmayabilir. Bellek sızıntısı olan bir uygulaman varsa daha sık recycle gerekir. Sağlıklı bir uygulaman varsa gereksiz yere recycle yapıp downtime yaratmak istemezsin.
# Belirli bir uygulama havuzunun recycle ayarlarını düzenle
Import-Module WebAdministration
$appPoolName = "DefaultAppPool"
# Zaman bazlı recycle'ı kapat
Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$appPoolName']/recycling/periodicRestart/@time" -Value "00:00:00"
# Bellek bazlı recycle koy: 512 MB'yi geçince recycle et
Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$appPoolName']/recycling/periodicRestart/@privateMemory" -Value 524288
# Gece 03:00'te scheduled recycle
$restartSchedule = @{
value = "03:00:00"
}
Add-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$appPoolName']/recycling/periodicRestart/schedule" -Value $restartSchedule
Recycle olaylarını loglamayı da unutma. Event Viewer’da “Windows Logs > System” altında IIS-W3SVC kaynaklı olayları görebilirsin ama daha kolay izlemek için:
# Son recycle olaylarını çek
Get-EventLog -LogName System -Source "WAS" -Newest 20 |
Where-Object { $_.EventID -eq 5074 -or $_.EventID -eq 5075 -or $_.EventID -eq 5076 } |
Select-Object TimeGenerated, EventID, Message | Format-List
İdling ve Process Modeli Ayarları
Varsayılan olarak IIS, 20 dakika istek almayan uygulama havuzunu kapatır. Düşük trafikli ama kritik bir uygulaman varsa bu ciddi sorun. İlk istek geldiğinde “cold start” yaşanır ve kullanıcı uzun bir beklemeyle karşılaşır.
# Idle timeout'u kapat
Set-WebConfigurationProperty -Filter "/system.applicationHost/applicationPools/add[@name='$appPoolName']/processModel" -Name "idleTimeout" -Value "00:00:00"
# Maksimum worker process sayısını artır (Web Garden)
Set-WebConfigurationProperty -Filter "/system.applicationHost/applicationPools/add[@name='$appPoolName']" -Name "maxProcesses" -Value 4
# Startup time limit artır (yavaş başlayan uygulamalar için)
Set-WebConfigurationProperty -Filter "/system.applicationHost/applicationPools/add[@name='$appPoolName']/processModel" -Name "startupTimeLimit" -Value 120
maxProcesses değerini artırmak (Web Garden), çok çekirdekli sunucularda paralel işlem kapasitesini artırır. Ama dikkat: Session state’i InProc kullanıyorsan Web Garden ile sticky session problemi yaşarsın. Bu durumda SQL veya Redis tabanlı session’a geçmen gerekir.
HTTP Sıkıştırma ve Önbellekleme
Bu iki konu performans iyileştirmesinde en hızlı geri dönüşü sağlayan ayarlar. Yapılandırması dakikalar alır, etkisi anında hissedilir.
Statik ve Dinamik Sıkıştırma
# Sıkıştırma durumunu kontrol et
Get-WebConfigurationProperty -Filter "system.webServer/httpCompression" -PSPath "IIS:" -Name "staticCompressionEnabled"
Get-WebConfigurationProperty -Filter "system.webServer/httpCompression" -PSPath "IIS:" -Name "dynamicCompressionEnabled"
# Aktif et
Set-WebConfigurationProperty -Filter "system.webServer/httpCompression" -PSPath "IIS:" -Name "staticCompressionEnabled" -Value $true
Set-WebConfigurationProperty -Filter "system.webServer/httpCompression" -PSPath "IIS:" -Name "dynamicCompressionEnabled" -Value $true
# Sıkıştırma dizinini ve disk limitini ayarla
Set-WebConfigurationProperty -Filter "system.webServer/httpCompression" -PSPath "IIS:" -Name "directory" -Value "C:inetpubtempIIS Temporary Compressed Files"
Set-WebConfigurationProperty -Filter "system.webServer/httpCompression" -PSPath "IIS:" -Name "maxDiskSpaceUsage" -Value 100
Dinamik sıkıştırma CPU’ya yük bindiriyor. Eğer sunucun zaten CPU bound bir durumda ise dinamik sıkıştırmayı kapat, sadece statik dosyalar için sıkıştırmayı açık tut.
Output Caching
Output caching doğru kullanıldığında sunucuyu neredeyse “kandırıyor” gibi düşünebilirsin. Aynı içeriği tekrar tekrar üretmek yerine önbellekten sunuyor.
# web.config üzerinden output caching kuralı ekle
$site = "Default Web Site"
$app = "/"
Add-WebConfigurationProperty -Filter "system.webServer/caching/profiles" -PSPath "IIS:Sites$site$app" -Name "." -Value @{
extension = ".aspx"
policy = "CacheUntilChange"
kernelCachePolicy = "DontCache"
duration = "00:01:00"
location = "Any"
varyByQueryString = "*"
}
Gerçek Hayat Senaryosu: Bellek Sızıntısı Tespiti
Geçen yıl bir müşterinin e-ticaret sitesinde ilginç bir sorun yaşadık. Sunucu her sabah yavaşlıyor, öğleden sonra neredeyse kullanılamaz hale geliyordu. Gece yarısı recycle olunca sorun geçiyor, sabah tekrar başlıyordu. Klasik bellek sızıntısı belirtileri.
Önce w3wp.exe process’lerinin bellek tüketimini izledik:
# Tüm w3wp process'lerinin bellek kullanımını göster
Get-Process w3wp | Select-Object Id,
@{N="AppPool";E={(Get-WmiObject Win32_Process -Filter "ProcessId=$($_.Id)").CommandLine}},
@{N="WorkingSet_MB";E={[math]::Round($_.WorkingSet64/1MB,2)}},
@{N="PrivateMemory_MB";E={[math]::Round($_.PrivateMemorySize64/1MB,2)}},
CPU |
Sort-Object PrivateMemory_MB -Descending |
Format-Table -AutoSize
Sonuç net ortaya çıktı: Belirli bir uygulama havuzu her saat yaklaşık 150 MB büyüyordu. Procdump ile memory dump alıp WinDbg ile inceledikten sonra sorunun büyük object heap’inde biriken önbelleklenmemiş veri olduğunu tespit ettik.
Acil çözüm olarak o uygulama havuzuna bellek bazlı recycle ekledik:
# 400 MB'yi geçince otomatik recycle
$appPoolName = "ECommercePool"
Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$appPoolName']/recycling/periodicRestart/@privateMemory" -Value 409600
# Recycle öncesi uyarı logla
Set-WebConfigurationProperty -Filter "/system.applicationHost/applicationPools/add[@name='$appPoolName']/recycling" -Name "logEventOnRecycle" -Value "Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory"
Bu yaklaşım semptomları giderdi, hastalığı değil. Asıl düzeltme kod tarafında yapıldı. Ama prod ortamda acil müdahale gereken durumlarda bu tür geçici önlemler hayat kurtarır.
ARR ve Load Balancing Senaryolarında İzleme
Eğer önünde Application Request Routing (ARR) varsa izleme katmanı daha karmaşık hale geliyor. Her backend sunucusunun sağlığını ayrı ayrı takip etmen gerekiyor.
# ARR sağlık durumunu PowerShell ile sorgula
Import-Module WebAdministration
# Server Farm'daki sunucuların durumunu listele
Get-WebConfiguration "system.webServer/proxy/serverFarm/server" |
Select-Object address, enabled,
@{N="State";E={$_.healthCheck.state}} |
Format-Table -AutoSize
# Belirli bir backend'in response time geçmişini logla
$farmName = "BackendFarm"
$logFile = "C:Monitoringarr_health_$(Get-Date -Format 'yyyyMMdd').log"
while ($true) {
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$servers = Get-WebConfiguration "system.webServer/proxy/serverFarm[@name='$farmName']/server"
foreach ($server in $servers) {
"$timestamp | $($server.address) | $($server.enabled)" |
Add-Content -Path $logFile
}
Start-Sleep -Seconds 30
}
Scheduled Task ile Otomatik Performans Raporu
Elle bakmanın sınırları var. Bu yüzden günlük otomatik rapor oluşturan bir script kullanıyorum. Sabah işe geldiğinde mailbox’ında dünün özeti seni bekliyor olsun.
# Günlük IIS sağlık raporu scripti
# C:ScriptsIIS-DailyReport.ps1
$reportDate = (Get-Date).AddDays(-1).ToString("yyyy-MM-dd")
$logBase = "C:inetpublogsLogFilesW3SVC1"
$reportFile = "C:ReportsIIS_Report_$reportDate.txt"
# W3WP bellek durumu
$w3wpStats = Get-Process w3wp -ErrorAction SilentlyContinue |
Measure-Object WorkingSet64 -Sum -Average
# Application Pool durumları
$appPools = Get-WebConfiguration "system.applicationHost/applicationPools/add" |
Select-Object name, state, managedRuntimeVersion
# Dün ki log dosyalarını bul
$yesterdayLogs = Get-ChildItem $logBase -Filter "*.log" |
Where-Object { $_.LastWriteTime.Date -eq (Get-Date).AddDays(-1).Date }
# 5xx hata sayısı
$errorCount = 0
$slowRequests = 0
foreach ($log in $yesterdayLogs) {
$content = Get-Content $log.FullName | Where-Object { $_ -notlike "#*" }
$errorCount += ($content | Where-Object { ($_ -split " ")[11] -match "^5" }).Count
$slowRequests += ($content | Where-Object {
$fields = $_ -split " "
$fields.Count -ge 15 -and [int]$fields[14] -gt 3000
}).Count
}
$report = @"
IIS Gunluk Saglik Raporu - $reportDate
==========================================
Toplam 5xx Hata: $errorCount
3 Saniyeden Uzun Istek: $slowRequests
W3WP Toplam Bellek (MB): $([math]::Round($w3wpStats.Sum/1MB, 2))
W3WP Ortalama Bellek (MB): $([math]::Round($w3wpStats.Average/1MB, 2))
Uygulama Havuzu Durumları:
$($appPools | ForEach-Object { " - $($_.name): $($_.state) (.NET $($_.managedRuntimeVersion))" } | Out-String)
"@
$report | Out-File -FilePath $reportFile -Encoding UTF8
# Mail gönder (SMTP ayarlarını kendi ortamına göre düzenle)
Send-MailMessage -From "[email protected]" -To "[email protected]" `
-Subject "IIS Gunluk Rapor - $reportDate" `
-Body $report `
-SmtpServer "smtp.sirket.com"
Write-Host "Rapor olusturuldu: $reportFile"
Bu scripti Task Scheduler’a sabah 07:00 için ekle, her sabah hazır bir rapor seni beklesin.
Failed Request Tracing ile Derinlemesine Analiz
Performans sorunlarını IIS loglarıyla tam olarak tespit edemediğinde Failed Request Tracing (FREB) devreye giriyor. Bu özellik, yavaş veya hatalı isteklerin tam yaşam döngüsünü kaydediyor.
IIS Manager üzerinden “Failed Request Tracing Rules” ayarını yapabileceğin gibi PowerShell ile de yapılandırabilirsin:
# 2 saniyeden uzun süren tüm istekler için FREB aktif et
$site = "Default Web Site"
# FREB'i etkinleştir
Set-WebConfigurationProperty -Filter "system.applicationHost/sites/site[@name='$site']/traceFailedRequestsLogging" `
-Name "enabled" -Value $true
Set-WebConfigurationProperty -Filter "system.applicationHost/sites/site[@name='$site']/traceFailedRequestsLogging" `
-Name "directory" -Value "C:inetpublogsFailedReqLogFiles"
Set-WebConfigurationProperty -Filter "system.applicationHost/sites/site[@name='$site']/traceFailedRequestsLogging" `
-Name "maxLogFiles" -Value 50
# 2000ms üzeri tüm istekleri yakala
Add-WebConfigurationProperty `
-Filter "system.webServer/tracing/traceFailedRequests" `
-PSPath "IIS:Sites$site" `
-Name "." `
-Value @{
path = "*"
timeTaken = "00:00:02"
}
FREB dosyaları XML formatında geliyor ve browser’da açıldığında güzel bir arayüzle sunuluyor. Hangi modülün, hangi handler’ın ne kadar süre tükettiğini tam olarak görebiliyorsun.
Sonuç
IIS performans yönetimi tek seferlik bir iş değil. Kuruyorsun, izliyorsun, optimize ediyorsun, tekrar baştan. Bu döngünün sağlıklı işlemesi için birkaç şeyin yerli yerinde olması gerekiyor.
Önce taban çizgisi belirle. Normal yükte metrikler ne? Bunu bilmeden “bu değer yüksek mi değil mi” sorusuna cevap veremezsin. Perfmon ile düzenli veri topla, zaman içindeki trendlere bak.
Sonra log analizini otomatize et. Elle log okumak yorucu ve atlama yapar. Yukarıdaki gibi scriptler yaz, günlük raporları otomatikleştir. Anormallik kendiliğinden seni bulacak.
Uygulama havuzu ayarlarını her uygulama için ayrı ayrı düşün. Her site aynı ayarlarla çalışmak zorunda değil. Bellek sızıntısı olan bir uygulama için agresif recycle, sağlıklı bir uygulama için minimal müdahale politikası uygula.
FREB ve performance counters’ı sadece sorun çıkınca hatırlama. Proaktif kullanım, sorunları büyümeden yakalamayı sağlar. Gece dörtte telefon almak yerine gündüz sakin bir ortamda sorunu çözmek çok daha iyidir. Bunu yeterince yaşadım, inan ki haklıyım.
