Penulis materi, terjemahan yang kami terbitkan hari ini, mengatakan bahwa eksperimen terbarunya di bidang arsitektur proyek perangkat lunak adalah pembuatan aplikasi web yang berfungsi hanya menggunakan bahasa Rust dan dengan penggunaan kode templat seminimal mungkin. Dalam artikel ini, dia ingin berbagi dengan pembaca apa yang dia temukan saat mengembangkan aplikasi dan menjawab 
pertanyaan apakah Rust siap menggunakannya di berbagai bidang pengembangan web.

Tinjauan Proyek
Kode untuk proyek yang akan dibahas di sini dapat ditemukan di 
GitHub . Bagian klien dan server aplikasi terletak di repositori yang sama, ini dilakukan untuk menyederhanakan pemeliharaan proyek. Perlu dicatat bahwa Cargo perlu mengkompilasi aplikasi frontend dan backend dengan dependensi berbeda. 
Di sini Anda dapat melihat aplikasi yang berfungsi.
Proyek kami adalah demonstrasi sederhana dari mekanisme otentikasi. Ini memungkinkan Anda untuk masuk dengan nama pengguna dan kata sandi yang dipilih (harus sama).
Jika nama pengguna dan kata sandi berbeda, otentikasi akan gagal. Setelah otentikasi berhasil, token 
JWT (JSON Web Token) disimpan di sisi klien dan server. Menyimpan token di server dalam aplikasi seperti itu biasanya tidak diperlukan, tetapi saya hanya melakukan itu untuk tujuan demonstrasi. Ini, misalnya, dapat digunakan untuk mencari tahu berapa banyak pengguna yang login. Seluruh aplikasi dapat dikonfigurasi menggunakan file 
Config.toml tunggal, misalnya, menentukan kredensial untuk mengakses database, atau alamat dan nomor port server. Seperti apa kode standar untuk file ini untuk aplikasi kita.
[server] ip = "127.0.0.1" port = "30080" tls = false [log] actix_web = "debug" webapp = "trace" [postgres] host = "127.0.0.1" username = "username" password = "password" database = "database" 
Pengembangan Aplikasi Klien
Untuk mengembangkan sisi aplikasi klien, saya memutuskan untuk menggunakan 
yew . Ini adalah kerangka kerja Rust modern yang terinspirasi oleh Elm, Angular dan React. Ini dirancang untuk membuat bagian klien dari aplikasi web multi-threaded menggunakan 
WebAssembly (Wasm). Saat ini, proyek ini sedang dalam pengembangan aktif, sementara tidak ada rilis stabil sangat banyak.
Kerangka kerja 
yew bergantung pada alat 
web-kargo , yang dirancang untuk mengkompilasi silang kode dalam Wasm.
Alat 
web kargo adalah ketergantungan 
yew langsung yang menyederhanakan kompilasi silang kode Rust di Wasm. Berikut adalah tiga tujuan utama untuk menyusun Wasm yang tersedia melalui alat ini:
- asmjs-unknown-emscripten- menggunakan asm.js melalui Emscripten.
- wasm32-unknown-emscripten- menggunakan WebAssembly melalui Emscripten
- wasm32-unknown-unknown- menggunakan WebAssembly dengan backend Rust asli untuk WebAssembly
Perakitan webSaya memutuskan untuk menggunakan opsi terakhir, yang membutuhkan penggunaan perakitan "malam" dari kompiler Rust, tetapi yang terbaik menunjukkan kemampuan Wasm asli Rust.
Jika kita berbicara tentang WebAssembly, maka berbicara tentang Rust hari ini adalah topik terpanas. Sejumlah besar pekerjaan sedang dilakukan pada cross-compiling Rust in Wasm dan mengintegrasikannya ke dalam ekosistem Node.js (menggunakan paket npm). Saya memutuskan untuk mengimplementasikan proyek tanpa ketergantungan JavaScript.
Ketika meluncurkan frontend aplikasi web (dalam proyek saya ini dilakukan dengan perintah 
make frontend ), 
cargo-web cross-kompilasi aplikasi di Wasm dan mengemasnya, menambahkan beberapa bahan statis. 
cargo-web meluncurkan server web lokal, yang memungkinkan Anda untuk berinteraksi dengan aplikasi untuk tujuan pengembangan. Inilah yang terjadi di konsol ketika Anda menjalankan perintah di atas:
 > make frontend  Compiling webapp v0.3.0 (file:///home/sascha/webapp.rs)   Finished release [optimized] target(s) in 11.86s   Garbage collecting "app.wasm"...   Processing "app.wasm"...   Finished processing of "app.wasm"! If you need to serve any extra files put them in the 'static' directory in the root of your crate; they will be served alongside your application. You can also put a 'static' directory in your 'src' directory. Your application is being served at '/app.js'. It will be automatically rebuilt if you make any changes in your code. You can access the web server at `http://0.0.0.0:8000`. 
Kerangka kerja 
yew memiliki beberapa fitur yang sangat menarik. Diantaranya adalah dukungan untuk arsitektur komponen yang dapat digunakan kembali. Fitur ini telah menyederhanakan penguraian aplikasi saya menjadi tiga komponen utama:
RootComponent . Komponen ini langsung dipasang ke tag 
<body> situs web. Dia memutuskan komponen anak mana yang harus dimuat selanjutnya. Jika, di pintu masuk pertama ke halaman, token JWT ditemukan, ia mencoba untuk memperbarui token ini dengan menghubungi bagian server dari aplikasi. Jika gagal, transisi ke komponen 
LoginComponent .
Komponen Login . Komponen ini adalah turunan dari komponen 
RootComponent , ini berisi formulir dengan bidang untuk memasukkan kredensial. Selain itu, ia berinteraksi dengan bagian belakang aplikasi untuk mengatur skema otentikasi sederhana berdasarkan verifikasi nama pengguna dan kata sandi, dan, jika otentikasi berhasil, menyimpan JWT dalam cookie. Selain itu, jika pengguna dapat mengautentikasi, ia melakukan transisi ke komponen 
ContentComponent .
Penampilan Komponen LoginComponentContentComponent Komponen ini adalah turunan lain dari komponen 
RootComponent . Ini berisi apa yang ditampilkan di halaman utama aplikasi (saat ini hanya judul dan tombol untuk keluar dari sistem). Akses ke sana dapat diperoleh melalui 
RootComponent (jika aplikasi, saat startup, berhasil menemukan token sesi yang valid), atau melalui 
LoginComponent (jika otentikasi berhasil). Komponen ini bertukar data dengan backend ketika pengguna mengklik tombol logout.
Komponen ContentComponentKomponen Router Komponen ini menyimpan semua rute yang mungkin antara komponen yang mengandung konten. Selain itu, ini berisi keadaan awal 
loading dan 
error aplikasi. Ini terhubung langsung ke 
RootComponent .
Salah satu konsep kunci 
yew yang akan kita bahas sekarang adalah layanan. Mereka memungkinkan Anda untuk menggunakan kembali logika yang sama di berbagai komponen. Katakanlah ini bisa berupa antarmuka atau alat logging untuk mendukung penggunaan 
cookie . Layanan tidak menyimpan beberapa keadaan global, mereka dibuat ketika komponen diinisialisasi. Selain layanan, 
yew mendukung konsep agen. Mereka dapat digunakan untuk mengatur berbagi data antara berbagai komponen, untuk mempertahankan keadaan umum aplikasi, seperti yang diperlukan untuk agen yang bertanggung jawab untuk routing. Untuk mengatur sistem perutean aplikasi kami, yang mencakup semua komponen, 
agen kami dan layanan perutean diterapkan di sini. Tidak ada router standar di 
yew , tetapi dalam repositori framework Anda dapat menemukan 
contoh implementasi router yang mendukung berbagai operasi URL.
Saya senang mencatat bahwa Anda menggunakan 
API Pekerja Web untuk menjalankan agen pada berbagai utas dan menggunakan penjadwal lokal yang terpasang pada utas untuk menyelesaikan tugas paralel. Hal ini memungkinkan untuk mengembangkan aplikasi browser dengan multithreading tingkat tinggi di Rust.
Setiap komponen mengimplementasikan 
sifat Renderable -nya sendiri, yang memungkinkan kita untuk memasukkan kode HTML secara langsung dalam kode sumber Rust menggunakan 
html! {} Makro .
Ini adalah fitur yang hebat, dan, tentu saja, penggunaannya yang tepat dikendalikan oleh kompiler. Berikut adalah 
Renderable implementasi 
LoginComponent komponen 
LoginComponent .
 impl Renderable<LoginComponent> for LoginComponent {   fn view(&self) -> Html<Self> {       html! {           <div class="uk-card uk-card-default uk-card-body uk-width-1-3@s uk-position-center",>               <form onsubmit="return false",>                   <fieldset class="uk-fieldset",>                       <legend class="uk-legend",>{"Authentication"}</legend>                       <div class="uk-margin",>                           <input class="uk-input",                                  placeholder="Username",                                  value=&self.username,                                  oninput=|e| Message::UpdateUsername(e.value), />                       </div>                       <div class="uk-margin",>                           <input class="uk-input",                                  type="password",                                  placeholder="Password",                                  value=&self.password,                                  oninput=|e| Message::UpdatePassword(e.value), />                       </div>                       <button class="uk-button uk-button-default",                               type="submit",                               disabled=self.button_disabled,                               onclick=|_| Message::LoginRequest,>{"Login"}</button>                       <span class="uk-margin-small-left uk-text-warning uk-text-right",>                           {&self.error}                       </span>                   </fieldset>               </form>           </div>       }   } } 
Koneksi antara frontend dan backend didasarkan pada koneksi 
WebSocket yang digunakan oleh setiap klien. Kekuatan teknologi WebSocket adalah kenyataan bahwa itu cocok untuk mengirimkan pesan biner, serta fakta bahwa server, jika perlu, dapat mengirim pemberitahuan push ke klien. 
yew memiliki layanan WebSocket standar, tetapi saya memutuskan untuk membuat 
versinya sendiri untuk tujuan demonstrasi, terutama karena inisialisasi koneksi "malas" langsung di dalam layanan. Jika layanan WebSocket akan dibuat selama inisialisasi komponen, saya harus memantau banyak koneksi.
Protokol Cap'n ProtoSaya memutuskan untuk menggunakan protokol 
Cap'n Proto (bukan sesuatu seperti 
JSON , 
MessagePack atau 
CBOR ) sebagai lapisan untuk mengirimkan data aplikasi untuk alasan kecepatan dan kekompakan. Perlu dicatat bahwa saya tidak menggunakan antarmuka protokol 
RPC yang dimiliki Cap'n Proto, karena implementasi Rustnya tidak dapat dikompilasi untuk WebAssembly (karena 
dependensi toxio-rs Unix). Ini agak rumit dalam pemilihan permintaan dan tanggapan dari jenis yang benar, tetapi masalah ini dapat diselesaikan dengan menggunakan 
API yang terstruktur dengan 
baik . Berikut ini adalah deklarasi protokol Proto Cap'n untuk aplikasi tersebut.
 @0x998efb67a0d7453f; struct Request {   union {       login :union {           credentials :group {               username @0 :Text;               password @1 :Text;           }           token @2 :Text;       }       logout @3 :Text; # The session token   } } struct Response {   union {       login :union {           token @0 :Text;           error @1 :Text;       }       logout: union {           success @2 :Void;           error @3 :Text;       }   } } 
Anda dapat melihat bahwa di sini kami memiliki dua versi berbeda dari permintaan login.
Salah satunya adalah untuk 
LoginComponent (di sini, untuk mendapatkan token, nama dan kata sandi digunakan), dan yang lainnya untuk 
RootComponent (digunakan untuk memperbarui token yang ada). Segala sesuatu yang diperlukan untuk protokol untuk bekerja dikemas dalam layanan 
protokol , karena itu nyaman untuk menggunakan kembali kemampuan yang sesuai di berbagai bagian frontend.
UIkit - kerangka front-end modular yang ringkas untuk mengembangkan antarmuka web yang cepat dan tangguhAntarmuka pengguna bagian klien dari aplikasi didasarkan pada kerangka 
UIkit , versi 3.0.0 akan dirilis dalam waktu dekat. Skrip 
build.rs yang disiapkan khusus secara otomatis mengunduh semua dependensi UIkit yang diperlukan dan mengkompilasi lembar gaya yang dihasilkan. Ini berarti bahwa Anda dapat menambahkan gaya Anda sendiri ke file 
style.scss tunggal, yang dapat diterapkan di seluruh aplikasi. Sangat nyaman.
FrontMenguji frontend
Saya percaya bahwa ada beberapa masalah dengan pengujian solusi kami. Faktanya adalah sangat mudah untuk menguji layanan individu, tetapi Anda tidak memberikan pengembang dengan cara yang nyaman untuk menguji komponen dan agen. Sekarang, dalam kerangka Rust murni, integrasi dan pengujian ujung ke ujung tidak tersedia. Di sini Anda dapat menggunakan proyek-proyek seperti 
Cypress atau 
Protractor , tetapi dengan pendekatan ini, Anda harus memasukkan banyak templat kode JavaScript / TypeScript dalam proyek, jadi saya memutuskan untuk meninggalkan implementasi tes tersebut.
Omong-omong, inilah ide untuk proyek baru: kerangka kerja untuk pengujian ujung-ke-ujung yang ditulis dalam Rust.
Pengembangan sisi server aplikasi
Untuk mengimplementasikan sisi server aplikasi, saya memilih kerangka kerja 
actix-web . Ini adalah kerangka 
model aktor berbasis Rust yang ringkas, praktis dan sangat cepat. Ini mendukung semua teknologi yang diperlukan, seperti WebSockets, TLS dan 
HTTP / 2.0 . Kerangka kerja ini mendukung berbagai penangan dan sumber daya, tetapi dalam aplikasi kami hanya beberapa rute utama yang digunakan:
- /wsadalah sumber daya utama untuk komunikasi WebSocket.
- /- pengendali utama yang memberikan akses ke aplikasi front-end statis.
Secara default, 
actix-web memulai alur kerja dalam jumlah yang sesuai dengan jumlah inti prosesor yang tersedia di komputer lokal. Ini berarti bahwa jika aplikasi memiliki status, aplikasi harus dibagikan dengan aman di antara semua utas, tetapi berkat template komputasi paralel Rust yang andal, ini bukan masalah. Meskipun demikian, backend seharusnya merupakan sistem tanpa kewarganegaraan, karena banyak salinannya dapat digunakan secara paralel di lingkungan cloud (seperti 
Kubernetes ). Akibatnya, data yang membentuk keadaan aplikasi harus dipisahkan dari backend. Misalnya, mereka mungkin berada di dalam instance terpisah dari wadah 
Docker .
DBMS dan Proyek Diesel PostgreSQLSebagai gudang data utama, saya memutuskan untuk menggunakan DBMS 
PostgreSQL . Mengapa Pilihan ini menentukan keberadaan proyek 
Diesel yang luar biasa yang sudah mendukung PostgreSQL dan menawarkan sistem ORM yang aman dan dapat dikembangkan serta alat bantu permintaan untuknya. Semua ini sangat sesuai dengan kebutuhan proyek kami, karena 
actix-web sudah mendukung Diesel. Sebagai hasilnya, di sini, untuk melakukan operasi CRUD dengan informasi tentang sesi dalam basis data, Anda dapat menggunakan bahasa khusus yang mempertimbangkan spesifikasi Rust. Berikut ini adalah contoh handler 
actix-web untuk 
actix-web berdasarkan Diesel.rs.
 impl Handler<UpdateSession> for DatabaseExecutor {   type Result = Result<Session, Error>;   fn handle(&mut self, msg: UpdateSession, _: &mut Self::Context) -> Self::Result {        
Proyek 
r2d2 digunakan untuk membuat koneksi antara 
actix-web dan Diesel. Ini berarti bahwa kami memiliki (selain aplikasi dengan alur kerjanya) keadaan bersama aplikasi, yang mendukung banyak koneksi basis data sebagai kumpulan koneksi tunggal. Ini sangat menyederhanakan penskalaan serius backend, membuat solusi ini fleksibel. 
Di sini Anda dapat menemukan kode yang bertanggung jawab untuk membuat instance server.
βMenguji backend
Pengujian integrasi backend dalam proyek kami dilakukan dengan meluncurkan instance server pengujian dan menyambungkan ke database yang sudah berjalan. Kemudian Anda dapat menggunakan klien WebSocket standar (saya menggunakan 
tungstenite ) untuk mengirim data yang dihasilkan menggunakan protokol Cap'n Proto ke server dan membandingkan hasilnya dengan yang diharapkan. Pengaturan tes ini telah terbukti sangat baik. Saya tidak menggunakan 
server uji khusus 
actix-web , karena lebih banyak pekerjaan tidak diperlukan untuk mengkonfigurasi dan menjalankan server nyata. Pengujian unit backend ternyata, seperti yang diharapkan, menjadi tugas yang cukup sederhana, melakukan tes seperti itu tidak menimbulkan masalah.
Penempatan proyek
Aplikasi ini sangat mudah digunakan menggunakan gambar Docker.
DockerDengan 
make deploy Anda dapat membuat gambar bernama 
webapp yang berisi executable backend yang terhubung secara statis, file 
Config.toml saat ini, sertifikat TLS, dan konten frontend statis. Perakitan executable yang sepenuhnya terhubung secara statis di Rust diimplementasikan menggunakan versi modifikasi dari gambar Docker 
rust-musl-builder . Aplikasi web yang sudah selesai dapat diuji menggunakan perintah 
make run , yang meluncurkan wadah yang mendukung jaringan. Wadah PostgreSQL harus dijalankan secara paralel dengan wadah aplikasi untuk memastikan sistem bekerja. Secara umum, proses penerapan sistem kami cukup sederhana, di samping itu, berkat teknologi yang digunakan di sini, kami dapat berbicara tentang fleksibilitasnya yang cukup, menyederhanakan adaptasi yang mungkin untuk kebutuhan aplikasi yang sedang berkembang.
Teknologi yang digunakan dalam pengembangan proyek
Berikut adalah diagram ketergantungan aplikasi.
Teknologi yang digunakan untuk mengembangkan aplikasi web di RustSatu-satunya komponen yang digunakan frontend dan backend adalah versi Rust dari Cap'n Proto, yang membutuhkan kompiler Cap'n Proto yang diinstal secara lokal untuk dibuat.
Hasilnya. Apakah Rust Siap Untuk Produksi Web?
Ini pertanyaan besar. Inilah yang bisa saya jawab. Dari sudut pandang server, saya cenderung menjawab βyaβ, karena ekosistem Rust, selain 
actix-web , memiliki 
tumpukan HTTP yang sangat matang dan banyak 
kerangka kerja berbeda untuk pengembangan API dan layanan server yang cepat.
Jika kita berbicara tentang front-end, maka, berkat perhatian umum ke WebAssembly, banyak pekerjaan sedang berlangsung sekarang. Namun, proyek yang dibuat di area ini harus mencapai kematangan yang sama dengan proyek server. Ini terutama berlaku untuk stabilitas API dan kemampuan pengujian. Jadi sekarang saya mengatakan "tidak" untuk menggunakan Rust di ujung depan, tetapi saya tidak bisa tidak mencatat bahwa itu bergerak ke arah yang benar.
Pembaca yang budiman! Apakah Anda menggunakan Rust dalam pengembangan web?