Bagaimana menangani keadaan awal dalam arsitektur yang digerakkan oleh peristiwa?

33

Dalam arsitektur yang digerakkan oleh peristiwa, setiap komponen hanya bertindak ketika suatu peristiwa dikirim melalui sistem.

Bayangkan sebuah mobil hipotetis dengan pedal rem dan lampu rem.

  • Rem bergantian cahaya pada saat menerima brake_on acara, dan off ketika menerima brake_off acara.
  • Pedal rem mengirimkan acara brake_on saat ditekan ke bawah, dan acara brake_off saat dilepaskan.

Ini semua baik dan bagus, sampai Anda memiliki situasi di mana mobil dihidupkan dengan pedal rem sudah ditekan . Karena lampu rem tidak pernah menerima acara rem_on , lampu rem akan tetap mati - jelas situasi yang tidak diinginkan. Menyalakan lampu rem secara default hanya membalikkan situasi.

Apa yang bisa dilakukan untuk menyelesaikan 'masalah keadaan awal' ini?

EDIT: Terima kasih atas semua tanggapan. Pertanyaan saya bukan tentang mobil yang sebenarnya. Di mobil mereka memecahkan masalah ini dengan terus mengirim negara - karena itu tidak ada masalah startup di domain itu. Dalam domain perangkat lunak saya, solusi itu akan menggunakan banyak siklus CPU yang tidak perlu.

EDIT 2: Selain jawaban @ gbjbaanb , saya akan mencari sistem di mana:

  • pedal rem hipotetis, setelah inisialisasi, mengirimkan peristiwa dengan kondisinya, dan
  • lampu rem hipotetis, setelah inisialisasi, mengirimkan peristiwa yang meminta keadaan peristiwa dari pedal rem.

Dengan solusi ini, tidak ada ketergantungan antara komponen, tidak ada kondisi balapan, tidak ada antrian pesan untuk basi, dan tidak ada komponen 'master'.

Frank Kusters
sumber
2
Hal pertama yang terlintas dalam pikiran adalah untuk menghasilkan acara "sintetis" (sebut saja initialize) yang berisi data sensor yang dibutuhkan.
msw
Bukankah seharusnya pedal mengirim acara brake_pedal_on, dan rem yang sebenarnya mengirim acara brake_on? Saya tidak ingin lampu rem saya menyala jika rem tidak bekerja.
bdsl
3
Sudahkah saya menyebutkan bahwa itu adalah contoh hipotetis? :-) Ini sangat disederhanakan untuk menjaga pertanyaan singkat dan to the point.
Frank Kusters

Jawaban:

32

Ada banyak cara untuk melakukan ini, tetapi saya lebih memilih untuk menjaga sistem berbasis pesan sedapat mungkin dipisahkan. Ini berarti bahwa keseluruhan sistem tidak dapat membaca status komponen apa pun, atau komponen apa pun membaca status komponen lainnya (karena dengan demikian terletak ikatan spaghetti dari ketergantungan).

Jadi, sementara sistem yang berjalan akan menjaga dirinya sendiri, kita perlu cara untuk memberitahu setiap komponen untuk memulai sendiri, dan kita sudah memiliki hal seperti itu dalam pendaftaran komponen, yaitu pada saat startup sistem inti harus menginformasikan setiap komponen bahwa itu adalah sekarang terdaftar (atau akan meminta setiap komponen untuk mengembalikan rinciannya sehingga dapat didaftarkan). Ini adalah tahap di mana komponen dapat melakukan tugas-tugas startupnya, dan dapat mengirim pesan seperti yang akan dilakukan dalam operasi normal.

Jadi pedal rem, ketika kunci kontak dimulai, akan menerima pesan registrasi / cek dari manajemen mobil dan itu akan mengembalikan tidak hanya pesan "Aku di sini dan bekerja", tetapi kemudian akan memeriksa keadaannya sendiri dan mengirim pesan untuk keadaan itu (mis. pesan yang ditekan pedal).

