Sans-IO: Rahasia Rust yang Efektif untuk Layanan Jaringan
(firezone.dev)- Di Firezone, Rust digunakan untuk membangun akses jarak jauh yang aman dan dapat diskalakan di ponsel Android, komputer macOS, atau server Linux
- Menggunakan library koneksi bernama
connlibuntuk mengelola koneksi jaringan dan tunnel WireGuard - Setelah melalui banyak iterasi, mereka sampai pada desain sans-IO yang memberikan pengujian yang cepat dan menyeluruh, kustomisasi mendalam, serta keandalan tinggi
connlib ditulis dalam Rust dan mengikuti desain sans-IO
- Cocok untuk membangun layanan jaringan berkat kecepatan dan keamanan memori Rust
- Menggunakan runtime
tokio, WebSockettungstenite, implementasi WireGuardboringtun, enkripsi trafik APIrustls, dan lainnya - Desain sans-IO mengimplementasikan protokol sebagai state machine murni alih-alih mengirim dan menerima byte melalui socket di banyak tempat
Model asinkron Rust dan perdebatan "function coloring"
- Fungsi asinkron hanya dapat dipanggil dari fungsi asinkron lain
- Jika sebuah fungsi berada jauh di dalam rantai fungsi asinkron, maka semua fungsi yang memanggilnya juga harus menjadi fungsi asinkron
- Hal ini bisa menjadi masalah bagi orang yang ingin menulis kode yang tidak peduli apakah dependensinya asinkron atau tidak
Pengenalan sans-IO
- Ide inti sans-IO mirip dengan prinsip inversi dependensi di dunia OOP
- Kebijakan (apa yang harus dilakukan) tidak boleh bergantung pada detail implementasi (bagaimana melakukannya)
- Alih-alih mengirim data menggunakan struct
Transmit, ia akan memancarkanTransmit
Menerapkan inversi dependensi
- Alih-alih mengirim data menggunakan struct
Transmit, ia akan memancarkanTransmit - Event loop mengimplementasikan efek samping dan benar-benar memanggil
UdpSocket::send
State machine
- Diagram state machine untuk permintaan binding STUN memiliki dua state:
SentdanReceived - State machine diimplementasikan dengan mendefinisikan struct
StunBindingdan fungsi-fungsi terkait
Event loop
- Event loop menggerakkan state machine dan memproses data menggunakan
poll_transmitdanhandle_input
Abstraksi waktu
- Menangani kebutuhan berbasis waktu menggunakan API
poll_timeoutdanhandle_timeout
Premis sans-IO
- Desain sans-IO menyerahkan keputusan tentang apakah dependensi bersifat asinkron kepada aplikasi
- Desain sans-IO mudah dikomposisikan, menyediakan API yang fleksibel, mudah diuji, dan sangat cocok dengan kemampuan Rust
Komposisi yang mudah
- API
StunBindingdapat diterapkan pada sebagian besar protokol jaringan - Library
snownetmilik Firezone menggabungkan ICE dan WireGuard untuk menyediakan tunnel IP "ajaib" yang bekerja terlepas dari konfigurasi jaringan
API yang fleksibel
- Menulis event loop sendiri memungkinkan penyetelan kode dan mempermudah pemeliharaan
Pengujian cepat
- Kode sans-IO tidak memiliki efek samping sehingga sangat mudah diuji
- Di Firezone, mereka mengimplementasikan state machine referensi dan menjalankan pengujian yang membandingkannya dengan state aktual
connlib
Edge case dan kegagalan IO
- Desain sans-IO memisahkan implementasi protokol dari efek samping IO nyata sehingga edge case dan penanganan kesalahan menjadi lebih mudah
Rust + sans-IO: pasangan serasi?
- Rust memodelkan kepemilikan dan mutabilitas secara eksplisit sehingga sangat cocok dengan desain sans-IO
- Desain sans-IO dengan leluasa menggunakan
&mutuntuk mengekspresikan perubahan state, dan tidak seperti Rustasync, hanya menggunakan API sinkron
Kekurangan
- Menulis event loop sendiri dapat menimbulkan bug yang sulit dideteksi
- Workflow sekuensial bisa membutuhkan lebih banyak kode
- Di komunitas Rust, desain sans-IO masih belum digunakan secara luas
Penutup
- Kode sans-IO awalnya terasa asing, tetapi menjadi sangat menyenangkan setelah terbiasa
- Rust menyediakan alat yang luar biasa untuk memodelkan state machine
- Desain sans-IO terasa seperti cara yang tepat untuk menulis kode jaringan karena memaksa penanganan kesalahan menjadi bagian dari pemrosesan input
Pendapat GN⁺
- Desain sans-IO sangat cocok dengan model kepemilikan Rust sehingga sangat sesuai untuk implementasi protokol jaringan
- Menulis event loop sendiri meningkatkan fleksibilitas kode dan mempermudah pemeliharaan
- Kemudahan pengujian sangat membantu dalam menulis kode yang andal
- Namun, karena belum banyak digunakan di komunitas Rust, library terkait mungkin masih kurang
- Saat mengadopsi teknologi baru, kurva pembelajaran dan dukungan komunitas perlu dipertimbangkan
1 komentar
Opini Hacker News
Sebelum sintaks async/await diperkenalkan di Rust, mesin status diimplementasikan secara manual
Saat menulis library VT100, penulis menyadari adanya masalah pada pola enkapsulasi Rust
Dibandingkan dengan desain yang mengirim data menggunakan channel
Di ekosistem Haskell ada gagasan untuk memisahkan logika dan eksekusi
tokio::select!dienkapsulasiFungsi async Rust dikompilasi menjadi mesin status
Dengan mengekspos status, fungsi async bisa menjadi 'murni'
Firezone adalah alat yang luar biasa
Akan bagus jika compiler bisa secara otomatis mengubah kode async menjadi sans io
Setelah membaca artikel dan komentarnya, rasanya seperti menemukan kembali gaya arsitektur hexagonal atau ports/adapters
Penasaran apakah lalu lintas nyata melewati gateway, atau hanya digunakan untuk penyiapan koneksi