Bangun Aplikasi Berbasis Peristiwa yang Dapat Diskalakan Dengan Nest.js
Pada artikel ini, saya ingin membahas tentang elemen aplikasi berbasis peristiwa yang dapat diskalakan yang tersedia untuk pengembang dengan kerangka kerja Nest.js. Saya akan menunjukkan betapa mudahnya memulai dengan framework modern untuk membangun aplikasi Node.js backend.
Agenda
What is Nest.js?
How Does Nest.js Help Build Highly-Scalable Apps?
Demo App and Tools
Demo App in Action
Apa itu Nest.js ?

Ini adalah kerangka kerja untuk membangun aplikasi Node.js.
Itu terinspirasi oleh Angular dan sangat bergantung pada TypeScript.
Jadi ini memberikan pengalaman pengembangan yang agak aman. Ini masih JavaScript setelah diubah, jadi Anda harus berhati-hati saat menghadapi risiko keamanan umum.
Ini adalah kerangka kerja yang cukup populer, dan Anda mungkin pernah mendengarnya.

Mengapa menggunakan kerangka lain?
- Injeksi ketergantungan
- Integrasi abstrak dengan database
- Kasus penggunaan umum abstrak: caching, konfigurasi, versi dan dokumentasi API, penjadwalan tugas, antrean, logging, cookie, peristiwa, dan sesi, validasi permintaan, server HTTP (Express atau Fastify), auth.
- TypeScript (dan dekorator)
- Elemen desain lain untuk aplikasi hebat: Middleware, filter Pengecualian, Penjaga, Pipa, dan sebagainya.
- Dan beberapa lagi, yang akan saya bicarakan nanti
Salah satu keuntungan utama menggunakan kerangka kerja adalah memiliki injeksi ketergantungan. Ini menghilangkan overhead untuk membuat dan mendukung pohon ketergantungan kelas.
Ini memiliki integrasi abstrak dengan sebagian besar database, jadi Anda tidak perlu memikirkannya. Beberapa paket yang paling berkembang dan populer yang didukung adalah mongoose, TypeORM, MikroORM, dan Prisma.
Ini telah mengabstraksi kasus penggunaan umum untuk pengembangan web seperti caching, konfigurasi, versi dan dokumentasi API, antrian, dll.
Untuk server HTTP, Anda dapat memilih antara Express atau Fastify.
Ini menggunakan TypeScript dan dekorator. Ini menyederhanakan membaca kode, terutama dalam proyek yang lebih besar, dan memungkinkan tim pengembang untuk berada di halaman yang sama saat mempertimbangkan tentang komponen.
Juga, seperti kerangka kerja apa pun, ini menyediakan elemen desain aplikasi lain seperti middleware, filter pengecualian, pelindung, pipa, dan sebagainya.
Dan terakhir, kita akan berbicara nanti tentang beberapa fitur lain yang khusus untuk skalabilitas.
Bagaimana Nest.js Membantu Membangun Aplikasi yang Sangat Skalabel?
Mari kita rangkum strategi utama untuk membangun aplikasi yang sangat skalabel.
Berikut pilihannya:
- Monolit (modular)
- Layanan Mikro
- Didorong oleh peristiwa
- Campuran
Pendekatan pertama yang ingin saya bicarakan adalah menggunakan monolit.

Ini adalah satu aplikasi yang memiliki komponen yang digabungkan dengan erat.
Mereka dikerahkan bersama, didukung bersama, dan biasanya, mereka tidak bisa hidup tanpa satu sama lain.
Jika Anda menulis aplikasi seperti itu, sebaiknya gunakan pendekatan modular, yang sangat bagus di Nest.js.
Saat menggunakan pendekatan modular, Anda dapat secara efektif memiliki satu basis kode, tetapi komponen sistem Anda bertindak sebagai entitas yang agak independen dan dapat dikerjakan oleh tim yang berbeda. Ini menjadi lebih sulit saat tim dan proyek Anda berkembang. Itu sebabnya kami memiliki model lain untuk pengembangan arsitektur.
Layanan Mikro

Layanan mikro adalah saat Anda memiliki penerapan terpisah untuk setiap layanan. Biasanya, setiap layanan hanya bertanggung jawab atas unit kerja kecil dan akan memiliki tokonya sendiri.
Pendekatan berbasis peristiwa mirip dengan layanan mikro.