Masalahnya kemudian menjadi salah satu ketergantungan startup, karena jika lampu rem belum terdaftar maka tidak akan menerima pesan, tetapi ini mudah diselesaikan dengan mengantri semua pesan ini sampai sistem inti telah menyelesaikan startup, pendaftaran dan pemeriksaan rutinnya .

Manfaat terbesar adalah bahwa tidak ada kode khusus yang diperlukan untuk menangani inisialisasi kecuali bahwa Anda sudah harus menulis (ok, jika pengiriman pesan Anda untuk acara pedal rem ada dalam handler pedal rem, Anda harus memanggil itu dalam inisialisasi Anda juga , tapi itu biasanya bukan masalah kecuali Anda telah menulis kode yang sangat terkait dengan logika handler) dan tidak ada interaksi antara komponen kecuali yang sudah mereka kirim satu sama lain seperti biasa. Arsitektur message passing sangat baik karena ini!

gbjbaanb
sumber
1
Saya suka jawaban Anda, karena menjaga semua komponen dipisahkan - itu adalah alasan paling penting untuk memilih arsitektur ini. Namun, saat ini tidak ada komponen 'master' nyata yang memutuskan sistem dalam keadaan 'diinisialisasi' - semuanya baru saja mulai berjalan. Dengan masalah dalam pertanyaan saya sebagai hasilnya. Setelah master memutuskan sistem berjalan, ia dapat mengirim acara 'sistem diinisialisasi' ke semua komponen, setelah itu setiap komponen mulai menyiarkan keadaannya. Masalah terpecahkan. Terima kasih! (Sekarang saya baru saja pergi dengan masalah bagaimana memutuskan apakah sistem diinisialisasi ...)
Frank Kusters
Bagaimana dengan meminta operator pembaruan status melacak pembaruan terbaru yang diterima dari setiap objek, dan setiap kali permintaan berlangganan baru diterima, sudahkah ia mengirimkan pelanggan baru pembaruan terbaru yang telah diterima dari sumber acara terdaftar?
supercat
Dalam hal ini Anda juga harus melacak kapan acara berakhir. Tidak semua acara dapat dipertahankan untuk selamanya untuk setiap komponen baru yang mungkin mendaftar.
Frank Kusters
@spaceknarf, dalam hal "semuanya mulai berjalan" Anda tidak dapat membangun ketergantungan ke dalam komponen sehingga pedal dimulai setelah cahaya, Anda hanya harus menjalankannya dalam urutan itu, meskipun saya membayangkan sesuatu mulai berjalan, jadi jalankan mereka dalam urutan 'benar' (misalnya skrip init startup linux sebelum systemd di mana layanan untuk memulai pertama disebut 1.xxx dan ke-2 disebut 2.xxx dll).
gbjbaanb
Script dengan urutan seperti itu rapuh. Ini berisi banyak dependensi implisit. Sebaliknya, saya berpikir jika Anda memiliki komponen 'master', yang memiliki daftar komponen yang dikonfigurasi secara statis yang harus dijalankan (seperti yang disebutkan oleh @Lie Ryan), maka ia dapat menyiarkan acara 'siap' setelah semua komponen dimuat. Menanggapi itu, semua komponen menyiarkan keadaan awal mereka.
Frank Kusters
4

Anda dapat memiliki acara inisialisasi yang menetapkan status secara tepat saat memuat / memulai. Ini dapat diinginkan untuk sistem atau program sederhana yang tidak termasuk beberapa bagian perangkat keras, namun untuk sistem yang lebih rumit dengan banyak komponen fisik saat Anda menjalankan risiko yang sama dengan tidak menginisialisasi sama sekali - jika acara "rem pada" dilewatkan atau hilang selama komunikasi Anda sistem (misalnya, sistem berbasis CAN) Anda mungkin secara tidak sengaja mengatur sistem Anda mundur seolah-olah Anda memulainya dengan rem ditekan. Semakin banyak pengendali yang mungkin Anda miliki, seperti dengan mobil, semakin tinggi kemungkinan ada sesuatu yang terlewatkan.

