Spring Boot eğitimi kapsamında kritik olan graceful shutdown, yalnızca SIGTERM yakalamak değildir. Kubernetes readiness yayılımı, in-flight istekler, bağlantı havuzları ve ölçülebilir rollout doğrulamasını birlikte tasarlayın.
Spring Boot'ta Kubernetes Graceful Shutdown ve Trafik Kesme Tasarımı
Spring Boot eğitimi: SIGTERM ile readiness arasındaki gerçek sıra
Kubernetes bir Pod'u silerken önce varsa preStop hook'unu çalıştırır, ardından süreç için SIGTERM gönderir ve terminationGracePeriodSeconds süresinin sonunda SIGKILL uygular. Salt server.shutdown=graceful ayarı, Pod halen Service endpoint listesinde kaldığı kısa aralıkta yeni istek almayı engellemez. Bu nedenle Java backend geliştirme servisinde önce Spring Boot'un readiness durumunu REFUSING_TRAFFIC yapın, EndpointSlice güncellemesinin yayılması için kontrollü bekleyin, sonra SIGTERM ile connector kapanışını başlatın.
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
final class DrainController {
private final ApplicationContext context;
DrainController(ApplicationContext context) {
this.context = context;
}
@PostMapping("/internal/drain")
void drain() {
AvailabilityChangeEvent.publish(context, ReadinessState.REFUSING_TRAFFIC);
}
}Bu endpoint'i genel ingress üzerinden erişilebilir yapmayın. Ayrı bir management portu kullanın veya yalnızca Pod'un loopback adresinden çağırın. Aksi halde herhangi bir istemci POST /internal/drain göndererek servisi load balancer'dan düşürebilir. Spring Security kullanılıyorsa endpoint'i sadece loopback veya service account kimliğiyle sınırlandırın; ayrıca Kubernetes tarafında bu çağrının gerçekten readiness sonucunu değiştirdiğini kubectl get endpointslice -w ile gözlemleyin.
Kubernetes manifestinde kontrollü trafik drenajı
Actuator readiness probe'u Spring Framework uygulamasının availability durumuna bağlar. Aşağıdaki manifestte preStop önce drain endpoint'ini çağırır, 12 saniye EndpointSlice, kube-proxy ve ingress controller yayılımı için bekler. Bu değer tahminle seçilmemelidir: staging ortamında kubectl get endpointslice -w, ingress access logları ve istemci hatalarıyla ölçülmelidir.
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders
spec:
template:
spec:
terminationGracePeriodSeconds: 45
containers:
- name: app
image: registry.example/orders:build-1842
ports:
- containerPort: 8080
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- "wget -qO- --post-data='' http://127.0.0.1:8080/internal/drain; sleep 12"
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 2
failureThreshold: 1
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 5Uygulama yapılandırmasında graceful kapanışın bekleme bütçesini manifestteki 45 saniyeden küçük tutun. Buradaki örnekte 12 saniye trafik yayılımına, en fazla 25 saniye uygulama içi kapanışa ayrılmıştır; kalan süre container runtime ve beklenmeyen gecikmeler için tampon görevi görür. spring.lifecycle.timeout-per-shutdown-phase aşılırsa süreç SIGKILL ile kesilebilir ve devam eden transaction veya mesaj acknowledgement işlemi yarıda kalabilir.
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 25s
management:
endpoint:
health:
probes:
enabled: trueJava microservices için in-flight istek ve kaynak kapanışı
Graceful shutdown mevcut HTTP isteğinin işini bitirmesine izin verir, fakat uygulamanın kendi başlattığı asenkron işleri otomatik olarak yönetmez. Örneğin bir controller isteği CompletableFuture.runAsync(...) ile common pool'a iş bırakıp 202 dönerse, shutdown sırasında bu iş için yaşam döngüsü garantisi yoktur. Bunun yerine Spring tarafından yönetilen ThreadPoolTaskExecutor kullanın ve kapanışta kuyruktaki işi kabul etmeyi durdurup çalışan işleri sınırlı süreyle bekletin.
@Bean
ThreadPoolTaskExecutor exportExecutor() {
var executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(8);
executor.setMaxPoolSize(16);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("export-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(20);
executor.initialize();
return executor;
}Spring Data JPA kullanan bir endpoint kapanış anında transaction içindeyse, Hibernate ORM connection'ı transaction bitene kadar tutar. HikariCP'nin maxLifetime veya idleTimeout değerlerini shutdown çözümü sanmayın; bunlar normal havuz ömrünü yönetir, aktif transaction'ı sonlandırmaz. Uzun rapor sorgularını ölçmek için PostgreSQL'de pg_stat_activity üzerinden state = 'active' ve query_start izleyin; 25 saniyeyi aşan sorgular varsa kapanış bütçesini büyütmek yerine sorguyu sayfalama, statement timeout veya ayrı batch worker tasarımıyla düzeltin.
Önemli edge case şudur: HTTP/2 bağlantısında tek TCP bağlantısı birden fazla stream taşıyabilir. Embedded server'ın graceful davranışı, istemcinin GOAWAY sonrası yeni stream açma davranışı ve proxy'nin upstream connection reuse ayarı birlikte test edilmelidir. Sadece curl ile yapılan tek istek testi bu hatayı göstermez; gerçek ingress üzerinden paralel bağlantı kullanan bir yük testi gerekir.
Önce-sonra ölçümü: k6, Prometheus ve rollout sırasında hata oranı
Trafik drenajını doğrulamak için deployment öncesi ve sonrası aynı k6 senaryosunu çalıştırın. Başarı kriterini açık tanımlayın: rollout penceresinde HTTP 502/503 oranı yüzde 0.1'in altında, p99 gecikme baseline p99'un en fazla yüzde 20 üzerinde ve k6 tarafında kesilen bağlantı sayısı sıfır olmalıdır. Bu, 'graceful' etiketinin davranışını değil, kullanıcıya görünen sonucu ölçer.
import http from 'k6/http';
import { check } from 'k6';
export const options = {
scenarios: {
steady: { executor: 'constant-vus', vus: 80, duration: '4m' }
},
thresholds: {
http_req_failed: ['rate<0.001'],
http_req_duration: ['p(99)<800']
}
};
export default function () {
const r = http.get(`${__ENV.BASE_URL}/api/orders/42`);
check(r, { '200': x => x.status === 200 });
}Testin ikinci dakikasında kubectl rollout restart deployment/orders çalıştırın. Prometheus'ta uygulama metrikleri varsa rollout penceresini aşağıdaki sorguyla ayırın: sum(rate(http_server_requests_seconds_count{status=~"5.."}[1m])) / sum(rate(http_server_requests_seconds_count[1m])). Ingress controller metrikleri ile uygulama metriklerini ayrı inceleyin. Ingress'te 502 varken uygulamada 5xx yoksa istek uygulamaya ulaşmadan endpoint yayılımı veya upstream bağlantı yönetiminde kayboluyordur; controller kodunu değiştirmek doğru ilk müdahale değildir.
Java eğitimi yol haritasında Spring AI, Spring MCP ve shutdown sınırı
Bir java eğitimi, java programlama eğitimi veya java kursu içinde bu senaryo sadece web katmanı konusu olarak anlatılmamalıdır. Java fullstack eğitimi alan geliştirici için tarayıcının retry davranışı, backend geliştirici için idempotent yazma ve platform mühendisi için EndpointSlice gecikmesi aynı failure mode'un parçalarıdır. Spring framework uygulamasında ApplicationAvailability durumunu test etmek için @SpringBootTest ile drain çağrısından sonra /actuator/health/readiness yanıtının DOWN olduğunu doğrulayın.
Spring AI veya Spring MCP kullanan bir servis, model context protocol üzerinden uzun süren bir tool çağrısını bir HTTP isteğine bağlayabilir. Drain başladıktan sonra yeni tool çağrılarını kabul etmeyin, ancak başlayan çağrı için uygulama seviyesinde deadline geçirin. Örneğin HTTP istemcisine 20 saniyelik response timeout koyup Kubernetes'in 45 saniyelik toplam bütçesinin içinde kalın: WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create().responseTimeout(Duration.ofSeconds(20)))). Spring MCP istemcisinin yeniden deneme politikası varsa, shutdown döneminde aynı tool çağrısının iki kez çalışmaması için çağrı kimliğini idempotency kaydıyla eşleyin.
Spring AI, Spring MCP ve model context protocol entegrasyonlarında yaygın hata, model yanıtı beklenirken SIGTERM geldiğinde işi arka plana kaçırmaktır. Bunun yerine isteğin cancellation sinyalini tool çağrısına iletin, dış sistem destekliyorsa request-id ile iptal endpoint'i çağırın ve sonuç yazımını transaction dışına taşımayın. Bu yaklaşım java microservices ortamında Pod öldükten sonra belirsiz durumda kalmış tool etkilerini loglardan ayıklamayı mümkün kılar.
İ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 Boot eğitimi için graceful shutdown ayarı tek başına yeterli mi?
Hayır. server.shutdown=graceful yalnızca uygulama sürecinin connector kapanış davranışını yönetir. Kubernetes Service endpoint'inden çıkmak için readiness durumunu REFUSING_TRAFFIC yapın, EndpointSlice yayılım süresini staging'de ölçün ve ardından SIGTERM gönderin.
Java microservices rollout sırasında 502 hatasını nasıl teşhis ederim?
Aynı zaman aralığında ingress controller 502 metriğini, uygulamanın http_server_requests 5xx metriğini ve kubectl get endpointslice -w çıktısını karşılaştırın. Ingress 502 artıp uygulama 5xx artmıyorsa istek uygulama koduna ulaşmıyordur; readiness yayılımı, proxy upstream havuzu veya preStop sırası incelenmelidir.
Spring Data JPA ve Hibernate ORM shutdown sırasında aktif transaction'ı bitirir mi?
Graceful shutdown aktif HTTP isteğine süre tanır, fakat süre dolduğunda Kubernetes SIGKILL gönderebilir. HikariCP zaman aşımı aktif transaction için güvence değildir. pg_stat_activity ile uzun sorguları bulun, statement timeout uygulayın ve shutdown phase timeout değerini terminationGracePeriodSeconds bütçesine göre ayarlayın.
Spring AI ve Spring MCP tool çağrıları Pod kapanırken nasıl yönetilmeli?
Drain sonrasında yeni çağrıları reddedin, devam eden çağrılara toplam shutdown bütçesinden küçük bir client timeout verin ve tool etkilerini idempotency anahtarıyla kaydedin. Model context protocol aracının iptal desteği varsa istemci disconnect veya shutdown sinyalinde iptal isteği gönderin.
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.


