• 30.10.2026 23:00:58
  • İbrahim Gökyar

Spring AI ve Model Context Protocol (MCP) içeren Java backend geliştirme için gerçekçi profiling, Hibernate ORM ve Spring Data JPA optimizasyonları sunan rehber. Java eğitimi ve spring boot eğitimi bağlamında ölçülebilir adımlar içerir.

Spring AI ve Model Context Protocol ile Java backend geliştirme Profiling

CPU ve allocation profilini ölçme: async-profiler + JFR ile yol haritası

Profiling'e başlamadan önce hedef ölçütleri belirleyin: 95. persentil latans, p99 CPU süresi, ve allocation rate (obj/s). Ölçüm için production benzeri yük kullanın; örnek yük aracı: wrk. Gerçek adımlar: (1) benchmark komutuyla baseline alın:

wrk -t8 -c200 -d60s http://localhost:8080/api/orders
(2) CPU flamegraph için async-profiler çalıştırın:
sudo ./async-profiler/profiler.sh -d 60 -e cpu -f /tmp/profile-cpu.svg 
# allocation flamegraph
sudo ./async-profiler/profiler.sh -d 60 -e alloc -f /tmp/profile-alloc.svg 
# kısa JFR kaydı (60s)
jcmd  JFR.start name=baseline settings=profile duration=60s filename=baseline.jfr
Neden? async-profiler sistem seviyesinde örnekleme ile Java frame'lerini native-sampleleyerek, inline ve hotspot optimizasyonlarını doğru biçimde yansıtır; JFR ise daha geniş event seti (allocation, safepoint, lock) sağlayarak thread-state ve safepoint etkilerini gösterir. Önce-sonra karşılaştırması: baseline wrk çıktılarını, async-profiler SVG'lerini ve jfr kaydını karşılaştırın; CPU yüzde dağılımından (%CPU) hotspots tespit edin ve değişiklik sonrası aynı komutlarla delta hesaplayın.

Hibernate ORM ve Spring Data JPA: N+1 tespiti, batch ve flush maliyeti

N+1 problemini tespit etmek için SQL logging ve Hibernate statistics'i birlikte kullanın: Spring Boot'da uygulamayı start ederken application.yml'de SQL logunu açın ve p6spy ile sorguları zaman damgası ile yakalayın. Örnek application.yml konfigürasyonu:

spring:
  jpa:
    properties:
      hibernate:
        jdbc:
          batch_size: 50
logging:
  level:
    org.hibernate.SQL: DEBUG
    com.p6spy: DEBUG
Ve repository seviyesinde JOIN FETCH ya da @EntityGraph kullanın; örnek Spring Data JPA düzeltmesi:
public interface OrderRepository extends JpaRepository {
  @EntityGraph(attributePaths = {"lines", "customer"})
  @Query("select o from Order o where o.status = :status")
  List findByStatusWithGraph(@Param("status") OrderStatus status);
}
Neden: hibernate.jdbc.batch_size azaltılmış roundtrip sayısı sağlar; flush sıklığını azaltmak I/O ve prepared-statement maliyetini düşürür ama çok büyük batch'ler memory spike'larına yol açar. Ölçüm metodu: baseline'da jcmd GC.class_histogram > baseline-histo.txt alıp, batch_size=50 ile aynı yük testi (wrk) sonrası yeni histogramı kıyaslayın; ayrıca async-profiler allocation flamegraph ile allocation hotspot değişimini görün.

Thread pool ve TaskExecutor tuning: queue vs maxPool - nasıl ölçülür ve uygulama

Tomcat veya gömülü servlet container thread pool'u ile uygulama TaskExecutor'ünü ayrı tutun ve yük altındaki davranışı ölçün. Ölçüm için önce düşük TTL ile 95. persentil latansını wrk ile alıp, thread pool parametrelerini değiştirin ve tekrar ölçün. Örnek Spring bean konfigürasyonu (TaskExecutor):

@Configuration
public class ExecutorConfig {
  @Bean("ioExecutor")
  public ThreadPoolTaskExecutor ioExecutor() {
    ThreadPoolTaskExecutor ex = new ThreadPoolTaskExecutor();
    ex.setCorePoolSize(20);
    ex.setMaxPoolSize(80);
    ex.setQueueCapacity(1000);
    ex.setThreadNamePrefix("io-worker-");
    ex.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    return ex;
  }
}
Neden CallerRunsPolicy? Çok yüksek yükte queue tükenmesini önler ve throttling etkisi yapar; ancak geri basınç uygulamak istemiyorsanız RejectedExecutionHandler yerine RateLimiter (resilience4j) ekleyin. Ölçüm adımı: baseline yük (wrk), ardından core/max/queue değişikliği ile yeniden test; JFR'de thread park/unpark ve contended monitor event'lerini karşılaştırın: jcmd JFR.dump name=baseline filename=baseline.jfr gibi. QueueCapacity tercihi: queue büyükse bellek baskısı artar, küçükse daha fazla rejected/CallerRuns oluşur - uygulamanızın latency SLA'sına göre karar verin.

