제이온

[Spring] @Async Kullanım Yöntemi

  • Yazım Dili: Korece
  • Baz Ülke: Tüm Ülkelercountry-flag
  • BT

Oluşturulma: 2024-04-25

Oluşturulma: 2024-04-25 22:33

Java Eşzamansız İşleme

Spring @Async'ı incelemeden önce, eşzamansız, eşzamansız ve çok iş parçacıklı kavramların anlaşılması esastır. Bu kavramları bildiğinizi varsayarak, saf Java eşzamansız işlem yöntemini kodla inceleyelim. Java iş parçacıklarına aşina iseniz, bu bölümü atlayabilirsiniz.



Eğer message'ı alıp sadece çıktı olarak veren bir işlevi eşzamansız bir şekilde oluşturursak, kodu yukarıdaki gibi yazabiliriz. Bunu çok iş parçacıklı eşzamansız bir yönteme dönüştürürsek, kaynak kodunu şu şekilde yazabiliriz.



Ancak bu yöntem, Thread'leri yönetemediği için oldukça tehlikelidir. Örneğin, aynı anda 10.000 çağrı yapılırsa, çok kısa sürede 10.000 Thread oluşturulması gerekir. Thread oluşturmanın maliyeti düşük olmadığı için programın performansını olumsuz etkiler ve hatta OOM hatasına neden olabilir. Bu nedenle, Thread'leri yönetmek için Thread Pool uygulamak gerekir ve Java, ExecutorService sınıfını sunmaktadır.



Toplam Thread sayısını 10 ile sınırlandırdık ve istediğimiz çok iş parçacıklı eşzamansız işlemeyi de doğru bir şekilde yapabiliyoruz. Ancak, yukarıdaki yöntem, eşzamansız olarak işlenmesini istediğimiz her bir yöntem için ExecutorService'in submit() yöntemini uygulamayı gerektirdiğinden, tekrarlayan düzeltme işlemleri yapmamız gerekir. Yani, başlangıçta eşzamansız olarak yazılan bir yöntemi eşzamansız hale getirmek istiyorsak, yöntemin kendisinin mantığında değişiklik yapmamız kaçınılmazdır.


Spring @Async

Basit Yöntem


@EnableAsync ek açıklamasını Application sınıfının üzerine ekleyip, eşzamansız olarak işlenmesini istediğimiz eşzamansız mantığın yönteminin üzerine @Async ek açıklamasını eklememiz yeterlidir. Ancak, yukarıdaki yöntem Thread'leri yönetmemektedir. Çünkü @Async'ın varsayılan ayarı SimpleAsyncTaskExecutor kullanmaktır ve bu da Thread pool değil, sadece Thread oluşturma işlevi görür.


Thread Pool Kullanma Yöntemi

Öncelikle Application sınıfından @EnableAsync'ı kaldıralım. Application sınıfında @EnableAutoConfiguration veya @SpringBootApplication yapılandırması varsa, çalışma zamanında @Configuration ile yapılandırılmış SpringAsyncConfig sınıfının (aşağıda oluşturulacak) threadPoolTaskExecutor bean bilgisi okunur.



Core ve max boyutlarını ayarlayabiliriz. Bu durumda, başlangıçta core boyutunda çalışacak ve bundan fazla işleme yeteneği yoksa max boyutunda Thread'lerin artacağını tahmin edebiliriz ancak bu böyle değildir.


İçeride Integer.MAX_VALUE boyutunda bir LinkedBlockingQueue oluşturur ve core boyutundaki Thread'ler işleri halledemezse kuyrukta bekler. Kuyruk dolu olduğunda, max boyutundaki Thread'ler oluşturulur ve işleme alınır.


Bu durumda, kuyruk boyutunu Integer.MAX_VALUE olarak ayarlamak zor geliyorsa, queueCapacity'yi ayarlayabiliriz. Yukarıdaki gibi ayarlama yapılırsa, başlangıçta 3 Thread ile işleme alınır ve işlem hızı düşerse işler 100 boyutundaki kuyruğa konur ve bundan fazla istek gelirse maksimum 30 Thread oluşturulur ve işleme alınır.