Sekarang, Anda tidak memiliki komunikasi langsung antar layanan. Sebaliknya, setiap layanan akan memancarkan suatu peristiwa, dan kemudian tidak peduli.
Mungkin ada pendengar acara ini, tetapi tidak ada pendengar. Jika seseorang mengkonsumsi acara tersebut, itu dapat kembali menghasilkan acara lain yang dapat dikonsumsi oleh layanan lain, dan seterusnya.
Akhirnya, seseorang akan menghasilkan respons untuk klien yang menunggu. Itu bisa berupa respons WebSocket atau webhook atau apa pun.
Layanan akan berkomunikasi dengan layanan lain melalui permintaan HTTP atau perpesanan.
Arsitektur campuran

Biasanya, proyek kami yang lebih besar merupakan gabungan dari semua desain — beberapa komponen dipasangkan dengan erat dan diterapkan bersama, beberapa komponen diterapkan secara terpisah, dan beberapa dikomunikasikan secara eksklusif melalui perpesanan acara.
Nest.js = Pengembangan Aplikasi Berbasis Peristiwa yang Mudah
Mari pikirkan mengapa kerangka kerja ini menyederhanakan pengembangan berbasis peristiwa.
- Terintegrasi dengan Redis/Bull untuk manajemen antrean ( github.com/OptimalBits/bull )
- Terintegrasi dengan sebagian besar broker perpesanan
- Mempromosikan pengembangan modular
- Dokumentasi dan contoh yang bagus
- Pengujian unit dan integrasi di-bootstrap (DI, Jest)
Untuk pengembangan dan komunikasi layanan mikro, ia memiliki integrasi dengan broker perpesanan paling populer seperti Redis, Kafka, RabbitMQ, MQTT, NATS, dan lainnya.
Ketiga, ini mempromosikan pengembangan modular, sehingga mudah bagi Anda untuk mengekstrak satu unit kerja nanti dalam siklus hidup proyek.
Poin saya selanjutnya adalah ia memiliki dokumentasi dan contoh yang bagus, yang selalu menyenangkan. Anda dapat menjalankan aplikasi terdistribusi pertama Anda dalam hitungan menit.
Dan hal lain yang ingin saya perhatikan adalah pengujian unit dan integrasi di-bootstrap untuk Anda. Ini memiliki DI untuk pengujian dan semua fitur canggih lainnya dari kerangka pengujian Jest.
Antrian (npm/bull)
Sekarang, mari kita lihat bagaimana antrean sederhana dapat dibuat di NestJS.
Antrian: menambahkan koneksi
Pertama, Anda menginstal dependensi yang diperlukan dengan perintah berikut:
npm install --save @nestjs/bull bull
npm install --save-dev @types/bull

Dan terakhir, daftarkan antrian.

Queues: produser acara menyuntikkan antrian

Selanjutnya, di tempat lain dalam konstruktor layanan, Anda mengetik-isyarat antrean Anda, dan antrean tersebut akan disuntikkan oleh wadah Injeksi Ketergantungan — Anda sekarang memiliki akses penuh ke antrean dan dapat mulai memancarkan peristiwa.
Queues: konsumen acara memproses antrian

Di suatu tempat di modul lain, Anda mendekorasi kelas prosesor Anda dengan Processor() and Process()
pengaturan minimal agar sistem antrian berfungsi.
Anda dapat membuat produsen dan konsumen ada dalam satu aplikasi atau secara terpisah. Mereka akan berkomunikasi melalui perantara pesan pilihan Anda.
Integrasi Perpesanan — Koneksi
Koneksi penyedia pesan dimulai dengan menambahkan koneksi modul klien. Dalam contoh ini, kami memiliki transportasi Redis dan harus menyediakan opsi koneksi khusus Redis.

Integrasi Perpesanan — Produser
Langkah selanjutnya adalah menyuntikkan antarmuka proxy klien ke layanan produser kami.

Opsi kami selanjutnya adalah SEND
metode atau EMIT
.
SEND
biasanya tindakan sinkron, mirip dengan permintaan HTTP, tetapi diabstraksi oleh kerangka kerja untuk bertindak melalui transportasi yang dipilih.
Pada contoh di bawah ini, accumulate()
respons metode tidak akan dikirimkan ke klien hingga pesan diproses oleh aplikasi pendengar.

