مقایسه WebFlux و Spring MVC با JMeter و Kotlin

1. مقدمه
در بسیاری از مواقع، ما نیاز داریم که از برخی ادعاهایی که در توسعه نرمافزار داریم، نسخه پشتیبان تهیه کنیم. در اغلب موارد، ما نیاز داریم که ارزش ادعاهای خود را به وضوح به سهامداران، مشاغل و مدیریت نشان دهیم. بسیاری از اوقات ما ارزش واقعی را در تغییر برخی از فناوری ها می بینیم و مزایای بلندمدت را در انجام این کار می بینیم. این که آیا پیشرفتهایی که مشاهده میکنیم مربوط به عملکرد، خوانایی کد، قابلیت نگهداری یا مدولار بودن هستند، آنچه در واقع برای کسبوکار مهم است پارامترهای دیگر هستند. اینها می توانند هر چیزی مانند تحویل سریع، فعال کردن ویژگی، تصویر، تجربه کاربر، سود و رشد کسب و کار باشند. به این ترتیب، اگر ما نیاز به استدلال داریم که چرا باید به سمت یک فناوری جدید گام برداریم، باید بتوانیم آن را توجیه کنیم. خیلی اوقات این کار آسانی نیست. ما باید اهداف تجاری را به وضوح شناسایی کنیم و روشن کنیم که پیشنهادات ما پیشرفت های قابل توجهی را به همراه خواهد داشت.
مدتی است که صحبت هایی در مورد برنامه نویسی واکنشی دیده ام و تمرکز من همیشه روی WebFlux بوده است. RxJava در افق است اما موضوع مقاله دیگری خواهد بود.
در این مقاله، من چند نکته را در مورد خوانندگان فرض می کنم. تجربه و دانش در مورد معماری های MVC و واکنشی بسیار مهم است. بنابراین اصول اولیه بسیار مهم است. اجرای دو سرویس Spring boot را طی خواهیم کرد. یکی از معماری مسدود کننده Spring MVC و دیگری از یک پیاده سازی Spring WebFlux Reactive در MVC پیروی می کند. به منظور سادهسازی معناشناسی، از MVC برای اشاره به مدل مسدودکننده MVC و WebFlux برای اشاره به مدل واکنشپذیر MVC استفاده میکنم. این پیاده سازی ها همه با Kotlin ساخته شده اند. تست های واحد با Spock پیاده سازی شده و زبان مورد استفاده Groovy است. این زبانها بسیار شبیه جاوا هستند و بنابراین اگر آنها را نمیدانید، اما جاوا را میدانید، میتوانید مطمئن باشید که کاملاً میتوانید اتفاقات را درک کنید.
برای ساده نگه داشتن مسائل، در این مقاله، اگرچه ما قصد داریم به برخی از تفاوتهای پیادهسازی یک برنامه WebFlux و یک برنامه MVC نگاهی بیندازیم، آزمایشهای ما فقط بر روی جدول هنرمندان متمرکز خواهد بود. در ادامه این مقاله، بقیه برنامه را بیشتر توضیح خواهم داد و مثال را بیشتر گسترش می دهم. در حال حاضر روی نسخه 1.0.0 تمرکز می کنیم.
بیایید تصور کنیم که باید یک مورد به نفع برنامه نویسی واکنشی ارائه کنیم. ما می خواهیم پیشنهاد کنیم که برنامه قدیمی مسدود کننده MVC خود را به یک برنامه غیرانسدادی MVC WebFlux منتقل کنید. ما هر آنچه را که در مورد برنامه های غیر مسدود می دانیم توضیح می دهیم. ما جادوی مسدود نکردن تاپیک اصلی را توضیح می دهیم. ما توضیح می دهیم که چگونه این در نهایت عملکرد را بهبود می بخشد و چقدر عالی خواهد بود که بتوانیم درخواست های بیشتری را پردازش کنیم. در پروژه ما بازیکنان مختلفی وجود دارد و اغلب، شکاف بین مهندسان و کسب و کار نیاز به تنظیم دارد. این جایی است که بنچمارک وارد میشود. کاری که ما باید در این موارد انجام دهیم این است که به وضوح مزایای تغییر به WebFlux را به تجارت نشان دهیم. صحبت در مورد رشته سرولت، چند رشته ای، الگوی مشترک، تخم ریزی رشته های مختلف به ازای هر مشترک، مقیاس بندی عمودی و JVM می تواند رویکرد خوبی باشد. با این حال، بیشتر اوقات، آنچه ما نیاز داریم این است که بتوانیم در کوتاه ترین زمان ممکن به مزایای انجام این کار اشاره کنیم. چگونه این کار را انجام دهیم؟ این چیزی است که ما در این مقاله خواهیم دید. درست قبل از اینکه ادامه دهیم، لطفاً تفاوت بین فکر کردن به تعداد درخواستهایی که میتوانیم در دقیقه دریافت کنیم و مدت زمان یک درخواست را در نظر بگیرید. بعداً خواهیم دید که چرا باید همین الان به خاطر بسپارید.
برنامه نویسی واکنشی با WebFlux به ما این امکان را می دهد که از داخل ماشین مجازی با رشته های مستقل کوچک مقیاس عمودی را افزایش دهیم. یکی دیگر از جنبه های بسیار مهم این نوع برنامه این است که تولید کنندگان آن مصرف کنندگان را تحت فشار قرار نمی دهند. به عبارت دیگر، با توجه به اینکه همه رویداد محور است، رشته های کوچکی که توسط ناشران ایجاد می شود، در صورت نیاز مشترک را فعال می کند. در نهایت، ذکر این نکته مهم است که با Webflux، سبک برنامه نویسی ما به صورت اعلامی است و نه الزامی. این بدان معنی است که کد ما در رشته اصلی اجرا نمی شود که معمولاً به ما امکان می دهد کد خود را به صورت متوالی اشکال زدایی کنیم.
2. توسعه
2.1. راه اندازی محیط
برای اینکه بتوانید این اپلیکیشن را اجرا کنید و تست های بنچمارک را انجام دهید. جدا از تنظیمات اولیه، باید JMeter را نصب کنیم. در این مقاله به آن خواهیم پرداخت.
ما اجرای دو برنامه Spring boot را بررسی خواهیم کرد. یکی با مدل قدیمی MVC و دیگری با مدل جدید WebFlux پیاده سازی خواهد شد. پس از اجرای docker-compose اینگونه به نظر می رسد:
2.2. ساختار
بیایید اکنون نگاهی به ماژولها و پوشههای اصلی پروژه اصلی که به آن خواهیم پرداخت:
- Concert-Demos-gui — هنوز برای این پروژه در دسترس نیست. این پروژه در مقاله بعدی تکمیل خواهد شد، جایی که ما عمیقتر به WebFlux و پارادایمهای آن خواهیم پرداخت.
- Concert-Demos-rest-service-mvc — برنامه Spring Boot MVC ما.
- کنسرت-نمایش-خدمات استراحت-webflux – برنامه WebFlux ما.
- کنسرت-دمو-داده – مخزن داده که DTO (اشیاء انتقال داده) ما در آن قرار دارد. اینجاست که قرارداد REST ما تعریف میشود و برای هر دو کاربرد مشترک است.
- docker-psql – تعریف و فایلهای تصویر سفارشی Postgres ما. در اینجا ما دو پایگاه داده را تعریف می کنیم: concertsdb-mvc و concertsdb-webflux. آنها جدا هستند تا نتایج تست قابل اعتمادتری داشته باشند.
- jmeter – محل فایل های پیکربندی همه JMeter. در اینجا ما همچنین می توانیم نتایج آزمایش را پیدا کنیم که به ما امکان می دهد در پایان این مقاله نتیجه گیری کنیم.
2.3. مدل ER
برای این مقاله، یک ابزار مدیریت کنسرت ایجاد کرده ام. ویژگیهایی که انتظار میرود حداقل این است که بتوان یک هنرمند را در پایگاه داده ذخیره کرد. در این مورد، ما با دو پایگاه داده متفاوت در سرور محلی PostgreSQL خود کار خواهیم کرد. یک پایگاه داده معماری معمولی MVC (Model View Controller) و پایگاه داده دیگر معماری WebFlux را ارائه می دهد. مورد دوم یکی از پیاده سازی های متعدد معماری برنامه نویسی واکنشی است. اگرچه هر دو پایگاه داده خدمات متفاوتی را ارائه می دهند، اما مدل مشابهی دارند.
بیایید با بررسی مدل های ER هر دو برنامه شروع کنیم:
همانطور که می بینید، این یک مدل بسیار ساده است که در آن ما با یک کنسرت شروع می کنیم. علاوه بر این، ما تعدادی لیست در هر کنسرت داریم. هنگام انجام این کار، می گوییم که کنسرت ما دارای لیست است و هر لیست می تواند بخشی از بسیاری از کنسرت ها باشد. این به عبارت دیگر الف است many to many
رابطه لیست کنسرت در اصل مجموعه ای از یک هنرمند، شعر یک موسیقی است که باید در طول کنسرت از روی قلب بدانیم، و یک لیست کامل از موسیقی هایی که هنرمند اجرا خواهد کرد. موسیقی همچنین می تواند بخشی از لیست های مختلف باشد و در واقع می تواند توسط هنرمندان مختلف خوانده و پخش شود. به همین دلیل است که رابطه بین لیست و موسیقی نیز یک است many to many
.
2.4. Spring MVC در مقابل WebFlux
در مقاله قبلی برنامهنویسی واکنشگرا که برای سرویسهای قدیمی اعمال میشود – یک مثال WebFlux، چند راهحل برای ایجاد یک برنامه کاملا واکنشگرا و متصل به سرویسهای SOAP توضیح دادم. اگرچه این ممکن است برای سازمانهایی که نرمافزار قدیمی دارند کاربرد داشته باشد، یکی از مراحل مهمی که مقاله ندارد، منبع داده واکنشی است. منبع داده ما در آن مقاله فقط یک سرویس صابون بود که به سادگی گفتیم، آن را پیرامون معماری Spring WebFlux پیچیده بودیم. در این مقاله قصد داریم در واقع یک وب اپلیکیشن کاملا واکنشی با استفاده از R2DBC ببینیم.
هر دو معماری می توانند از مدل Onion یا معماری Ports and Adapters پیروی کنند. ما قصد داریم پیاده سازی را انجام دهیم که از قوانین مشترک برای هر دو معماری استفاده می کند. همچنین، به خاطر داشته باشید که اگرچه برنامه های کامل به ما امکان ذخیره کنسرت ها، لیست ها، موسیقی و هنرمندان را می دهند، تمرکز ما برای این مقاله جدول Artist است. بنابراین ما بر روی ایجاد و بازیابی هنرمندان برای هر دو برنامه تمرکز می کنیم.
شاید بهترین راه برای شروع این کار فقط نگاهی به لایه مخزن باشد. بیایید نگاهی به پیادهسازی MVC بیندازیم (من عمداً HashCode و متدهای برابر را به دلیل طولانی بودن آنها حذف میکنم. آنها نیز برای این مقاله مرتبط نیستند).
2.4.1 مدل داده
ابتدا به مدل داده های هنرمند نگاه می کنیم:
@Entity
data class Artist(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,
val name: String? = null,
val gender: Gender? = null,
val careerStart: Long? = null,
val birthDate: String? = null,
val birthCity: String? = null,
val country: String? = null,
val keywords: String? = null
) {}
در Spring MVC، باید مشخص کنیم که کدام پارامتر کلید اصلی است، شناسه ها چگونه تولید می شوند و البته باید مشخص کنیم که POJO (ابجکت جاوا ساده قدیمی) ما به درستی توسط Spring Data JPA (Hibernate در داخل) شناسایی می شود. . این کار با کلیشهسازی POJO ما با @Entity و تنظیم شناسه ما با حاشیهنویسی @id و @GeneratedValue انجام میشود.
بیایید این پیاده سازی را با پیاده سازی WebFlux مقایسه کنیم:
واردات org.jesperancinha.concerts.types.Gender
وارد کردن org.springframework.data.annotation.Id
data class Artist(
@Id var id: Long? = null,
val name: String,
val gender: Gender,
val careerStart: Long,
val birthDate: String,
val birthCity: String,
val country: String,
val keywords: String
) {}
اولین تفاوتی که متوجه می شویم عدم وجود حاشیه نویسی @Entity است و همچنین هیچ حاشیه نویسی خاصی از نسل شناسه نمی بینیم. این تنها راهی است که R2DBC در حال حاضر پیاده سازی می شود. در وضعیت فعلی، خواهیم دید که بسیاری از حاشیهنویسیهایی که دوست داریم با JPA/Hibernate استفاده کنیم، وجود ندارند. دلایل زیادی را می توان برای این مورد اشاره کرد، اما ظاهرا مهمترین آنها این است که بسیاری از حاشیه نویسی های مدل MVC در تضاد مستقیم با اصول برنامه نویسی Reactive هستند. ما باید برنامه خود را تا حد امکان واکنشپذیر کنیم و به این ترتیب چند چیز اساسی مهم مانند واگذاری نسل شناسه به سیستم اصلی پایگاه داده یا عدم نیاز به مشخص کردن اینکه POJO ما نیز یک موجودیت @ است، جنبههای مهم این امر هستند. با این حال، احتمالاً قابل توجه ترین تفاوت بین این دو پیاده سازی نحوه برقراری روابط بین جداول داده است. در ادامه نگاهی به رابطه بین جداول Listing و Artist خواهیم داشت. می دانم که اشاره کردم که تمرکز این مقاله فقط جدول Artist بود. با این حال، با توجه به اینکه این مقاله همچنین در مورد ساخت یک برنامه کاملاً واکنشی است، مهم است که بدانیم چگونه می توانیم روابط ER را کار کنیم.
2.4.2. کارکردن IS
بیایید دو پیاده سازی را در کنار هم مقایسه کنیم. ابتدا، اجازه دهید به اجرای MVC جدول لیستینگ نگاه کنیم:
@Entity
data class Listing(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long = 0,
@OneToOne
val artist: Artist? = null,
@OneToOne
val referenceMusic: Music? = null,
@ManyToMany(cascade = [CascadeType.ALL])
@JoinTable(
name = "listing_music",
joinColumns = [JoinColumn(name = "music_id")],
inverseJoinColumns = [JoinColumn(name = "listing_id")]
)
var musics: MutableSet<Music> = HashSet(),
@ManyToMany(mappedBy = "listings")
val concerts: MutableSet<Concert> = HashSet()
) {}
بیایید روی دو حاشیه نویسی مهم @OneToOne و @ManyToMany تمرکز کنیم. و اکنون نسخه WebFlux:
data class Listing(
@Id var id: Long? = null,
val artistId: Long,
val referenceMusicId: Long
) {}
اجرای اخیر بسیار ساده به نظر می رسد، اما ما روابط خود را از دست داده ایم. این بخشی از مفهوم معماری R2JDB است. اگر لازم باشد روابط را در نظر بگیریم، پس باید وابستگی ها را در نظر بگیریم. درخواست های وابسته تاخیر را افزایش می دهند. آنها همچنین می توانند به طور بالقوه به معنای انتخاب های بیشتری برای پایگاه داده باشند. با این حال، R2JDB راه های جالبی برای ایجاد روابط وابستگی رفع انسداد ارائه می دهد. همچنین راه هایی را برای صدور پرس و جوهای پایگاه داده خاص ارائه می دهد، اما ما بعداً به آنها خواهیم پرداخت. در حال حاضر فقط توجه به این نکته مهم است که حاشیه نویسی که روابطی مانند یک به بسیاری، یک به یک را تعریف می کند، many to many
و many to one
به روش واکنشی R2JDB در دسترس نیستند. ما باید بین دریافت این داده ها از جستارهای JOIN یا از سوی دیگر از یک ساختارساز پیچیده ناشر یکی را انتخاب کنیم.
2.4.3. مخزن
مخازن در هر صورت بسیار ساده و ساده برای پیاده سازی هستند. بیایید نگاهی به کیس MVC بیندازیم:
interface ArtistRepository : CrudRepository<Artist, Long>
و اکنون، مورد R2DBC:
interface ArtistRepository : ReactiveCrudRepository<Artist, Long>
ما فقط تفاوت جزئی را در اینجا می بینیم که یکی از مخزن ها CrudRepository و دیگری ReactiveCrudRepository است. همچنین توجه داشته باشید که آنها از بسته های مختلف می آیند.
2.4.4. خدمات
مانند قبل، ابتدا نگاهی به سرویس MVC بیندازید:
@Service
class ArtistServiceImpl(private val artistRepository: ArtistRepository) : ArtistService {
override fun getAllArtists(): List<ArtistDto>? {
return artistRepository.findAll().map { toArtistDto(it) }
}
override fun createArtist(artist: ArtistDto): ArtistDto {
return toArtistDto(artistRepository.save(toArtist(artist)))
}
}
و اکنون مورد WebFlux:
@Service
class ArtistServiceImpl(private val artistRepository: ArtistRepository) : ArtistService {
override fun getAllArtists(): Flux<ArtistDto>? {
return artistRepository.findAll().map { toArtistDto(it) }
}
override fun createArtist(artist: ArtistDto): Mono<ArtistDto> {
return artistRepository.save(toArtist(artist)).map { toArtistDto(it) }
}
}
در حال حاضر بیایید به خاطر داشته باشیم که ArtistDto کلاسی است که بین هر دو پیاده سازی برنامه به اشتراک گذاشته شده است. در پیاده سازی WebFlux می بینیم که به جای برگرداندن اشیا یا لیستی از آبجکت ها، Flux و Mono را برمی گردانیم. اینها به عنوان ناشر شناخته می شوند. در WebFlux، ناشران را کنترل کننده رویداد نیز می نامند. قبل از پرداختن به این موضوع، اجازه دهید نحوه پیاده سازی کنترلرها را بررسی کنیم.
2.4.5. کنترل کننده ها
در نهایت اجازه دهید به کنترلرها نگاه کنیم. ابتدا، اجازه دهید به اجرای کنترلر برای مورد MVC نگاه کنیم:
@RestController
@RequestMapping("/concerts/data/artists")
class ArtistControllerImpl(private val artistService: ArtistService) : ArtistController {
override fun getAllArtists(): List<ArtistDto>? {
return artistService.getAllArtists()
}
override fun createArtist(@RequestBody artistDto: ArtistDto): ArtistDto {
return artistService.createArtist(artistDto)
}
}
و پیاده سازی برای مورد WebFlux:
@RestController
@RequestMapping("/concerts/data/artists")
class ArtistControllerImpl(private val artistService: ArtistService) : ArtistController {
override fun getAllArtists(): Flux<ArtistDto>? {
return artistService.getAllArtists()
}
override fun createArtist(@RequestBody artistDto: ArtistDto): Mono<ArtistDto> {
return artistService.createArtist(artistDto)
}
}
با توجه به اینکه حداقل منطق تجاری در لایه سرویس ما وجود دارد، ما هیچ چیز را در اجرای کنترلر خود متفاوت نمی یابیم. با در نظر گرفتن این موضوع، ما هنوز باید بفهمیم که چگونه در WebFlux داده ها در نهایت به کاربر ارسال می شوند. پس از برقراری تماس، روش برگشت تماس کنترلر، یک ناشر را به جای DTO ساده (Data Transfer Object) به netty می فرستد. سپس ما netty را داریم که مشترک این ناشران می شود و داده های برگشت تماس را به کاربر برمی گرداند. مشترکین ما Flux و Mono هستند. آنها پس از پاسخ به تماس اعدام می شوند. نکته اینجاست که این فرآیند مستقل از رشته اصلی است که معمولاً درخواستها را در معماری MVC مدیریت میکند. این تفاوت است که ما در این مقاله می خواهیم بررسی کنیم.
2.4.6 Dtos
هر دو برنامه از یک قرارداد REST استفاده می کنند. پروژه. برای نقطه پایانی POST و GET هنرمند ما، قرارداد REST ما بسیار ساده است و با پیام هایی مانند نمونه زیر سازگار است:
{
"name": "Nicky Minaj",
"gender": "FEMALE",
"careerStart": 1000,
"birthDate": "a date",
"birthCity": "Port of Spain",
"country": "Trinidad en Tobago",
"keywords": "Rap"
}
3. تست و محک زدن
3.1. JMeter به طور خلاصه
استفاده از JMeter بسیار آسان است. این در یک نسخه مستقل در دسترس است و هدف اصلی آن انجام تست های بنچمارک است. شرکتهای زیادی از آن استفاده میکنند و علاقهمندان معمولاً بانکها و شرکتهایی هستند که با اطلاعات حساس سروکار دارند. بانک ها معمولاً با تعداد بی پایان تراکنش در سراسر جهان سروکار دارند. فنآوریهای فینتک به راحتی باید با حجم زیادی از دادهها در سیستمهای هوش مصنوعی (هوش مصنوعی) و ML (یادگیری ماشین) خود مقابله کنند. شرکتهایی که با دادههای حسگرها سر و کار دارند، باید در مورد عملکرد نیز به ویژه دقیق باشند. ما JMeter می توانیم به راحتی آزمایش هایی را از جعبه انجام دهیم.
در مورد ما، ما قصد داریم چندین هنرمند خلق کنیم و آنها را همزمان بدست آوریم. به عبارت دیگر، تست های ما شامل درخواست های POST و درخواست های GET خواهد بود.
بیایید نگاهی بیندازیم که چگونه این اتفاق را میسازیم. از آنجایی که JMeter یک ابزار بسیار آسان برای استفاده است، من فقط نکات اصلی پیکربندی JMeter خود را برجسته می کنم.
ابتدا، بیایید گروه موضوع همزمان BlazeMeter (واقع در Threads (کاربران)/گروه موضوع Concurrency) را پیکربندی کنیم:
کاری که ما در اینجا انجام می دهیم یک پیکربندی فرآیند است. حداکثر 1000 رشته برای 1 دقیقه کامل تولید می شود و آنها به طور همزمان از هر نمونه برداری استفاده می کنند و از خدمات ما درخواست می کنند. همزمانی به صورت خطی تا 1000 افزایش می یابد.
بیایید اکنون نمونه های خود را پیکربندی کنیم. می توانید آنها را در Sampler/Http Request پیدا کنید. اساساً همه آنها به یک شکل پیکربندی شده اند:
یک بار GET
و POST
از آنجایی که MVC و WebFlux هر دو پیکربندی شده اند (4 نمونه)، ما هنوز باید درخواست های پست خود را پیکربندی کنیم. به یاد داشته باشید که اطلاعات از طریق پیامهای فرمتشده JSON رد و بدل میشود و JSON نحوه تعریف قرارداد خدمات REST ما است. ما هنوز باید آن را در درخواست های پست خود تعریف کنیم.
برای هر یک از نمونهگیرهای POST ما اجازه میدهیم پارامترهای Http را اضافه کنیم (اینها را در Config Element/Http Header Manager بیابید):
در تب Body Data مقدار زیر را پر می کنیم:
{
"name": "Nicky Minaj",
"gender": "FEMALE",
"careerStart": 1000,
"birthDate": "a date",
"birthCity": "Port of Spain",
"country": "Trinidad en Tobago",
"keywords": "Rap"
}
با پیکربندی تمام درخواستهای POST و GET، اجازه دهید نمایشهای گرافیکی و جدولی نتایج خود را اضافه کنیم. بیایید فعلاً فقط به دو مورد از آنها نگاهی بیندازیم، زیرا آنها همانهایی هستند که باید بدانیم با WebFlux چه چیزی می توانیم به دست آوریم.
بیایید نمودار زمان پاسخ را پیکربندی کنیم (این یکی را در نمودار شنوندگان/زمان پاسخگویی پیدا کنید):
با این پیکربندی میتوانیم ببینیم که میانگین درخواست چقدر طول میکشد.
در مرحله بعد، گزارش خلاصه (شنوندگان/گزارش خلاصه) را پیکربندی می کنیم:
این شنونده اطلاعات خلاصه شده زیادی در مورد درخواست های ما به ما می دهد، اما تعداد درخواست ها را نیز به ما می دهد. این نشان دهنده میزان باری است که خدمات ما می تواند پردازش کند.
3.2. در حال اجرای آزمایشات ما
ما اکنون آماده اجرای تست های واحد خود هستیم. ابتدا تست های MVC و WebFlux را جداگانه اجرا می کنیم و سپس آنها را با هم اجرا می کنیم و می بینیم که آیا می توانیم به نتیجه ای برسیم. برای هر آزمایش، بیایید جهان را به مکانی بهتر تبدیل کنیم و هزاران نیکی میناژ بسازیم و ببینیم که چقدر میتوانیم بسازیم و این سیستم واقعاً چقدر کارآمد است.
تصویر docker PostgreSQL از قبل برای اجرای این تست ها آماده شده است. اگر بخواهیم این تست ها را بدون docker و در مقابل پایگاه داده PostgreSQL دیگری اجرا کنیم، باید ظرفیت آن را برای 2 ویژگی مهم افزایش دهیم:
max_connections = 1500
shared_buffers = 512MB
پس از این باید سرویس PostgresSQL خود را مجددا راه اندازی کنیم.
3.2.1. نتایج MVC
ابتدا نتایج تست های MVC را بررسی می کنیم:
در این مرحله می توانیم متوجه شویم که درخواست های پست ما در مقطعی مسدود شده است. همچنین می بینیم که برخی از درخواست ها تا 20 میلی ثانیه مسدود شده اند. هنوز زمان پاسخگویی به سختی قابل قبول است، اما هنوز قابل قبول است.
بیایید ببینیم گزارش خلاصه چه می گوید:
برچسب بزنید | نمونه ها | میانگین | حداقل | حداکثر | Std. توسعه دهنده | خطای Per | توان عملیاتی | KB/sec دریافت شد | KB/s ارسال شد | میانگین بایت ها |
---|---|---|---|---|---|---|---|---|---|---|
MVC Get Artists | 1149 | 10235 | 78 | 33906 | 6867.42 | 0.261 | 18.86142 | 12214.32 | 2.52 | 663124.1 |
هنرمندان پست MVC | 750 | 9812 | 9 | 33294 | 7136.17 | 0.267 | 12.38359 | 4.42 | 4.99 | 365.4 |
TOTAL | 1899 | 10068 | 9 | 33906 | 6977.87 | 0.263 | 31.17305 | 12218.71 | 7.48 | 401371.1 |
در این مرحله، فقط باید در نظر بگیریم که مقدار ناچیزی از خطا وجود داشته است و ما توانستیم 1149 درخواست GET و 750 درخواست POST را در مجموع ارسال کنیم.
3.2.2. نتایج WebFlux
در مورد Webflux چیزی شبیه به زیر دریافت می کنیم:
در این مرحله همچنین می بینیم که به نظر می رسد بار بر افزایش تأخیر در زمان پاسخ تأثیر دارد. با این حال، این رفتاری است که توجه به آن جالب است و می تواند به خود PostgreSQL یا سایر عناصر در خط لوله بین درخواست و پاسخ مربوط باشد. آنچه در اینجا مهم است که در زمان پاسخ به آن توجه کنید، این است که به نظر می رسد هیچ تفاوتی بین اولین تست هایی که برای MVC انجام دادیم و تست های WebFlux وجود ندارد. با این حال یک تفاوت قابل توجه این است که با نگاه کردن به نمودار، در هیچ نقطه ای درخواست ها مسدود نشدند. این در حال حاضر یک دستاورد به نفع WebFlux است. اکنون به گزارش خلاصه نگاه می کنیم:
برچسب بزنید | نمونه ها | میانگین | حداقل | حداکثر | Std. توسعه دهنده | خطای Per | توان عملیاتی | KB/sec دریافت شد | KB/s ارسال شد | میانگین بایت ها |
---|---|---|---|---|---|---|---|---|---|---|
هنرمندان WebFlux GET | 1444 | 8952 | 62 | 20804 | 6164.03 | 6.440 | 23.85830 | 7209.95 | 2.99 | 309451.4 |
هنرمندان WebFlux Post | 1013 | 8254 | 6 | 20088 | 6155.44 | 4.837 | 16.75876 | 7.39 | 6.43 | 451.6 |
TOTAL | 2457 | 8664 | 6 | 20804 | 6170.09 | 5.779 | 40.59547 | 7217.33 | 9.41 | 182053.4 |
ما توانستیم 1444 درخواست GET و 1013 درخواست POST را در مجموع ارسال کنیم. بیایید نتایج خود را واقعی تر کنیم و درخواست های ناشی از خطا را حذف کنیم. از این نظر ما 1444-1444 * 0.0644 = 1351 درخواست GET و 1013-1013 * 0.0483 = 964 درخواست POST داریم. اگر این نتایج را با WebFlux مقایسه کنیم، می بینیم که برای درخواست های POST و برای همان مورد، 1351-1149 = 202 درخواست GET به دست آوردیم. و در نهایت ما 964-750 = 214 / این یک سود عالی است. با توجه به اینکه من این تست ها را روی یک لپ تاپ بسیار متوسط (Google Chrome Notebook ASUS C302C) انجام دادم. این در حال حاضر نشانه ای از چیزهای بزرگ در یک محیط تولید است
3.2.3. نتایج مختلط
حال بیایید نگاهی بیندازیم که وقتی هر دو برنامه را در محیطی اجرا می کنیم که به جای استفاده از پایگاه های داده جداگانه، از یک سرور استفاده می کنند چه اتفاقی می افتد. در این مورد من این تست ها را با حداکثر 800 رشته همزمان اجرا کردم تا از خطا جلوگیری کنم.
این هم نتیجه ما:
اگر به نتایج خود نگاه کنیم، ممکن است نتیجه بگیریم که زمان پاسخگویی WebFlux سریعتر است. متأسفانه، اگر این تست ها را چندین بار اجرا کنیم، می بینیم که بسته به زمان اجرای این تست ها، عملکرد بسیار تغییر می کند.
از آنجایی که نمی توانیم با این نتایج نتیجه گیری زیادی کنیم، بیایید به جدول خلاصه نگاهی بیندازیم:
برچسب بزنید | نمونه ها | میانگین | حداقل | حداکثر | Std. توسعه دهنده | خطای Per | توان عملیاتی | KB/sec دریافت شد | KB/sec ارسال شد | میانگین بایت ها |
---|---|---|---|---|---|---|---|---|---|---|
هنرمندان WebFlux GET | 1818 | 1714 | 7 | 22403 | 2846.73 | 0.000 | 39.69346 | 4512.75 | 5.31 | 116418.6 |
MVC Get Artists | 1510 | 3320 | 7 | 16520 | 3516.68 | 0.066 | 33.09589 | 5812.04 | 4.42 | 179826.8 |
هنرمندان WebFlux Post | 1394 | 1425 | 4 | 14886 | 2597.78 | 0.000 | 30.97984 | 10.26 | 12.49 | 339.1 |
هنرمندان پست MVC | 1179 | 2813 | 5 | 20344 | 3265.32 | 0.000 | 26.18371 | 8.69 | 10.56 | 340.0 |
TOTAL | 5901 | 2276 | 4 | 22403 | 3160.81 | 0.017 | 128.35795 | 10282.47 | 32.26 | 82030.3 |
آنچه از این جدول به دست می آوریم مانند موارد فردی است. در این حالت درصد خطا ناچیز است. برای درخواستهای GET افزایشی بین 1818–1510 = 308 دریافت میکنیم. و در نهایت برای درخواستهای POST افزایشی بین 1394–1179 = 215 دریافت میکنیم.
4. نتیجه گیری
همانطور که در آزمایش خود دیدیم، تعیین اینکه آیا WebFlux گزینه مناسبی برای یک تغییر معماری بالقوه است، می تواند دشوار باشد. خوشبختانه ما به راحتی می توانیم با اندازه گیری عملکرد، مزایای چنین تغییری را تعیین کنیم.
در این مقاله روشی برای ساخت آسان اپلیکیشن Reactive تنها با یک جدول دیده ایم. ما زمان پاسخگویی و ظرفیت را بین معماری MVC و معماری WebFlux مقایسه کرده ایم.
پس از اندازهگیری نتایج، واقعاً نتوانستیم از WebFlux نسبت به زمان پاسخدهی دفاع کنیم. با این حال، از نظر بارگیری، برنامه WebFlux ما قادر به پردازش درخواست های بسیار بیشتری در یک دقیقه بود.
با این نتایج، دیگر نیازی به ارائه راه حل خود به صورت کاملا فنی نداریم. تنها کاری که باید انجام دهیم این است که توضیح دهیم که یک برنامه واکنشی در شرایط یکسان قادر به پردازش درخواست های بسیار بیشتری نسبت به یک برنامه MVC خواهد بود.
بیایید دو مثال از اجرای برنامه های MVC را مرور کنیم:
مثال 1: یک برنامه بانکی توسط دسته ای از مشتریان استفاده می شود که دارای حساب بانکی با حجم بالا هستند. این یک برنامه کاربردی است که اغلب استفاده نمی شود، اما برای بانک اهمیت زیادی دارد. باید عملکرد خوبی داشته باشد و همیشه در دسترس باشد. میانگین استفاده حدود 500 بار در هفته است. آیا این نمونه ای برای استفاده از برنامه نویسی واکنشی است؟
R: برنامه نویسی واکنشی همیشه یک چیز عالی برای استفاده است. با این حال، در این مورد، برنامه در حال حاضر کار می کند. احتمالاً هیچ بهبود عملکردی وجود نخواهد داشت. فقط درخواست های کافی برای به چالش کشیدن معماری مسدودکننده MVC وجود ندارد. هیچ مزیتی وجود ندارد و کار زیادی برای انجام دادن وجود دارد. این برای توسعه دهندگان لذت بخش خواهد بود، اما برای تجارت یک پروژه گران قیمت خواهد بود. هیچ دلیلی برای ایجاد این تغییر در اینجا وجود ندارد.
مثال 2: شرکتی که حجم عظیمی از داده ها را از حسگرهایی که در اطراف ساختمان ها قرار داده شده است دریافت می کند، باید تا حد امکان سریع باشد. تقاضای زیادی برای عملکرد وجود دارد. برنامهای که در حال اجرا است باید دادههای سری زمانی را به گونهای ارائه کند که تجهیزات آنها با بیشترین سرعت ممکن نسبت به تغییرات داده واکنش نشان دهند. آیا این مثال می تواند نمونه ای برای مهاجرت به یک مدل برنامه نویسی Reactive باشد؟
ر: حتما! اگر ما نیاز به انتخاب بین MVC ماندن یا فعال شدن MVC داشته باشیم، در این مورد باید واکنش نشان دهیم. همانطور که با نتایج ما ثابت شد، برنامه های کاربردی واکنشی می توانند بار بسیار بیشتری را اشغال کنند و برای مدت طولانی تری پاسخگو بمانند. این یک نگرانی معتبر برای چنین برنامهها/فرآیندهایی است که نیاز به تحمل بار زیادی به طور همزمان دارند.
به عبارت دیگر، سه رکن مهم Reactive Applications را در عمل دیده ایم. دیدهایم که یک پیادهسازی واکنشی مانند WebFlux میتواند بسیار انعطافپذیرتر از پیادهسازی معماری MVC مسدودکننده باشد. دیدیم که در موارد بارگذاری بالا، برنامه ما همچنان پاسخگو بود. ما همچنین دیدیم که چگونه ماهیت ناهمزمان WebFlux در زمان واقعی کار می کند.
من تمام کد منبع این برنامه را در گیت هاب قرار داده ام
امیدوارم از این مقاله به همان اندازه که من از نوشتن آن لذت بردم لذت برده باشید.
من دوست دارم نظرات شما را در مورد آن بشنوم، پس لطفا نظرات خود را در زیر بنویسید.
پیشاپیش از کمک شما متشکریم و از خواندن شما متشکرم!
5. منابع