Spring Boot 3.2 ve üzeri sürümler, JDK 21’in sanal thread desteğini otomatik olarak yapılandırarak @Async anotasyonuyla basit ve yüksek performanslı asenkron işlemler yapmanıza imkan tanır.
Bu bölümde Neden Spring Boot'ta Sanal Thread Kullanmalıyız? konusunu netleştirip uygulamada kullanacağınız temel noktaları özetliyoruz.
@Async ile işaretlenen metodlar ana thread’i meşgul etmeden çalışır.Bu bölümde Gereksinimler konusunu netleştirip uygulamada kullanacağınız temel noktaları özetliyoruz.
Asenkron işleme olanak tanıyan spring-boot-starter-web paketini projenize ekleyin.
Maven:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Gradle:
implementation 'org.springframework.boot:spring-boot-starter-web'
application.yml veya application.properties dosyanıza şu satırı ekleyin:
spring: threads: virtual: enabled: true
spring.threads.virtual.enabled=true
Bu ayar, aşağıdakileri otomatik yapılandırır:
applicationTaskExecutor (Async)@Scheduled)Ana uygulama sınıfınıza @EnableAsync ekleyin:
@Async anotasyonlu metodlar sanal thread üzerinde çalışır:
Aşağıdaki endpoint’leri kullanarak servis metodlarını tetikleyin:
./mvnw spring-boot:run # veya gradle bootRun
Void görevi tetikle
curl http://localhost:8080/async/run
Sanal thread başlangıç/bitiş log’larını kontrol edin.
Sonuç döndüren görevi tetikle
curl http://localhost:8080/async/run-return # dönen: "Tamamlandı"
Artık Spring Boot Sanal Thread’li Asenkron Görevler için üretim odaklı bir Spring Boot temeliniz var. Sonraki adımda ayarları kendi domainine uyarlayıp test ve gözlemlenebilirlik katmanını ekleyerek gerçek trafik altında doğrulayın.
package com.example.async; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync public class AsyncVirtualApplication { public static void main(String[] args) { SpringApplication.run(AsyncVirtualApplication.class, args); } }
package com.example.async import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.scheduling.annotation.EnableAsync @SpringBootApplication @EnableAsync class AsyncVirtualApplication fun main(args: Array<String>) { runApplication<AsyncVirtualApplication>(*args) }
package com.example.async; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.time.LocalTime; import java.util.concurrent.CompletableFuture; @Slf4j @Service public class AsyncVirtualService { @Async public void runTask() { log.info("[{}] Asenkron başlatıldı: {}", LocalTime.now(), Thread.currentThread()); try { Thread.sleep(1000); } catch (InterruptedException ignored) {} log.info("[{}] Asenkron tamamlandı: {}", LocalTime.now(), Thread.currentThread()); } @Async public CompletableFuture<String> runAndReturn() throws InterruptedException { Thread.sleep(500); return CompletableFuture.completedFuture("Tamamlandı"); } }
package com.example.async import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service import java.time.LocalTime import java.util.concurrent.CompletableFuture @Service class AsyncVirtualService { private val log = LoggerFactory.getLogger(AsyncVirtualService::class.java) @Async fun runTask() { log.info("[{}] Asenkron başlatıldı: {}", LocalTime.now(), Thread.currentThread()) try { Thread.sleep(1000) } catch (_: InterruptedException) {} log.info("[{}] Asenkron tamamlandı: {}", LocalTime.now(), Thread.currentThread()) } @Async fun runAndReturn(): CompletableFuture<String> { Thread.sleep(500) return CompletableFuture.completedFuture("Tamamlandı") } }
package com.example.async; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/async") @RequiredArgsConstructor public class AsyncVirtualController { private final AsyncVirtualService service; @GetMapping("/run") public String triggerRun() { service.runTask(); return "Asenkron sanal thread görevi tetiklendi"; } @GetMapping("/run-return") public String triggerRunAndReturn() throws Exception { var future = service.runAndReturn(); return future.get(); } }
package com.example.async import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController import lombok.RequiredArgsConstructor @RestController @RequestMapping("/async") @RequiredArgsConstructor class AsyncVirtualController(private val service: AsyncVirtualService) { @GetMapping("/run") fun triggerRun(): String { service.runTask() return "Asenkron sanal thread görevi tetiklendi" } @GetMapping("/run-return") @Throws(Exception::class) fun triggerRunAndReturn(): String { val future = service.runAndReturn() return future.get() } }