EMIT
perintah adalah awal alur kerja asinkron, ini akan bertindak sebagai api dan lupakan ATAU di beberapa transportasi, ini akan bertindak sebagai acara antrian yang tahan lama. Ini akan tergantung pada transportasi yang dipilih dan konfigurasinya.

SEND
dan EMIT
pola memiliki kasus penggunaan yang sedikit berbeda di sisi KONSUMEN. Ayo lihat.
Integrasi Perpesanan — Konsumen
MessagePattern
dekorator hanya untuk metode yang mirip sinkronisasi (diproduksi dengan SEND
perintah) dan hanya dapat digunakan di dalam kelas yang didekorasi dengan pengontrol.
Jadi kami mengharapkan tanggapan atas permintaan yang diterima melalui protokol perpesanan kami.

Di sisi lain, EventPattern
dekorator dapat digunakan di kelas kustom apa pun dari aplikasi Anda dan akan mendengarkan peristiwa yang dihasilkan pada antrean ATAU bus peristiwa yang sama, dan tidak mengharapkan aplikasi kita mengembalikan sesuatu.

Pengaturan ini mirip dengan broker perpesanan lainnya. Dan jika itu sesuatu yang khusus, Anda masih dapat menggunakan wadah DI dan membuat penyedia subsistem peristiwa khusus dengan antarmuka Nest.js.


Ini adalah betapa mudahnya untuk berintegrasi dengan broker perpesanan paling umum menggunakan abstraksi Nest.js.
Aplikasi dan Alat Demo
Tersedia di GitHub berikut .
Pada bagian ini, saya akan meninjau bagian dari aplikasi nyata (disederhanakan, tentu saja). Anda bisa mendapatkan kode sumber di halaman GitHub saya untuk mengikuti atau mencobanya nanti. Saya akan mendemonstrasikan bagaimana EDA yang dirancang dengan baik dapat menghadapi tantangan dan bagaimana kita dapat dengan cepat menyelesaikannya dengan alat kerangka kerja.
Ikhtisar aplikasi demo
Pertama-tama mari kita lakukan ikhtisar singkat. Alur kerja yang diharapkan adalah seperti ini:

Kami memiliki tindakan yang telah terjadi di gateway API kami, dan itu menyentuh layanan perdagangan, yang memancarkan suatu peristiwa.
Acara ini masuk ke antrian atau bus acara. Dan kemudian, kami memiliki empat layanan lain yang mendengarkan dan memprosesnya.
Untuk mengamati kinerja aplikasi ini, saya menggunakan aplikasi sampingan yang merupakan "monitor saluran" saya. Ini adalah pola yang ampuh untuk meningkatkan kemampuan pengamatan dan dapat membantu mengotomatiskan penskalaan naik dan turun berdasarkan metrik saluran.

Saya akan menunjukkan cara kerjanya sebentar lagi.
Demo App in Action — Kondisi Normal
Saya menyiapkan Makefile
sehingga Anda dapat mengikuti.
Pertama, jalankan make start
perintah yang akan memulai buruh pelabuhan dengan semua layanan yang diperlukan. Selanjutnya, jalankan make monitor
perintah untuk mengintip metrik aplikasi.
Monitor menampilkan nama antrean, jumlah pekerjaan yang menunggu, jumlah pekerjaan yang diproses, dan jumlah instance pekerja online.

Seperti yang Anda lihat, dalam kondisi normal, jobs_waiting
hitungannya nol, aliran acara lambat, dan kami tidak memiliki pekerjaan yang menumpuk.
Aplikasi ini berfungsi dengan baik dengan jumlah kejadian yang rendah. Tapi apa yang terjadi jika lalu lintas tiba-tiba meningkat?
Aplikasi Demo Beraksi — Lonjakan Lalu Lintas
Anda dapat memulai demo ini dengan menjalankan make start-issue1
perintah dan memulai ulang monitor dengan make monitor
perintah tersebut. Alur acara kami meningkat tiga kali lipat.

Anda akan melihat pada akhirnya di aplikasi monitor bahwa jobs_waiting
hitungan akan mulai meningkat, dan saat kami masih memproses pekerjaan dengan satu pekerja, antrean telah melambat dibandingkan dengan lalu lintas yang meningkat.

