Spring Boot tabanlı Java backend geliştirme için adaptif backpressure, thread pool tuningi ve blocking call tespitine odaklı, pratik adımlar ve ölçülebilir profilleme örnekleri içerir. (java eğitimi, spring boot eğitimi)
Spring Boot'ta Adaptif Backpressure ve Thread Pool Tuning: Java backend
Backpressure neden önemli: Reactor Netty, blocking call tespiti ve ölçüm
Reactor Netty tabanlı Spring WebFlux uygulamalarında event loop'larda blocking IO olması tüm isteklere gecikme olarak yansır; tespitin ilk adımı JFR ile 60s örnek kaydı almak ve eventloop thread stacklerindeki blocking çağrıları kontrol etmektir. Komut örneği:
jcmd JFR.start name=webflux,duration=60s,filename=webflux.jfr Bu JFR dosyasını Java Mission Control veya jfr-flame-graph aracı ile açın ve 'park' veya 'socketRead' gibi blocking göstergeleri arayın.Neden: Reactor Netty event loopları tek iş parçacığı kümesi üzerinde çalışır; blocking çağrı event loop'u bekletir ve downstream backpressure veya TCP kuyruğunda head-of-line blocking oluşturur. Bunu çözmek için blocking kodu Schedulers.boundedElastic() veya dedicated worker pool ile izole edin ve performans farkını Prometheus p95 latency sorgusuyla ölçün:
histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket[5m])) by (le, uri)) Örnek önce-sonra karşılaştırması için JFR snapshot, async-profiler CPU flamegraph ve Prometheus p95 kullanın.ThreadPoolTaskExecutor ile Java backend thread pool tuning: neden SynchronousQueue veya bounded queue?
Spring MVC veya blocking Spring Web uygulamalarında default task executor'unuzun kuyruğu sınırsızsa thread starvation gizlenir; bunun mekanizması ThreadPoolExecutor'un queue kabulü sayesinde core thread sayısı artmaz ve istekler kuyruğa girer. Somut düzeltme için bir ThreadPoolTaskExecutor bean'i ekleyin:
@Bean(name = "httpTaskExecutor")
public ThreadPoolTaskExecutor httpTaskExecutor() {
ThreadPoolTaskExecutor ex = new ThreadPoolTaskExecutor();
ex.setCorePoolSize(20);
ex.setMaxPoolSize(80);
ex.setQueueCapacity(1000); // bounded queue tercih edin
ex.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
ex.initialize();
return ex;
} Değişiklikten sonra jstat ve jcmd ile thread sayısını doğrulayın: jcmd <pid> Thread.print | grep httpTaskExecutor -A3Neden: SynchronousQueue kullanılması durumunda her task için doğrudan worker thread gerekir, bu da latency spike'larında istek reddine yol açar ama uçta kuyruğa gömülmeyi önler. Tipik bir değişiklik stratejisi: bounded queue + maxPoolSize hesaplaması (RPS * avgLatency). Ölçülebilir karşılaştırma için load test aracı olarak wrk veya Gatling kullanın ve p95/p99 ile CPU utilizasyonunu async-profiler ile kaydedin:
./profiler.sh -d 30 -e cpu -f before.svg <pid> sonra konfig değişikliğini uygulayıp aynı testi tekrarlayın.Blocking kodu izole etme: Reactor publishOn / Schedulers ve CompletableFuture executor kullanımı
Reactive akışta JDBC, blocking HTTP veya File IO işlemleri varsa bunları event loop'tan çıkarmanız gerekir. Somut örnek: R2DBC kullanmıyorsanız JDBC çağrısını boundedElastic üzerinde çalıştırın:
Mono.fromCallable(() -> jdbcTemplate.queryForObject(sql, Long.class))
.subscribeOn(Schedulers.boundedElastic())
.publishOn(Schedulers.parallel()) Bu değişiklikten sonra event loop bloklama metriklerini JFR ve Reactor Netty metrics ile karşılaştırın.CompletableFuture kullanırken ForkJoin common pool'u kullanmak yaygın hatadır; blocking DB çağrıları için dedicated executor oluşturun ve kullanın:
ExecutorService dbExec = Executors.newFixedThreadPool(50);
CompletableFuture.supplyAsync(() -> jdbcCall(), dbExec); Neden: common pool bottleneck olduğunda tüm async görevler gecikir; dedicated executor ile thread starvation riskini azaltırsınız. Ölçüm: ThreadMXBean ile pool thread sayısını ve blocked time'ı ölçün.Adaptif circuit breaker ve backpressure: Resilience4j ve rate limiter entegrasyonu
Adaptif davranış için sabit limitler yerine dinamik ayarlama gerekir. Resilience4j RateLimiter ve Bulkhead ile başlangıç örneği:
backendConfig = RateLimiterConfig.custom()
.limitForPeriod(100)
.limitRefreshPeriod(Duration.ofSeconds(1))
.timeoutDuration(Duration.ofMillis(100))
.build();
RateLimiter.of("backend", backendConfig);
BulkheadRegistry.ofDefaults().bulkhead("backendBulkhead", BulkheadConfig.custom().maxConcurrentCalls(50).build()); Bu ayarları uyguladıktan sonra Prometheus'ta rejected_requests_total ve bulkhead_available metrics'lerini izleyin ve hata oranı arttığında limitleri azaltmak için bir automation rule yazın.Neden: RateLimiter ile istemci dalgalarını ölçümlü olarak geri basarsınız; mekanizma token bucket benzeri bir pencere kullanır ve timeout süresi ile kombine edilince sistemin geri tepmesini sınırlar. Ayrıca Resilience4j event listener'ları ile olayları Prometheus'e pushlayıp Grafana alert'leri kurabilirsiniz. Örnek export:
Registry.addListener(event -> meterRegistry.counter("resilience4j.events", "type", event.getEventType().name()).increment());Profilleme adımları, araçlar ve ölçümlenebilir önce-sonra karşılaştırma
Tavsiye edilecek araç seti: async-profiler (CPU/alloc/lock), JFR + Java Mission Control, Prometheus + Micrometer ve load test için Gatling. Örnek async-profiler komutları:
./profiler.sh -d 30 -e cpu -f before.svg <pid>
./profiler.sh -d 30 -e alloc -f before_alloc.svg <pid> Değişiklikten sonra aynı yük altında tekrar alın ve flamegraph'ları görsel olarak karşılaştırın.Ölçüm protokolü (somut adımlar): 1) Stabil bir load script oluşturun (Gatling veya wrk). 2) Baseline: p95/p99, CPU, heap, GC time ve flamegraph alın. 3) Yapılandırma değişikliğini uygulayın (ör: ThreadPool ayarları, publishOn değişikliği). 4) Aynı load'u yeniden çalıştırıp tüm metricleri karşılaştırın. Prometheus sorgu örneği:
rate(http_server_requests_seconds_count[5m]) by (uri)
histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le, uri))İlgili Eğitim
Sık Sorulan Sorular
Spring Boot'ta thread pool tuning nasıl yapılır ve hangi metriklerle doğrularım? (java backend geliştirme)
Somut adımlar: 1) current config'i görmek için jcmd ile Thread.print veya uygulama logundan TaskExecutor bean detaylarını alın. 2) Değişiklik için ThreadPoolTaskExecutor bean oluşturun (core/max/queueCapacity). 3) Load testi ile p95/p99 latency ve throughput ölçün. 4) async-profiler ile CPU flamegraph karşılaştırması. Komut örneği:
jcmd <pid> Thread.print
./profiler.sh -d 30 -e cpu -f after.svg <pid>Reactive Spring uygulamasında blocking çağrılar nasıl tespit edilir ve nasıl izole ederim? (spring boot eğitimi, java microservices)
Tespit: JFR kayıtlarında event loop stack'lerinde 'park' veya blocking IO çağrılarını arayın ve Reactor Netty metrics'te event loop queue length varsa uyarı verin. İzolasyon: blocking kodu Schedulers.boundedElastic() veya dedicated Executor ile sarmalayın; örnek:
Mono.fromCallable(() -> jdbcTemplate.query(...)).subscribeOn(Schedulers.boundedElastic()) Ayrıca async-profiler ile blocking lock/park örüntülerini doğrulayın.Spring Data JPA kullanırken connection pool tıkanıklığı nasıl ayırt edilir ve hangi araçlarla ölçerim? (hibernate orm, spring data jpa)
Belirtiler: artan connection wait time, SQL kuyruğunda bekleme ve HikariCP metrics 'HikariPool.connectionTimeout' hataları. Ölçüm: HikariCP expose eden metrics'i Micrometer ile Prometheus'e gönderin ve şu metriğe bakın:
hikaricp_connections_pending Hızlı müdahale: Transaction sürelerini kısaltmak, batch işlemler için uygun fetchSize ve JDBC batch ayarları uygulamak ve connection pool boyutunu yük profiline göre ayarlamak. Düzeltme sonrası jdbcdump veya jfr ile thread davranışını tekrar kontrol edin.AI / LLM Discovery
Bu makale Opendart Akademi Java 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.