Untuk menjelaskan hal ini, Anda dapat meminta logika "rem pada" berulang kali mengirimkan "rem pada". Mungkin setiap 1/100 detik atau sesuatu. Kode Anda yang berisi otak dapat mendengarkan peristiwa-peristiwa ini dan memicu "rem on" ketika sedang menerima mereka. Setelah 1 / 10sec dari tidak menerima sinyal "rem pada" itu memicu acara "rem_off" internal.

Peristiwa yang berbeda akan memiliki persyaratan waktu yang sangat berbeda. Di mobil, lampu rem Anda harus jauh lebih cepat daripada lampu bahan bakar cek Anda (di mana penundaan multisecond mungkin diterima) atau sistem lain yang kurang penting.

Kompleksitas sistem fisik Anda akan menentukan pendekatan mana yang lebih tepat. Mengingat contoh Anda adalah kendaraan, Anda mungkin ingin sesuatu yang mirip dengan yang terakhir.

Either way, dengan sistem fisik, Anda TIDAK ingin bergantung pada satu peristiwa yang diterima / diproses dengan benar. Mikrokontroler yang terhubung pada sistem jaringan sering kali memiliki batas waktu "Saya hidup" karena alasan ini.

enderland
sumber
dalam sistem fisik Anda akan menjalankan kawat dan menggunakan logika biner: TINGGI adalah rem tertekan dan RENDAH rem tidak tertekan
ratchet freak
@ scratchetfreak ada banyak kemungkinan untuk hal semacam ini. Mungkin saklar bisa mengatasinya. Ada banyak peristiwa sistem lain yang tidak ditangani dengan mudah.
enderland
1

Dalam hal ini, saya tidak akan memodelkan rem sebagai on / off sederhana. Sebaliknya, saya akan mengirim acara "tekanan rem". Misalnya, tekanan 0 akan menunjukkan mati dan tekanan 100 akan sepenuhnya tertekan. Sistem (simpul) akan secara konstan mengirimkan peristiwa tekanan putus (pada interval tertentu) ke pengontrol sesuai kebutuhan.

Ketika sistem dimulai itu akan mulai menerima peristiwa tekanan sampai dimatikan.

Jon Raynor
sumber
1

Jika satu-satunya cara Anda menyampaikan informasi negara adalah melalui acara, maka Anda dalam masalah. Sebaliknya, Anda harus dapat keduanya:

  1. menanyakan keadaan pedal rem saat ini, dan
  2. mendaftar untuk acara "keadaan berubah" dari pedal rem.

Lampu rem dapat dilihat sebagai pengamat pedal rem. Dengan kata lain, pedal rem tidak tahu apa-apa tentang lampu rem, dan dapat beroperasi tanpanya. (Ini berarti bahwa setiap gagasan tentang pedal rem secara proaktif mengirimkan "kondisi awal" ke lampu rem tidak dipahami.)

Setelah instantiasi sistem, lampu rem mendaftar dengan pedal rem untuk menerima pemberitahuan pengereman, dan juga membaca keadaan pedal rem saat ini dan menyalakan atau mematikan dirinya sendiri.

Kemudian, notifikasi pengereman dapat diimplementasikan dalam satu dari tiga cara:

  1. saat parameter "kondisi pengereman berubah"
  2. karena sepasang "pedal pengereman sekarang ditekan" dan "pedal pengereman sekarang dilepaskan"
  3. sebagai peristiwa "kondisi pedal putus baru" dengan parameter "tertekan" atau "dilepaskan".

Saya lebih suka pendekatan pertama, yang berarti bahwa setelah menerima pemberitahuan, lampu rem hanya akan melakukan apa yang sudah diketahui caranya: membaca keadaan pedal rem saat ini dan menghidupkan atau mematikannya sendiri.