Sekarang kita dapat melihat bahwa ini menghambat konfirmasi layanan perdagangan penting kami.
Pekerja akan memproses semua peristiwa tanpa prioritas, sehingga setiap konfirmasi perdagangan baru harus terlebih dahulu menunggu beberapa peristiwa selesai.
Anda dapat membayangkan ini menciptakan waktu respons yang lebih lambat pada aplikasi klien front-end kami untuk pemrosesan perdagangan.
Solusi?
Mari jelajahi opsi yang harus kita perbaiki ini:
- Menskalakan instance pekerja sehingga akan memproses antrean lebih cepat
- Tingkatkan jumlah instance pekerja
- Optimalisasi aplikasi
- Pisahkan antrian
- Prioritaskan acara
Yang kedua adalah meningkatkan jumlah instance pekerja. Ini adalah opsi yang valid tetapi terkadang tidak terlalu hemat biaya.
Selanjutnya, kita dapat memikirkan pengoptimalan aplikasi, termasuk membuat profil, menyelidiki kueri basis data, dan aktivitas serupa. Ini bisa memakan waktu dan tidak memberikan hasil atau perbaikan yang sangat terbatas.
Dua opsi terakhir kami adalah tempat Nest.js dapat membantu kami. Ini untuk memisahkan antrean dan memprioritaskan beberapa acara.
Langkah 1 — Pisahkan Antrian
Saya akan mulai dengan menerapkan metode pemisahan antrian.
Antrean perdagangan hanya akan bertanggung jawab untuk memproses peristiwa konfirmasi perdagangan.
Kode saya untuk ini akan terlihat seperti ini:

Langkah pertama adalah meminta kami PRODUCER
untuk memancarkan TRADE CONFIRM
acara ke antrean baru - TRADES
.
Di sisi konsumen, saya mengekstrak kelas baru yang dipanggil TradesService
dan menugaskannya sebagai pendengar TRADES
antrian.

Layanan QUEUE DEFAULT
pendengar tetap sama. Saya tidak perlu melakukan perubahan apa pun di sini.

Sekarang, apa pun yang terjadi, lonjakan apa pun yang kita miliki — perdagangan tidak akan pernah berhenti diproses (perdagangan akan melambat tetapi tidak akan menunggu peristiwa yang tidak penting).

Anda dapat menjalankan contoh ini dengan start-step1
perintah dan memulai ulang monitor.
Anda akan melihat bahwa antrean perdagangan memiliki jobs_waiting
hitungan nol, namun antrean default masih mengalami masalah.

Dan sekarang, saya akan menerapkan langkah kedua untuk penskalaan berdasarkan informasi yang saya miliki, saya meningkatkan jumlah instance pekerja menjadi satu 3
- DEFAULT QUEUE
satunya.
Langkah 2 — Pekerja Skala

Anda dapat memulai demo ini dengan menjalankan start-step2
perintah dan memulai ulang monitor. Seiring waktu, aplikasi ini menjadi nol jobs_waiting
di kedua antrean, jadi kerja bagus!

Seperti yang dapat Anda pahami, contoh saya sedikit dibuat-buat dan sebagian besar untuk tujuan demo. Anda dapat dengan mudah melihat bagaimana kami dapat memanfaatkan channel monitor patterns
untuk bereaksi secara terprogram terhadap perubahan kinerja aplikasi kami dengan menaikkan atau menurunkan pekerja antrean terpisah.
Solusi — Rekap
Mari kita rekap. Saya menerapkan tiga solusi di sini dari daftar saya:
- Menskalakan instance pekerja sehingga akan memproses antrean lebih cepat
- Tingkatkan jumlah instance pekerja
- Optimalisasi aplikasi
- Antrian terpisah
- Prioritaskan acara
Selanjutnya, saya menambah jumlah instance pekerja untuk DEFAULT QUEUE
to 3
.
Semua ini sebagian besar dilakukan untuk saya oleh Docker dan framework Nest.js.
Langkah selanjutnya yang dapat Anda terapkan hanya dengan menggunakan alat kerangka kerja adalah memprioritaskan beberapa peristiwa lain di atas yang lain. Misalnya, apa pun yang terkait dengan logging atau metrik internal dapat ditunda demi acara yang lebih penting seperti interaksi DB, pemberitahuan, dll.
Repositori dengan kode uji ada di sini: github.com/dkhorev/conf42-event-driven-nestjs-demo .
Untuk wadah dan pengembangan modular, saya menggunakan yang Container Role Pattern
dijelaskan di tautan ini .
Saya harap ini membantu. Semoga berhasil, dan selamat teknik!
Nest.js yang lebih menarik berbunyi: