Halo Dalam artikel ini saya akan berpacu di seluruh Eropa, yaitu, saya akan memberi tahu Anda apa yang mereka maksud dengan pemrograman reaktif, memperkenalkan Anda kepada aktor, aliran reaktif, dan akhirnya, menggunakan aliran reaktif, kami akan mengenali gerakan mouse, seperti di Opera lama dan penerus spiritualnya - Vivaldi .
Tujuannya adalah untuk memperkenalkan konsep dasar pemrograman reaktif dan menunjukkan bahwa tidak semuanya serumit dan seram seperti yang terlihat pada pandangan pertama.
SumberApa itu pemrograman reaktif?
Untuk menjawab pertanyaan ini, kita beralih ke
situs . Ini memiliki gambar yang indah yang menunjukkan 4 kriteria utama yang harus dipenuhi oleh aplikasi reaktif.

Aplikasi harus cepat, toleran terhadap kesalahan dan skala dengan baik.
Sepertinya "kita untuk semua baik versus semua buruk", kan?
Apa yang dimaksud dengan kata-kata ini:
- Responsif
Aplikasi harus memberi pengguna hasil dalam setengah detik. Ini juga termasuk prinsip gagal cepat - yaitu, ketika terjadi kesalahan, lebih baik mengembalikan pesan kesalahan seperti โMaaf, ada masalah. Coba lagi nanti daripada membuat cuaca menunggu di laut. Jika operasinya panjang, kami menampilkan bilah kemajuan kepada pengguna. Jika sangat panjang - โpermintaan Anda akan dipenuhi sementara pada tanggal 18 Maret 2042. Kami akan mengirimkan pemberitahuan kepada Anda melalui pos. " - Skalabilitas adalah cara untuk memberikan responsif di bawah beban. Bayangkan siklus hidup dari layanan yang relatif sukses:
- Peluncuran - aliran permintaan kecil, layanan berjalan pada mesin virtual dengan satu inti.
- Aliran permintaan meningkat - kernel ditambahkan ke mesin virtual dan permintaan diproses dalam beberapa utas.
- Bahkan lebih banyak memuat - kami menghubungkan batching - permintaan ke database dan hard drive dikelompokkan.
- Semakin banyak memuat - Anda perlu meningkatkan lebih banyak server dan menyediakan pekerjaan di kluster.
Idealnya, sistem itu sendiri harus naik atau turun tergantung pada beban.
- Toleransi kesalahan
Kami menerima bahwa kami hidup di dunia yang tidak sempurna dan semuanya terjadi. Jika terjadi kesalahan pada sistem kami, kami harus menyediakan metode penanganan kesalahan dan pemulihan - Dan akhirnya, kami diundang untuk mencapai semua ini menggunakan sistem yang arsitekturnya didasarkan pada pesan berbasis pesan
Sebelum melanjutkan, saya ingin membahas bagaimana sistem yang digerakkan oleh peristiwa berbeda dari sistem yang digerakkan oleh pesan.
Didorong oleh acara:- Peristiwa - sistem melaporkan bahwa ia telah mencapai kondisi tertentu.
- Mungkin ada banyak pelanggan untuk acara tersebut.
- Rantai peristiwa biasanya singkat, dan penangan acara dekat (baik secara fisik dan dalam kode) ke sumbernya.
- Sumber acara dan penangannya biasanya memiliki keadaan umum (secara fisik - mereka menggunakan potongan RAM yang sama untuk pertukaran informasi).
Berbeda dengan event-driven, dalam sistem yang digerakkan oleh pesan:- Setiap pesan hanya memiliki satu penerima.
- Pesan tidak berubah: Anda tidak dapat mengubah apa pun dalam pesan yang diterima sehingga pengirim tahu tentangnya dan dapat membaca informasi.
- Elemen-elemen sistem merespons (atau tidak merespons) untuk menerima pesan dan dapat mengirim pesan ke elemen-elemen lain dari sistem.
Semua ini menawarkan kita
Model aktor
Tonggak perkembangan:
- Penyebutan aktor pertama adalah dalam makalah ilmiah tahun 1973 - Carl Hewitt, Peter Bishop, dan Richard Steiger, "Sebuah formalisme universal ACTOR modular untuk kecerdasan buatan,"
- 1986 - Erlang muncul. Ericson membutuhkan bahasa untuk peralatan telekomunikasi yang akan memberikan toleransi kesalahan dan propagasi bebas kesalahan. Dalam konteks artikel ini, fitur utamanya adalah:
- Semuanya adalah proses
- Pesan adalah satu-satunya cara komunikasi (Erlang adalah bahasa fungsional, dan pesan di dalamnya tidak dapat diubah).
- ..
- 2004 - versi pertama bahasa Scala. Fitur-fiturnya:
- Didukung oleh JVM,
- Fungsional
- Untuk multi-threading, model aktor telah dipilih.
- 2009 - implementasi aktor dialokasikan di perpustakaan terpisah - Akka
- 2014 - Akka.net - porting ke .Net.
Apa yang bisa dilakukan aktor?
Aktor adalah objek yang sama, tetapi:
- Tidak seperti objek biasa, aktor tidak dapat memanggil metode satu sama lain.
- Aktor dapat mengirimkan informasi hanya melalui pesan yang tidak dapat diubah .
- Setelah menerima pesan, aktor dapat
- Buat aktor baru (mereka akan lebih rendah dalam hierarki),
- Kirim pesan ke aktor lain,
- Hentikan aktor di bawah ini dalam hierarki dan diri Anda sendiri.
Mari kita lihat sebuah contoh.

Aktor A ingin mengirim pesan ke Aktor B. Yang dia miliki hanyalah ActorRef (beberapa alamat). Aktor B bisa dimana saja.
Aktor A mengirimkan huruf B melalui sistem (ActorSystem). Sistem meletakkan surat itu di kotak surat aktor B dan aktor "bangun" B. Aktor B mengambil surat itu dari kotak surat dan melakukan sesuatu.
Dibandingkan dengan memanggil metode pada objek lain, itu terlihat tidak perlu rumit, tetapi model aktor sangat cocok di dunia nyata, jika Anda membayangkan bahwa aktor adalah orang yang dilatih untuk melakukan sesuatu dalam menanggapi rangsangan tertentu.
Bayangkan seorang ayah dan seorang putra:

Sang ayah mengirim putranya SMSku "Bersihkan di kamar" dan terus melakukan hal sendiri. Anak itu membaca SMSku dan mulai membersihkan. Ayah, sementara itu, sedang bermain poker. Sang putra selesai membersihkan dan mengirim SMS "Selesai". Itu terlihat sederhana, bukan?
Sekarang bayangkan ayah dan anak itu bukan aktor, tetapi benda biasa yang bisa saling tarik metode. Sang ayah menarik putranya untuk metode "membersihkan kamar" dan mengikuti dengan sendirinya, menunggu sampai putranya selesai membersihkan dan mentransfer kendali kembali ke ayahnya. Ayah tidak bisa bermain poker saat ini. Dalam konteks ini, model aktor menjadi lebih menarik.
Sekarang mari kita beralih ke
Akka.NET
Segala sesuatu yang ditulis di bawah ini benar untuk Akka asli untuk JVM, tetapi bagi saya, C # lebih dekat daripada Java, jadi saya akan menggunakan Akka.NET sebagai contoh.
Jadi apa manfaat Akka?
- Multithreading melalui olahpesan. Anda tidak lagi harus menderita dengan segala macam kunci, semaphore, mutex, dan kesenangan lain yang merupakan karakteristik multithreading klasik dengan memori bersama.
- Komunikasi transparan antara sistem dan komponennya. Tidak perlu khawatir tentang kode jaringan yang kompleks - sistem itu sendiri akan menemukan tujuan pesan dan menjamin pengiriman pesan (di sini Anda dapat memasukkan lelucon tentang UDP vs TCP).
- Arsitektur fleksibel yang dapat secara otomatis menaikkan atau menurunkan. Misalnya, di bawah beban, sistem dapat meningkatkan node cluster tambahan dan mendistribusikan beban secara merata.
Tetapi topik penskalaan sangat luas dan layak untuk publikasi terpisah. Oleh karena itu, saya akan memberi tahu lebih detail hanya tentang fitur, yang akan berguna dalam semua proyek:
Menangani kesalahan
Aktor memiliki hierarki - dapat direpresentasikan sebagai pohon. Setiap aktor memiliki orang tua dan dapat memiliki "anak-anak".
Dokumentasi Akka.NET Hak Cipta 2013-2018 proyek Akka.NETUntuk setiap aktor, Anda dapat menetapkan strategi Pengawasan - apa yang harus dilakukan jika ada yang tidak beres untuk "anak-anak". Misalnya, "mengalahkan" aktor yang memiliki masalah, dan kemudian membuat aktor baru dengan tipe yang sama dan mempercayakan kepadanya dengan pekerjaan yang sama.
Sebagai contoh, saya membuat aplikasi pada Akka.net CRUD, di mana lapisan "logika bisnis" diimplementasikan pada aktor. Tujuan dari proyek ini adalah untuk mengetahui apakah para aktor harus digunakan dalam sistem yang tidak dapat diskalakan - apakah mereka akan membuat hidup lebih baik atau menambah rasa sakit.
Bagaimana penanganan kesalahan bawaan Akka dapat membantu:
- semuanya baik-baik saja, aplikasi berfungsi,
- sesuatu terjadi pada repositori, dan sekarang hanya memberikan hasilnya 1 kali dari 5,
- Saya mengatur strategi Pengawasan untuk "coba 10 kali per detik",
- aplikasi bekerja lagi (walaupun lebih lambat), dan saya punya waktu untuk mencari tahu ada apa.
Ada godaan untuk mengatakan: "Ayo, saya akan menulis kesalahan seperti itu sendiri, mengapa beberapa aktor harus melakukan kesalahan?" Ucapan wajar, tetapi hanya jika poin kegagalannya sedikit.
Dan beberapa kode. Beginilah inisialisasi sistem aktor dalam wadah IoC:
public Container() { system = ActorSystem.Create("MySystem"); var echo = system.ActorOf<EchoActor>("Echo");
EchoActor adalah aktor paling sederhana yang mengembalikan nilai ke pengirim:
public class EchoActor : ReceiveActor { public EchoActor() { Receive<bool>(flag => { Sender.Tell(flag); }); } }
Untuk menghubungkan aktor dengan kode "biasa", perintah Ask digunakan:
public async Task<ActionResult> Index() { ViewBag.Type = typeof(Model); var res = await CrudActorRef.Ask<IEnumerable<Model>>(DataMessage.GetAll<Model>(), maxDelay); return View(res); }
Total
Mencibir dengan para aktor, saya dapat mengatakan:
- Lihatlah mereka jika Anda membutuhkan skalabilitas.
- Untuk logika bisnis yang kompleks, lebih baik tidak menggunakannya karena
- Injeksi Ketergantungan aneh. Untuk menginisialisasi aktor dengan dependensi yang diperlukan, Anda harus terlebih dahulu membuat objek Props, lalu memberikannya ke ActorSystem untuk membuat aktor dengan tipe yang diinginkan. Untuk membuat Alat Peraga menggunakan wadah IoC (misalnya, Castle Windsor atau Autofac) ada pembungkus siap pakai - DependencyResolvers. Tetapi saya dihadapkan dengan kenyataan bahwa wadah IoC berusaha untuk mengontrol masa pakai ketergantungan, dan setelah beberapa saat sistem diam-diam jatuh.
* Mungkin, alih-alih menyuntikkan dependensi ke objek, Anda harus menempatkan dependensi ini sebagai aktor cilik. - masalah mengetik. ActorRef tidak tahu apa-apa tentang tipe aktor yang dimaksud. Artinya, pada waktu kompilasi tidak diketahui apakah aktor dapat memproses pesan jenis ini atau tidak.
Bagian 2: Jet stream
Sekarang mari kita beralih ke topik yang lebih populer dan berguna - aliran jet. Jika Anda tidak pernah bisa bertemu dengan aktor dalam proses kerja, maka aliran Rx tentu akan berguna baik di frontend dan di backend. Implementasinya hampir di semua bahasa pemrograman modern. Saya akan memberikan contoh pada RxJs, karena saat ini bahkan programmer backend terkadang harus melakukan sesuatu dalam JavaScript.
Streaming Rx tersedia untuk semua bahasa pemrograman populer.โ Pengantar Pemrograman Reaktif yang Anda lewatkan โ oleh Andre Staltz , dilisensikan di bawah CC BY-NC 4.0Untuk menjelaskan apa itu jet stream, saya akan mulai dengan tarikan dan dorong koleksi.
| Nilai pengembalian tunggal | Nilai pengembalian berganda |
---|
Tarik Sinkron Interaktif | T | IEnumerable <T> |
Dorong Tidak sinkron Reaktif | Tugas <T> | IObservable <T> |
Tarik koleksi adalah apa yang kita semua terbiasa dalam pemrograman. Contoh yang paling mencolok adalah array.
const arr = [1,2,3,4,5];
Sudah memiliki data, dia sendiri tidak akan mengubah data ini, tetapi dia dapat memberikannya berdasarkan permintaan.
arr.forEach(console.log);
Juga, sebelum Anda melakukan sesuatu dengan data, Anda dapat memprosesnya.
arr.map(i => i+1).map(I => โmy number is โ+i).forEach(console.log);
Sekarang mari kita bayangkan bahwa pada awalnya tidak ada data dalam koleksi, tetapi itu pasti akan memberitahu Anda bahwa mereka telah muncul (Push). Dan pada saat yang sama, kami masih dapat menerapkan transformasi yang diperlukan untuk koleksi ini.
Sebagai contoh:
source.map(i => i+1).map(I => โmy number is โ+i).forEach(console.log);
Ketika nilai seperti 1 muncul di sumber, console.log akan menampilkan "nomor saya adalah 1".
Cara kerjanya:
Entitas baru muncul - Subjek (atau Dapat Diamati):
const observable = Rx.Observable.create(function (observer) { observer.next(1); observer.next(2); observer.next(3); setTimeout(() => { observer.next(4); observer.complete(); }, 1000); });
Ini adalah koleksi push yang akan mengirimkan pemberitahuan tentang perubahan di negaranya.
Dalam hal ini, angka 1, 2 dan 3 akan segera muncul di dalamnya, dalam 4 detik, dan kemudian koleksi akan "berakhir". Ini adalah jenis acara khusus.
Entitas kedua adalah Pengamat. Dia dapat berlangganan acara Subjek dan melakukan sesuatu dengan data yang diterima. Sebagai contoh:
observable.subscribe(x => console.log(x)); observable.subscribe({ next: x => console.log('got value ' + x), error: err => console.error('something wrong occurred: ' + err), complete: () => console.log('done'), }); observable .map(x => 'This is ' + x) .subscribe(x => console.log(x));
Dapat dilihat bahwa satu Subjek dapat memiliki banyak pelanggan.
Itu terlihat mudah, tetapi belum jelas mengapa ini perlu. Saya akan memberikan 2 definisi lagi yang perlu Anda ketahui ketika bekerja dengan aliran reaktif, dan kemudian saya akan menunjukkan dalam praktiknya bagaimana mereka bekerja dan dalam situasi apa potensi penuh mereka terungkap.
Diamati dingin
- Beri tahu tentang peristiwa saat seseorang berlangganan.
- Seluruh aliran data dikirim lagi ke setiap pelanggan, terlepas dari waktu berlangganan.
- Data disalin untuk setiap pelanggan.
Apa artinya ini: katakanlah perusahaan (Subjek) memutuskan untuk mengatur distribusi hadiah. Setiap karyawan (Pengamat) datang untuk bekerja dan menerima salinan hadiahnya. Tidak ada yang dirampas.
Teramati panas
- Mereka mencoba memberi tahu acara tersebut terlepas dari keberadaan pelanggan. Jika pada saat acara tidak ada pelanggan, data hilang.
Contoh: di pagi hari, kue panas untuk karyawan dibawa ke perusahaan. Ketika mereka dibawa masuk, semua lark terbang ke bau dan membuat pai untuk sarapan. Tetapi burung hantu yang datang kemudian tidak mendapatkan pai lagi.
Dalam situasi apa jet stream digunakan?
Ketika ada aliran data yang didistribusikan dari waktu ke waktu. Misalnya, input pengguna. Atau log dari layanan apa pun. Di salah satu proyek, saya melihat logger buatan sendiri yang mengumpulkan peristiwa dalam N detik, dan kemudian secara bersamaan merekam seluruh paket. Kode baterai memenuhi halaman. Jika Rx stream digunakan, maka itu akan jauh lebih sederhana:
โ Referensi / Observasi RxJ , dokumentasi dilisensikan di bawah CC BY 4.0 .
(ada banyak contoh dan gambar yang menjelaskan apa yang dilakukan berbagai operasi dengan aliran reaktif) source.bufferTime(2000).subsribe(doThings);
Dan akhirnya, contoh penggunaan.
Mengenali gerakan mouse dengan aliran Rx
Di Opera lama atau penerus spiritualnya - Vivaldi - ada kontrol browser menggunakan gerakan mouse.
Gif - gerakan mouse di Vivaldi Artinya, Anda perlu mengenali gerakan mouse atas / bawah, kanan / kiri, dan kombinasinya. Itu dapat ditulis tanpa stream Rx, tetapi kodenya akan kompleks dan sulit untuk dipelihara.
Dan inilah tampilannya dengan aliran Rx:
Saya akan mulai dari akhir - Saya akan mengatur data apa dan dalam format apa saya akan mencari dalam urutan asli:
Ini adalah vektor satuan dan kombinasinya.
Selanjutnya, Anda perlu mengonversi acara mouse ke aliran Rx. Semua pustaka Rx memiliki alat bawaan untuk mengubah acara standar menjadi Dapat Diobservasi.
const mouseMoves = Rx.Observable.fromEvent(canvas, 'mousemove'), mouseDowns = Rx.Observable.fromEvent(canvas, 'mousedown'), mouseUps = Rx.Observable.fromEvent(canvas, 'mouseup');
Selanjutnya, saya mengelompokkan koordinat mouse dengan 2 dan menemukan perbedaannya, mendapatkan offset mouse.
const mouseDiffs = mouseMoves .map(getOffset) .pairwise() .map(pair => { return { x: pair[1].x-pair[0].x, y: pair[1].y-pair[0].y } });
Dan kelompokkan gerakan-gerakan ini menggunakan acara 'mousedown' dan 'mouseup'.
const mouseGestures = mouseDiffs .bufferToggle(mouseDowns, x => mouseUps) .map(concat);
Fungsi concat memotong gerakan yang terlalu pendek dan mengelompokkan gerakan yang kira-kira sejajar.
function concat(values) {
Jika gerakan pada sumbu X atau Y terlalu pendek, itu diatur ulang ke nol. Dan kemudian hanya tanda yang tersisa dari koordinat perpindahan yang diperoleh. Dengan demikian, vektor satuan yang kami cari diperoleh.
const normalizedMouseGestures = mouseGestures.map(arr => arr.map(v => { const dist = Math.hypot(vx, vy);
Hasil:
gestures.map(gesture => normalizedMouseGestures.mergeMap( moves => Rx.Observable.from(moves) .sequenceEqual(gesture.sequence, comparer) ).filter(x => x).mapTo(gesture.name) ).mergeAll().subscribe(gestureName => actions[gestureName]());
Menggunakan sequenceEqual, Anda dapat membandingkan gerakan yang diterima dengan yang asli dan, jika ada kecocokan, lakukan tindakan tertentu.
โ
Anda dapat bermain dengan gerakan di siniHarap dicatat bahwa, selain pengenalan gerakan, ada juga gambar gerakan mouse awal dan normal pada kanvas HTML. Keterbacaan kode tidak mengalami hal ini.
Dari mana satu lagi keuntungan berikut - fungsi yang ditulis dengan bantuan aliran Rx dapat dengan mudah ditambahkan dan diperluas.
Ringkasan
- Perpustakaan dengan aliran Rx tersedia untuk hampir semua bahasa pemrograman.
- Aliran Rx harus digunakan ketika ada aliran peristiwa yang membentang dari waktu ke waktu (misalnya, input pengguna).
- Fungsi yang ditulis menggunakan stream Rx dapat dengan mudah ditambahkan dan diperluas.
- Saya tidak menemukan kekurangan yang berarti.