Spring WebFlux یکی از محبوب ترین فریم ورک ها برای برنامه نویسی واکنشی در جاوا است. در اینجا نگاهی عملی به استفاده از WebFlux با Reactor است.
برنامه نویسی واکنشی یک سبک برنامه نویسی مهم است که از دنیای عملکردی تکامل یافته است. کد واکنشگرا از یک فلسفه رویداد محور از جریانها، تولیدکنندگان و مشترکین برای سادهسازی منطق پیچیده و فعال کردن مدیریت ناهمزمان و غیرانسدادی پردازش IO در برنامهها استفاده میکند. در جاوا، این بدان معناست که ما میتوانیم برنامههای کاربردی را با استفاده از بسته java.nio
(IO غیر مسدودکننده) با APIهای رسا بسازیم. بسیاری از چارچوب ها و رویکردها از واکنش پذیری در جاوا پشتیبانی می کنند. یکی از محبوبترین آنها Spring WebFlux است. این مقاله یک مقدمه عملی برای برنامه نویسی جاوا واکنشی با Spring WebFlux است.
واکنشی با فنر
Reactivity یک اصطلاح واحد قدرتمند برای توصیف و ترکیب عملکردهایی مانند درخواستهای وب و دسترسی به داده به ما میدهد. به طور کلی، ما از تولیدکنندگان رویداد و مشترکین برای توصیف منابع رویداد ناهمزمان و آنچه باید هنگام فعال شدن یک رویداد رخ دهد، استفاده میکنیم.
در سبک معمولی چارچوب Spring، WebFlux یک لایه انتزاعی برای ساخت مؤلفههای وب واکنشپذیر فراهم میکند. این بدان معناست که شما می توانید از چند پیاده سازی واکنشی زیربنایی مختلف استفاده کنید. پیشفرض Reactor است که برای نمایش از آن استفاده میکنیم.
برای شروع، یک برنامه جدید را با ابزار خط فرمان Spring مقداردهی اولیه می کنیم. چند راه برای نصب این ابزار وجود دارد، اما من دوست دارم از SDKMan استفاده کنم. باید جاوا ۱۷+ را نصب کرده باشید. میتوانید دستورالعملهای نصب SDKMan برای سیستم عامل خود را اینجا پیدا کنید. پس از نصب، می توانید Spring CLI را با: $ sdk i Springboot
اضافه کنید. اکنون دستور $ spring --version
باید کار کند.
برای شروع برنامه جدید، تایپ کنید:
$ spring init --dependencies=webflux --build=maven --language=java spring-reactive
بعد، cd
را در دایرکتوری spring-reactive
وارد کنید. Spring برای ما یک چیدمان ساده ایجاد کرده است، از جمله یک کلاس اصلی در src/main/java/com/example/springreactive2/DemoApplication.java
.
اجازه دهید این کلاس را تغییر دهیم تا یک کنترل کننده نقطه پایانی واکنشی اضافه کنیم، همانطور که در فهرست ۱ نشان داده شده است.
package com.example.springreactive;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@RestController
public class EchoController {
@GetMapping("/hello")
public Mono<String> hello() {
return Mono.just("Hello, InfoWorld!");
}
@GetMapping("echo/{str}")
public Mono<String> echo(@PathVariable String str) {
return Mono.just("Echo: " + str);
}
@GetMapping("echoquery")
public Mono<String> echoQuery(@RequestParam("name") String name) {
return Mono.just("Hello, " + name);
}
}
}
توجه: در کتابخانه Reactor، Mono
نوعی است که به یک “مقدار مونادیک” اشاره دارد.
@SpringBootApplication
بسیاری از تنظیمات را برای ما انجام داد. ما از یک کلاس داخلی به نام EchoController
با حاشیهنویسی @RestController
استفاده میکنیم تا به Spring بفهمیم از چه نقاط پایانی استفاده میکنیم.
سه مثال در فهرست ۱ وجود دارد که هر کدام به یک مسیر URL با @GetMapping
نگاشت شده اند. اولین مورد، /hello
، به سادگی یک تبریک به پاسخ می نویسد. دومی، /echo/{str}
نشان میدهد که چگونه یک پارامتر URL (یک متغیر مسیر) را گرفته و از آن در پاسخ استفاده کنید. و سومی، /echoquery
، نحوه گرفتن پارامتر درخواست (مقادیر URL بعد از علامت سوال) و استفاده از آن را نشان می دهد.
در هر مورد، ما به روش Mono.just()
برای توصیف پاسخ متکی هستیم. این یک راه ساده برای ایجاد یک تولید کننده رویداد در چارچوب Reactor است. میگوید: با یک رویداد که در آرگومان یافت میشود، یک تولیدکننده رویداد بسازید و آن را به همه مشترکین بسپارید. در این حالت، مشترکین توسط چارچوب Spring WebFlux و سرور غیر مسدود کننده میزبان آن اداره می شوند. به طور خلاصه، ما به یک خط لوله کاملا غیر مسدود کننده برای پاسخ دسترسی داریم.
پردازش درخواست ورودی نیز به طور کامل بر روی IO غیر مسدود کننده ساخته شده است. این باعث می شود مقیاس سرور به طور بالقوه بسیار کارآمد باشد زیرا هیچ رشته مسدود کننده ای برای محدود کردن همزمانی وجود ندارد. به خصوص در سیستمهای بلادرنگ، IO غیر مسدود میتواند برای توان عملیاتی کلی بسیار مهم باشد.
Spring WebFlux به طور پیشفرض از سرور Netty استفاده میکند. اگر ترجیح می دهید، می توانید از سرور دیگری مانند Undertow یا یک کانتینر Servlet 3.1 مانند Tomcat استفاده کنید. برای بیشتر در مورد گزینه های سرور.
برنامه نویسی واکنشی با مثال
برنامه نویسی واکنشی مستلزم یک طرز فکر و مجموعه ای از مفاهیم است که ما در اینجا آنها را بررسی نمی کنیم. درعوض، چند مثال را بررسی خواهیم کرد که جنبههای مهم این سبک برنامهنویسی را آشکار میکند.
برای شروع، اجازه دهید یک نقطه پایانی ایجاد کنیم که یک پست آپلود فایل را می پذیرد و محتوا را روی دیسک می نویسد. میتوانید این روش را به همراه واردات آن در فهرست ۲ ببینید. باقیمانده کد ثابت میماند.
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.util.FileSystemUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@PostMapping(value = "/writefile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Mono<String> writeFile(@RequestPart("file") Flux<FilePart> filePartFlux) {
Path path = Path.of("/tmp/file.txt");
// Delete the existing file if it already exists
FileSystemUtils.deleteRecursively(path.toFile());
// Save the file parts to the specified path
return filePartFlux
.flatMap(filePart -> filePart.transferTo(path))
.then(Mono.just("File saved: " + path.toString()));
}
روش writeFile()
با @PostMapping
حاشیه نویسی شده است، و این برای پذیرش آپلود فرم چند قسمتی پیکربندی شده است. تا کنون، این یک پیکربندی عادی بهار وب است. WebFlux به ما امکان می دهد از حاشیه نویسی @RequestPart
در آرگومان متد با یک نوع Flux
استفاده کنیم. این به ما امکان میدهد تکههای چند قسمتی را به روشی واکنشی و غیرمسدود با Flux بپذیریم.
با در دست داشتن filePartFlux
، می توانیم از روش واکنشی flatMap
برای نوشتن آن بر روی دیسک استفاده کنیم: filePartFlux.flatMap(filePart -> filePart. transferTo(path))
. هر “رویداد” از فایل چند بخشی به تابع transferTo
داده می شود تا به فایل اضافه شود. این یک عملیات واکنشی بسیار اصطلاحی است.
استفاده از توابع مرتبه بالاتر مانند flatMap
برای تبدیل و پردازش جریان رویدادها، نوعی برنامهنویسی واکنشی است. میتوان آن را دارای تولیدکنندگان رویداد، مشترکین و ترانسفورماتورهایی مانند flatMap
دانست. با گرفتن یک یا چند جریان و دستکاری آنها با زنجیرهای از متا توابع، میتوانید با نحو نسبتاً ساده به افکتهای قدرتمندی برسید.
برای آزمایش نقطه پایانی جدید، میتوانید از دستور CURL استفاده کنید، همانطور که در فهرست ۳ نشان داده شده است.
$ echo "Facing it, always facing it, that’s the way to get through." >> testfile.txt
$ curl -X POST -F "file=@./testfile.txt" http://localhost:8080/writefile
File saved: /tmp/file.txtmatthewcarltyson@dev3:~/spring-reactive2
$ cat /tmp/file.txt
در فهرست ۳، ما یک فایل testfile.txt
با مقداری محتوا ایجاد میکنیم (“روبهروی آن، همیشه روبهروی آن، این راه عبور است”)، سپس آن را به نقطه پایانی ارسال میکنیم، دریافت میکنیم. پاسخ، و محتوای فایل جدید را تأیید کنید.
استفاده از کلاینت واکنش پذیر HTTP
اکنون، اجازه دهید یک نقطه پایانی ایجاد کنیم که پارامتر ID را بپذیرد. ما از سرویس گیرنده HTTP واکنشی Spring برای درخواست کاراکتر در آن شناسه در SWAPI (Star Wars API) استفاده خواهیم کرد. ، سپس داده های کاراکتر را برای کاربر ارسال می کنیم. میتوانید این روش جدید apiChain()
و واردات آن را در فهرست ۴ مشاهده کنید.
import org.springframework.web.reactive.function.client.WebClient;
@GetMapping("character/{id}")
public Mono<String> getCharacterData(@PathVariable String id) {
WebClient client = WebClient.create("https://swapi.dev/api/people/");
return client.get()
.uri("/{id}", id)
.retrieve()
.bodyToMono(String.class)
.map(response -> "Character data: " + response);
}
اکنون اگر به localhost:8080/character/10
بروید، اطلاعات بیوگرافی Obi-Wan Kenobi را دریافت خواهید کرد.
فهرست ۴ با پذیرش پارامتر مسیر ID و استفاده از آن برای ارسال درخواست به کلاس WebClient
کار می کند. در این مورد، ما در حال ایجاد یک نمونه برای درخواست خود هستیم، اما شما می توانید یک WebClient
با یک URL پایه ایجاد کنید و سپس از آن به طور مکرر با مسیرهای زیادی استفاده کنید. شناسه را در مسیر قرار می دهیم و سپس retrieve
را فراخوانی می کنیم و سپس bodyToMono()
را فراخوانی می کنیم که پاسخ را به Mono تبدیل می کند. به یاد داشته باشید که همه این موارد غیرمسدود و ناهمزمان باقی میمانند، بنابراین کدی که منتظر پاسخ SWAPI است، رشته را مسدود نمیکند. در نهایت، ما از map()
برای فرمولبندی پاسخ به کاربر استفاده میکنیم.
اثر کلی همه این مثالها فعال کردن یک پشته با کارایی بالا از سرور تا کد شما، با کمترین سر و صدا است.
نتیجه گیری
برنامه نویسی واکنشی روشی متفاوت از طرز تفکر آشناتر است که می تواند استدلال در مورد سناریوهای ساده را دشوارتر کند. همچنین یافتن برنامه نویسانی که برنامه نویسی واکنشی را درک می کنند دشوارتر است. به طور کلی، الزامات فنی یا تجاری خوب باید تعیین کند که آیا از یک چارچوب واکنشی در مقابل یک چارچوب استانداردتر استفاده می کنید.
پست های مرتبط
Java Reactive با Spring WebFlux و Reactor
Java Reactive با Spring WebFlux و Reactor
Java Reactive با Spring WebFlux و Reactor