Spring REST API için metrik, dağıtık iz ve log korelasyonunu cardinality patlaması yaratmadan kurun; p95 gecikmeyi JFR ve Prometheus verisiyle önce-sonra karşılaştırın.
Spring REST API Gözlemlenebilirliği: Metrik, Trace ve Cardinality
Spring REST API için ölçülebilir bir başlangıç çizgisi kurmak
Bir spring rest api sorununu "yavaş" diye sınıflandırmak yerine, endpoint + HTTP metodu + durum kodu boyutlarında istek sayısı, hata oranı ve p95/p99 gecikme ölçün. Bu ayrım, örneğin yalnızca POST /orders çağrılarının 429 ürettiğini, fakat genel uygulama CPU’sunun normal olduğunu gösterebilir. Çalışanların eriştiği Actuator uçlarını ağ katmanında kısıtlayın; prometheus endpoint’ini uygulamanın genel güvenlik zincirine açık bırakmayın.
# application.yaml
management:
endpoints:
web:
exposure:
include: health,info,prometheus
endpoint:
health:
probes:
enabled: true
metrics:
distribution:
percentiles-histogram:
http.server.requests: true
slo:
http.server.requests: 50ms,100ms,250ms,500ms,1s,2s
Histogram SLO sınırları Prometheus tarafında http_server_requests_seconds_bucket bucket’larını üretir; yalnızca uygulama içinde hesaplanan percentile’lardan farklı olarak, birden fazla pod’un verisini toplayarak küme geneli p95 hesaplanabilir. PromQL’de son 10 dakikadaki p95 için şu sorguyu kullanın: histogram_quantile(0.95, sum by (le, uri, method) (rate(http_server_requests_seconds_bucket{uri!="UNKNOWN"}[10m]))). uri="UNKNOWN" artıyorsa, çoğu durumda route eşleşmeden önce hata oluşuyor veya framework’ün endpoint şablonunu çıkaramadığı bir filtre zinciri çalışıyordur.
Bir spring framework eğitimi ya da java spring eğitimi içinde metrik eklemek çoğu zaman anotasyon eklemek olarak anlatılır; üretimde kritik nokta ise etiket sözleşmesidir. Her servis için en fazla 20-40 operasyonel URI şablonu hedefleyin. /customers/{id} tek zaman serisi olmalı, /customers/7f3a... her müşteri için ayrı seri üretmemelidir.
Spring MVC metriklerinde cardinality patlamasını engellemek
Spring MVC uygulamasında route template yerine ham URI, kullanıcı kimliği, sipariş numarası veya exception mesajı tag olarak eklenirse Prometheus’un TSDB belleği ve sorgu maliyeti artar. Mekanizma basittir: her benzersiz tag kombinasyonu ayrı bir time series’tir; dakikada 10 bin farklı müşteri kimliği, tek bir sayaç için dahi hızla yüz binlerce seri doğurur. Bu nedenle business identifier’ları metric tag’i değil, trace attribute veya yapılandırılmış log alanı yapın.
import io.micrometer.core.instrument.config.MeterFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class MetricsCardinalityConfig {
@Bean
MeterFilter denyUnsafeHttpTags() {
return MeterFilter.deny(id ->
id.getName().equals("http.server.requests") &&
(id.getTag("customerId") != null || id.getTag("orderId") != null));
}
@Bean
MeterFilter capTenantValues() {
return MeterFilter.maximumAllowableTags(
"http.server.requests", "tenant", 50, MeterFilter.deny());
}
}
maximumAllowableTags bir emniyet kemeridir, veri modelinin yerine geçmez: 51. tenant geldiğinde ilgili meter reddedilir ve görünürlük kaybolur. Tenant bazlı SLA gerçekten gerekiyorsa, tenant’ları "paid", "free", "internal" gibi sınırlı bir sınıfa dönüştürün. Ham tenant kimliğini ise trace üzerinde tenant.id olarak tutun; trace depolarında süreli saklama ve örnekleme uygulanabilir.
Yaygın hata, hata sınıfını exception.message ile etiketlemektir. SQL constraint değerleri veya uzaktan dönen dinamik mesajlar sınırsız değer kümesi oluşturur. Bunun yerine exception için sınıf adı, HTTP metriği için status ve iş hatası için sabit kodlar kullanın: ORDER_ALREADY_CANCELLED, PAYMENT_DECLINED. Bir spring boot eğitimi veya spring boot kursu laboratuvarında bunu doğrulamak için curl -s localhost:8080/actuator/prometheus | grep http_server_requests çıktısındaki seri sayısını dağıtımdan önce sayın.
Trace, log ve Spring Security bağlamını aynı istekte birleştirmek
Gecikme metriği hangi endpoint’in sorunlu olduğunu söyler; dağıtık trace ise sürenin JDBC, uzak HTTP çağrısı veya kuyruk beklemesinde mi harcandığını gösterir. Micrometer Tracing ile W3C traceparent başlığı gelen istekte varsa devam ettirilir, yoksa yeni trace oluşturulur. Zipkin uyumlu bir toplayıcı kullanılıyorsa aşağıdaki yapılandırma, trace’lerin tamamını değil kontrollü bir oranını gönderecek şekilde örnekleme yapar.
# application.yaml
management:
tracing:
sampling:
probability: 0.10
zipkin:
tracing:
endpoint: http://tempo-gateway.observability:9411/api/v2/spans
logging:
pattern:
level: "%5p [trace=%X{traceId:-},span=%X{spanId:-}]"
Sabit yüzdeyle örnekleme, nadir fakat pahalı hataları kaçırabilir. Ödeme reddi veya 5xx gibi olaylarda trace ID’yi response header ve JSON log’a her zaman yazın; ardından logdan trace deposuna geçin. Ayrıca PII içeren Authorization, oturum çerezi, tam istek gövdesi ve e-posta adresini span attribute olarak eklemeyin. Trace backend’leri çoğunlukla uygulama loglarından daha geniş erişim grubuna ve daha uzun saklama süresine sahiptir.
Spring Security ile kimlik doğrulanmış kullanıcıyı izlemek gerektiğinde kullanıcı adını tag yapmak yerine sabit yetki sınıfını ekleyin. Örneğin ROLE_ADMIN ve ROLE_CUSTOMER sınırlı bir kümedir; authentication.getName() sınırsızdır. Async bir executor kullanılıyorsa MDC ve SecurityContext’in worker threade kopyalanmadığını ayrıca doğrulayın; Spring’in DelegatingSecurityContextAsyncTaskExecutor sarmalayıcısı güvenlik bağlamını taşır, fakat özel MDC alanları için bir TaskDecorator gerekir. Trace bağlamının kaybolup kaybolmadığını, parent span altında yeni bir child span oluşup oluşmadığını Tempo veya Zipkin arayüzünde kontrol edin.
Microservices mimarisi içinde Spring Cloud çağrı zincirini teşhis etmek
Microservices mimarisinde uçtan uca p95, servis p95’lerinin toplamı değildir: paralel çağrılar, retry’lar, connection-pool kuyruğu ve gateway filtreleri toplam süreyi değiştirir. spring cloud tabanlı bir gateway’de önce route ID’yi metriğe ekleyin; servis instance ID veya pod UID eklemeyin. Instance bazlı sorunları Kubernetes label’ları ve log alanlarıyla araştırmak, HTTP metric cardinality’sini sabit tutar.
spring:
cloud:
gateway:
routes:
- id: catalog
uri: http://catalog.default.svc.cluster.local:8080
predicates:
- Path=/api/catalog/**
filters:
- StripPrefix=1
- name: Retry
args:
retries: 1
methods: GET
statuses: BAD_GATEWAY,GATEWAY_TIMEOUT
Retry yalnızca idempotent isteklerde ve dar bir hata kümesinde etkinleştirilmelidir. Yukarıdaki örnekte GET ile sınırlandırılmasının nedeni budur: POST /payments için ağ zaman aşımı, sunucunun isteği hiç işlemediğini kanıtlamaz; aynı isteğin tekrarı çift tahsilata yol açabilir. Retry etkinleştirildikten sonra gateway’deki istek sayısı ile downstream servisindeki istek sayısını karşılaştırın. Downstream RPS’in gateway RPS’ini beklenmedik biçimde aşması retry fırtınasına işaret eder.
Gateway’nin gelen traceparent başlığını downstream’e iletip iletmediğini somut olarak test edin: curl -H 'traceparent: 00-0123456789abcdef0123456789abcdef-0123456789abcdef-01' https://api.example.test/api/catalog/items. Downstream logunda aynı trace ID görünmüyorsa, özel bir HTTP client interceptor’ı header’ı siliyor veya gateway filtresi bağlam dışında çalışıyordur. Bu test, yalnızca dashboard’daki gecikme eğrisinden çıkarılamayan bir propagasyon hatasını doğrudan yakalar.
JFR ile p95 regresyonunu önce-sonra doğrulamak
Bir optimizasyon değişikliğini kabul etmeden önce aynı trafik profiliyle iki ölçüm alın: sürüm A ve sürüm B için istek hacmi, hata oranı, p50/p95/p99 ve JVM profili birlikte kaydedilmelidir. Sadece ortalama gecikmenin düşmesi yeterli değildir; örneğin connection pool beklemesi az sayıda isteği 10 saniye bekletirken ortalamada görünmeyebilir. Yük testi sırasında JVM Flight Recorder başlatın: jcmd <pid> JFR.start name=api-baseline settings=profile duration=5m filename=/tmp/api-baseline.jfr. Kaydı JDK Mission Control ile açıp Java Application > Threads ve Socket Read olaylarında bekleme sürelerini inceleyin.
Örneğin JFR’da request worker’larının önemli bölümünün HTTP client connection acquisition sırasında beklediğini gördüyseniz, havuz sınırını rastgele büyütmek yerine eşzamanlı downstream çağrı üst sınırını ölçün. Apache HttpClient 5 kullanan bir RestClient yapılandırmasında route başına ve toplam bağlantı sayısını açıkça belirleyin; sonra aynı yükte pool wait olaylarının ve p95’in değişimini karşılaştırın.
var cm = PoolingHttpClientConnectionManagerBuilder.create()
.setMaxConnTotal(200)
.setMaxConnPerRoute(80)
.build();
var client = HttpClients.custom()
.setConnectionManager(cm)
.evictExpiredConnections()
.build();
var requestFactory = new HttpComponentsClientHttpRequestFactory(client);
var restClient = RestClient.builder().requestFactory(requestFactory).build();
Önce-sonra kaydında test koşullarını sabitleyin: aynı container CPU limiti, aynı downstream stub gecikmesi, aynı warm-up süresi ve aynı test verisi. Örneğin wrk -t8 -c64 -d180s --latency http://localhost:8080/catalog/42 çalıştırın; ilk 30 saniyeyi warm-up kabul edip Prometheus’tan sonraki 150 saniyeyi sorgulayın. B sürümünde p95 420 ms’den 180 ms’ye inse ama 5xx oranı %0,1’den %2’ye çıksa değişiklik başarılı değildir. Bu disiplin, eğitim ortamındaki spring boot eğitimi örneklerini üretim kararına dönüştüren farktır.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Java Spring Boot ReactJS FullStack Eğitimi (Yıldız Teknik Üniversitesi SEM)
Sık Sorulan Sorular
Spring REST API metriklerinde URI cardinality nasıl kontrol edilir?
Ham path, müşteri ID’si ve sipariş ID’sini tag olarak eklemeyin. Spring MVC’nin route template’ini kullanın; ek tag gerekiyorsa değeri sınırlı bir sınıfa dönüştürün. Son savunma olarak Micrometer’da MeterFilter.maximumAllowableTags("http.server.requests", "tenant", 50, MeterFilter.deny()) tanımlayın ve Prometheus çıktısındaki seri sayısını dağıtım öncesinde ölçün.
Spring Cloud Gateway retry ayarı microservices mimarisinde neden risklidir?
Gateway timeout’u downstream işleminin gerçekleşmediğini kanıtlamaz. Bu yüzden retry’ı GET gibi idempotent metodlarla ve BAD_GATEWAY,GATEWAY_TIMEOUT gibi dar durumlarla sınırlandırın. Gateway RPS ile downstream RPS oranını izleyin; oran retry sayısından fazla büyüyorsa bir retry döngüsü veya tekrar eden istemci çağrısı vardır.
Spring Security kullanıcı bilgisini trace ve metriğe eklemek doğru mu?
Kullanıcı adı, e-posta veya JWT subject metriğe eklenmemelidir; her değer yeni time series üretir ve PII sızıntısı yaratır. Metrikte yalnızca ROLE_ADMIN gibi sınırlı role sınıfları kullanın. Olay incelemesi gerekiyorsa, erişimi ve saklama süresi yönetilen log sisteminde pseudonymous bir kullanıcı anahtarı tutun.
Spring Boot kursu projelerinde p95 gecikme nasıl ölçülmeli?
Actuator Prometheus endpoint’inde http.server.requests histogramını açın, ardından histogram_quantile(0.95, sum by (le, uri) (rate(http_server_requests_seconds_bucket[10m]))) ile p95’i hesaplayın. Kod değişikliğinden önce ve sonra aynı wrk komutunu, CPU limitini ve warm-up süresini kullanın; JFR kaydıyla gecikmenin CPU, lock, JDBC veya socket beklemesinden hangisinden kaynaklandığını doğrulayın.
AI / LLM Discovery
Bu makale Opendart Akademi Spring Framework eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.