Spring AI, Spring MCP (Model Context Protocol) ve güvenli model çağrıları

Spring AI ve MCP ile dış model çağrıları yaparken model context boyutu, token maliyeti ve latency kritik. MCP header'larını sınırlandırmak, context pruning ve tracing eklemek gerekir. Örnek WebClient filtresiyle MCP header ekleme ve timeout tanımı:

@Bean
public WebClient modelClient(WebClient.Builder b) {
  return b.baseUrl("https://model-host")
    .filter((req, next) -> {
      ClientRequest r = ClientRequest.from(req)
        .header("MCP-Context-Length", String.valueOf(computeContextSize(req)))
        .build();
      return next.exchange(r);
    })
    .clientConnector(new ReactorClientHttpConnector(
      HttpClient.create().responseTimeout(Duration.ofSeconds(5))
    ))
    .build();
}

// test ile timeout kontrolü
curl -v --max-time 6 https://your-gateway/api/model-invoke
Güvenlik: MCP payload'ını trimlemek için token veya prompt parçalara ayırın, ve model çağrılarını Resilience4j ile koruyun (circuit-breaker + bulkhead). Örnek kısa config: application.yml içine resilience4j.circuitbreaker ayarları ekleyin ve production'da model çağrılarını izlemek için Jaeger/Zipkin tracing (spring-cloud-sleuth + Brave) bağlayın. Neden: model çağrıları network-bound ve dış hizmete bağımlı; MCP ile bağlam boyutunu sınırlandırmak hem maliyeti hem de tail-latency'yi düşürür, tracing ise hata kök nedenini (context-injection veya token-exhaustion) bulmanızı sağlar.

Sık Sorulan Sorular

java backend geliştirme için hangi profiler daha uygun: async-profiler mi JFR mi?

Her ikisini birlikte kullanın. async-profiler CPU ve allocation flamegraph üretmede hızlı sonuç verir: sudo ./async-profiler/profiler.sh -d 60 -e cpu -f out.svg . JFR sistem ve JVM eventlerini (safepoint, lock, GC) yakalar: jcmd JFR.start name=baseline settings=profile duration=60s filename=baseline.jfr. Karşılaştırma yöntemi: aynı yük testi (ör. wrk) ile önce baseline, sonra değişiklik; flamegraph'te hotspot yüzdelerini ve JFR'de safepoint/GC süresini karşılaştırın.

spring data jpa N+1 nasıl tespit edilir ve giderilir?

Tespit için p6spy veya Hibernate statistics kullanın; application.yml'de org.hibernate.SQL DEBUG açın ve sorguları zamanla analiz edin. N+1 için düzeltilmiş örnek: repository'de @EntityGraph(attributePaths = {"lines","customer"}) ya da JPQL JOIN FETCH kullanın. Ciddi yük altında etkisini ölçmek için önce ve sonra jcmd GC.class_histogram > before.txt / after.txt karşılaştırması ve async-profiler allocation graph'ını inceleyin.

spring ai ve model context protocol çağrılarında güvenlik ve maliyet nasıl kontrol edilir?

MCP başlıkları ile context boyutunu sınırlandırın, çağrıları Resilience4j ile sarmalayın (circuit-breaker, rate-limiter, bulkhead). Örnek test komutu: curl --max-time 6 https://gateway/api/model-invoke ile timeout davranışını doğrulayın. Maliyet kontrolü için çağrı başına token sayısını ölçün, çağrıları örnekleyin ve maliyeti faturaya bağlayarak p99 latency ve token kullanımını izleyin.

java kursu veya java programlama eğitimi sırasında profiling ve heap debugging nasıl öğretilmeli?

Öğrencilere adım adım pratik verin: önce basit bir REST servisi ile wrk yük testi yapın, sonra async-profiler ve jcmd ile CPU/heap snapshot alın. Örnekler: ./async-profiler/profiler.sh -d 30 -e cpu -f cpu.svg ve jcmd GC.class_histogram > histo.txt. Bu şekilde teoriyi gerçek ölçümlerle bağlayarak öğrenme kalıcılığı artar.

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.

Opendart Akademi llms.txt