Spring WebFlux, Java'da modern, ölçeklenebilir web uygulamaları oluşturmaya yönelik reaktif, engellemeyen bir web çerçevesidir. Spring Framework'ün bir parçasıdır ve Java'da reaktif programlamayı uygulamak için Reactor kütüphanesini kullanır.
"Reaktif" terimi, G/Ç olaylarına tepki veren ağ bileşenleri, fare olaylarına tepki veren kullanıcı arayüzü denetleyicileri ve diğerleri gibi değişime tepki verme etrafında oluşturulan programlama modellerini ifade eder. Bu anlamda engellememe tepkiseldir, çünkü artık engellenmek yerine, işlemler tamamlandığında veya veriler kullanılabilir hale geldikçe bildirimlere tepki verme modundayız.
Spring WebFlux'ta (ve genel olarak engellemeyen sunucularda), uygulamaların engellemediği varsayılır. Bu nedenle, engellemeyen sunucular, istekleri işlemek için küçük, sabit boyutlu bir iş parçacığı havuzu (olay döngüsü çalışanları) kullanır.
WebFlux istek işleme biraz farklı olsa da:
tarafından oluşturulan oldukça minimalist bir uygulamaya ihtiyacımız var. Kod mevcuttur.
İş parçacığıyla ilgili tüm konular CPU'ya oldukça bağımlıdır. Genellikle istekleri işleyen işlem parçacıklarının sayısı CPU çekirdeği sayısıyla ilişkilidir . Eğitim amaçlı olarak, Docker kapsayıcısını çalıştırırken CPU'ları sınırlayarak bir havuzdaki iş parçacığı sayısını kolayca değiştirebilirsiniz:
docker run --cpus=1 -d --rm --name webflux-threading -p 8081:8080 local/webflux-threading
Uygulamamız basit bir falcıdır. /karma
endpoint'i çağırdığınızda, balanceAdjustment
ile 5 kayıt alacaksınız. Her ayarlama, size verilen karmayı temsil eden bir tam sayıdır. Evet, çok cömertiz çünkü uygulama yalnızca pozitif sayılar üretiyor. Artık kötü şans yok!
@GetMapping("/karma") public Flux<Karma> karma() { return prepareKarma() .map(Karma::new) .log(); } private Flux<Integer> prepareKarma() { Random random = new Random(); return Flux.fromStream( Stream.generate(() -> random.nextInt(10)) .limit(5)); }
log
yöntemi burada çok önemli bir şeydir. Tüm Reaktif Akış sinyallerini gözlemler ve bunları INFO düzeyindeki günlüklere kadar takip eder.
curl localhost:8081/karma
günlük çıktısı aşağıdaki gibidir:
Görebildiğimiz gibi, IO iş parçacığı havuzunda işlem yapılıyor. Konu adı ctor-http-nio-2
reactor-http-nio-2
anlamına gelir. Görevler, onları gönderen iş parçacığında hemen yürütüldü. Reactor bunları başka bir havuza planlamak için herhangi bir talimat görmedi.
@GetMapping("/delayedKarma") public Flux<Karma> delayedKarma() { return karma() .delayElements(Duration.ofMillis(100)); }
Orijinal karma()
çağrısında zaten bildirilmiş olduğundan buraya log
yöntemini eklememize gerek yok.
Bu sefer IO iplik reactor-http-nio-4
yalnızca ilk eleman alındı. Geri kalan 4'ünün işlenmesi parallel
bir iş parçacığı havuzuna ayrıldı.
delayElements
Javadoc'u bunu doğrular:
Sinyaller geciktirilir ve paralel varsayılan Zamanlayıcıda devam eder
Çağrı zincirinin herhangi bir yerinde .subscribeOn(Schedulers.parallel())
komutunu belirterek aynı etkiyi gecikmeden elde edebilirsiniz.
parallel
zamanlayıcının kullanılması, birden fazla görevin farklı iş parçacıklarında aynı anda yürütülmesine izin vererek performansı ve ölçeklenebilirliği artırabilir; bu, CPU kaynaklarını daha iyi kullanabilir ve çok sayıda eşzamanlı isteği işleyebilir.
Bununla birlikte, kod karmaşıklığını ve bellek kullanımını da artırabilir ve maksimum çalışan iş parçacığı sayısı aşılırsa potansiyel olarak iş parçacığı havuzunun tükenmesine yol açabilir. Bu nedenle parallel
iş parçacığı havuzunu kullanma kararı, uygulamanın özel gereksinimlerine ve değiş tokuşlarına dayanmalıdır.
flatMap
kullanacağız ve falcıyı daha adil hale getireceğiz. Her Karma örneği için, orijinal ayarlamayı 10 ile çarpacak ve zıt ayarlamaları üreterek, orijinali telafi eden dengeli bir işlemi etkili bir şekilde yaratacaktır.
@GetMapping("/fairKarma") public Flux<Karma> fairKarma() { return delayedKarma() .flatMap(this::makeFair); } private Flux<Karma> makeFair(Karma original) { return Flux.just(new Karma(original.balanceAdjustment() * 10), new Karma(original.balanceAdjustment() * -10)) .subscribeOn(Schedulers.boundedElastic()) .log(); }
Gördüğünüz gibi makeFair's
Flux'u boundedElastic
bir Elastik iş parçacığı havuzuna abone olmalıdır. İlk iki Karma için kayıtlarımızda neler olduğuna bir göz atalım:
Reactor, IO iş parçacığındabalanceAdjustment balanceAdjustment=9
ile ilk öğeye abone olur
Daha sonra, boundedElastic
havuzu boundedElastic-1
iş parçacığında 90
ve -90
ayarlamalar yayarak Karma adaleti üzerinde çalışır
İlkinden sonraki öğeler paralel iş parçacığı havuzuna abone olur (çünkü zincirde hâlâ delayedElements
öğeler var)
boundedElastic
zamanlayıcı nedir ?
Varsayılan olarak, boundedElastic
iş parçacığı havuzunun maksimum boyutu, kullanılabilir işlemci sayısının 10 ile çarpılmasıyla , ancak gerekirse farklı bir maksimum boyut kullanacak şekilde yapılandırabilirsiniz.
boundedElastic
gibi eşzamansız bir iş parçacığı havuzu kullanarak, iş parçacıklarını ayırmak için görevleri boşaltabilir ve diğer istekleri işlemek için ana iş parçacığını serbest bırakabilirsiniz. İş parçacığı havuzunun sınırlı yapısı, iş parçacığı açlığını ve aşırı kaynak kullanımını önleyebilir; havuzun esnekliği ise iş yüküne bağlı olarak çalışan iş parçacığı sayısını dinamik olarak ayarlamasına olanak tanır.
single
: Bu, eşzamanlı yürütme için tasarlanmış tek iş parçacıklı, serileştirilmiş bir yürütme bağlamıdır. Bir görevin sırayla yürütüldüğünden ve iki görevin aynı anda yürütülmediğinden emin olmanız gerektiğinde kullanışlıdır.
immediate
: Bu, herhangi bir iş parçacığı değişimi olmadan çağıran iş parçacığında görevleri hemen yürüten bir zamanlayıcının önemsiz, işlemsiz bir uygulamasıdır.