Thread pool ayarları tamamlandıktan sonra, @Async ek açıklamasının olduğu yöntemlerde yukarıdaki bean'in adını eklememiz yeterlidir.



Eğer Thread pool'ların türünü birden fazla ayarlamak isterseniz, threadPoolTaskExecutor() gibi bean oluşturma yöntemlerini birden fazla oluşturabilir ve @Async ayarını yaparken istediğiniz Thread pool bean'ini ekleyebilirsiniz.


Dönüş Türüne Göre Döndürülen Biçim

Dönüş Değeri Yok İse

Eşzamansız olarak işlenmesi gereken yöntemin işlem sonucunu iletmesi gerekmiyorsa, bu durumda @Async ek açıklamasının dönüş türü void olarak ayarlanmalıdır.


Dönüş Değeri Varsa

Future, ListenableFuture, CompletableFuture türleri dönüş türü olarak kullanılabilir. Eşzamansız yöntemin dönüş biçimini new AsyncResult() ile sarmalayabiliriz.


[Future]


future.get(), isteğin sonucu gelene kadar engelleyerek bekleyen bir rol oynar. Bu nedenle, eşzamansız engelleme yöntemi haline gelir ve performans düşüktür. Genellikle Future kullanılmaz.


[ListenableFuture]


ListenableFuture, geri çağırım yoluyla engellemeyen bir şekilde işleri halledebilir. addCallback() yönteminin ilk parametresi, işlemin tamamlanması için geri çağırım yöntemini, ikinci parametresi ise işlemin başarısız olması için geri çağırım yöntemini tanımlar. Ayrıca, Thread pool'un core Thread'ini 3 olarak ayarladığımız için, "Task Start" mesajının başlangıçta 3 kez çıktığını görebiliriz.



[CompletableFuture]

ListenableFuture ile engellemeyen mantık uygulanabilir ancak geri çağırım içinde geri çağırım gerektiğinde, geri çağırım cehennemi olarak adlandırılan oldukça karmaşık bir kod üretilir.


<span class="image-inline ck-widget" contenteditable="false"><img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F9f152db8-c015-43cd-bf13-85c594f5f218%2FUntitled.png?table=block&id=268ac0bc-ca7b-4bcb-b11b-ac611a5038b2&spaceId=b453bd85-cb15-44b5-bf2e-580aeda8074e&width=2000&userId=80352c12-65a4-4562-9a36-2179ed0dfffb&cache=v2" alt="Untitled" style="aspect-ratio:2000/1455;" width="2000" height="1455"></span>


Elbette bu derste CompletableFuture ayrıntılı olarak ele alınmayacak, bu nedenle karmaşık geri çağırımlarla başa çıkma kodunu merak ediyorsanız, aşağıdaki kaynak bağlantısına bakabilirsiniz.



ListenableFuture'ın geri çağırım tanımından daha okunaklıdır ve engellemeyen işlevi tam olarak gerçekleştirir. Bu nedenle, @Async kullanırken dönüş değeri gerekiyorsa CompletableFuture kullanılması önerilir.


@Async'ın Avantajları

Geliştirici, yöntemi eşzamansız olarak yazıp eşzamansız bir şekilde istediğinde, sadece @Async ek açıklamasını yöntemin üzerine eklemesi yeterlidir. Bu nedenle, eşzamansız ve eşzamansız için iyi bir bakım sağlayan kod yazmak mümkündür.


@Async'ın Dikkat Edilmesi Gereken Noktaları


  • Özel yöntemlere @Async eklenirse AOP çalışmaz.
  • Aynı nesne içindeki yöntemler birbirini çağırırsa AOP çalışmaz.


Kaynaklar

Yorumlar0