Mike Nakis
sumber
0

Dalam sistem yang digerakkan oleh peristiwa (yang saat ini saya gunakan dan cintai), saya merasa penting untuk menjaga hal-hal yang terpisah sebanyak mungkin. Jadi dengan gagasan itu dalam pikiran, mari kita selami.

Sangat penting untuk memiliki beberapa status default. Lampu rem Anda akan mengambil status default 'mati' dan pedal rem Anda akan mengambil status default 'naik'. Setiap perubahan setelah itu akan menjadi suatu peristiwa.

Sekarang untuk menjawab pertanyaan Anda. Bayangkan pedal rem Anda diinisialisasi dan ditekan, acara menyala, tetapi belum ada lampu rem untuk menerima acara. Saya merasa paling mudah untuk memisahkan penciptaan objek (di mana pendengar acara akan diinisialisasi) sebagai langkah terpisah sebelum menginisialisasi logika apa pun. Itu akan mencegah segala kondisi balapan seperti yang telah Anda jelaskan.

Saya juga merasa canggung menggunakan dua peristiwa berbeda untuk hal yang secara efektif sama . brake_offdan brake_onbisa disederhanakan menjadi e_brakedengan parameter bool on. Anda dapat menyederhanakan acara Anda dengan cara ini dengan menambahkan data pendukung.

Ikan kecil
sumber
0

Yang Anda butuhkan adalah acara siaran dan kotak masuk pesan. Siaran adalah pesan yang dipublikasikan ke sejumlah pendengar yang tidak ditentukan. Komponen dapat berlangganan untuk acara siaran sehingga hanya menerima acara yang menarik. Ini memberikan decoupling, karena pengirim tidak perlu tahu siapa penerima. Tabel berlangganan perlu dikonfigurasikan secara statis selama instalasi komponen (alih-alih ketika diinisialisasi). Kotak masuk adalah bagian dari router pesan yang bertindak sebagai penyangga untuk menyimpan pesan ketika komponen tujuan sedang offline.

Menggunakan faktur membawa satu masalah, yaitu ukuran kotak masuk. Anda tidak ingin sistem harus mempertahankan semakin banyak pesan untuk komponen yang tidak akan pernah online lagi. Ini penting terutama dengan sistem tertanam dengan batasan memori yang ketat. Untuk mengatasi batas ukuran kotak masuk, semua pesan yang disiarkan perlu mengikuti beberapa aturan. Aturannya adalah:

  1. setiap acara siaran membutuhkan nama
  2. pada waktu tertentu, pengirim acara siaran hanya dapat memiliki satu siaran aktif dengan nama yang ditentukan
  3. efek yang disebabkan oleh acara harus idempoten

Nama siaran harus dinyatakan selama waktu pemasangan komponen. Jika suatu komponen mengirimkan siaran kedua dengan nama yang sama sebelum penerima memproses yang sebelumnya, siaran yang baru menimpa yang sebelumnya. Sekarang Anda dapat memiliki batas ukuran kotak masuk statis, yang dapat dijamin tidak akan pernah melebihi ukuran tertentu dan dapat dihitung berdasarkan tabel langganan.

Akhirnya, Anda juga membutuhkan arsip siaran. Arsip siaran adalah tabel yang menampung acara terakhir dari setiap nama siaran. Komponen-komponen baru yang baru saja diinstal akan memiliki kotak masuknya yang diisi sebelumnya dengan pesan-pesan dari arsip siaran. Seperti kotak masuk pesan, arsip siaran juga dapat memiliki ukuran statis.

Selain itu, untuk menangani situasi di mana router pesan itu sendiri sedang offline, Anda juga perlu kotak keluar pesan. Kotak keluar pesan adalah bagian dari komponen yang menyimpan pesan keluar sementara.

Lie Ryan
sumber