Menyiapkan Proyek Rust untuk Database Terdistribusi
Dari sebelumnya membahas teori, mau melanjutkan ke praktik, kita siapkan dulu lingkungan pengembangan proyeknya.
Bagian ini menyiapkan lingkungan pengembangan dan memperkenalkan crate-crate fundamental yang akan digunakan di seluruh proyek. Kombinasi tokio1, tonic2, dan serde3 merupakan tumpukan standar de facto untuk membangun layanan jaringan berkinerja tinggi di Rust. Menguasai alat-alat ini memberikan keterampilan yang dapat ditransfer jauh melampaui pengembangan database.
Struktur Proyek
Menggunakan Cargo workspace adalah praktik yang baik untuk mengelola proyek multi-komponen. Kita akan membuat struktur berikut:
kv-store/
├── Cargo.toml # Workspace manifest
├── proto/ # Definisi Protocol Buffers
│ └── raft.proto
├── client/ # Crate untuk klien
│ └── Cargo.toml
│ └── src/
└── server/ # Crate untuk server
└── Cargo.toml
└── src/
└── build.rs
Runtime Asinkron: tokio
Aplikasi jaringan seperti server database terikat oleh I/O (I/O-bound). Mereka menghabiskan sebagian besar waktunya menunggu data tiba dari jaringan atau dibaca dari disk. Pemrograman asinkron memungkinkan satu thread untuk mengelola ribuan koneksi secara bersamaan. Ketika satu operasi menunggu I/O, runtime dapat menjadwalkan tugas lain untuk dijalankan, sehingga memaksimalkan penggunaan CPU.
tokio adalah runtime asinkron yang paling banyak digunakan di ekosistem Rust. Ia menyediakan komponen-komponen penting:
Scheduler Tugas: Mengelola eksekusi ribuan tugas asinkron, bahkan pada sejumlah kecil thread sistem operasi.
I/O Asinkron: Menyediakan versi non-blocking dari API jaringan (TCP, UDP) dan I/O file.
Timer: Memungkinkan tugas untuk tidur atau menunggu hingga titik waktu tertentu.
Untuk memulai dengan tokio, Anda akan menggunakan makro atribut #[tokio::main] pada fungsi main Anda. Ini akan menyiapkan runtime dan menjalankan fungsi main asinkron Anda di dalamnya. Tugas-tugas konkuren dibuat menggunakan tokio::spawn, yang mengambil sebuah Future (biasanya blok async) dan menjalankannya secara independen dari tugas saat ini.
Komunikasi Antar-Node: tonic dan gRPC
Untuk komunikasi internal antar node Raft, kita memerlukan protokol Remote Procedure Call (RPC) yang efisien dan terdefinisi dengan baik. gRPC4 adalah pilihan yang sangat baik karena menggunakan Protocol Buffers5 untuk definisi skema dan HTTP/2 untuk transportasi, yang memberikan performa tinggi.
tonic adalah implementasi gRPC untuk Rust, yang dibangun di atas tokio dan hyper. Alur kerja untuk menggunakan tonic adalah sebagai berikut :
Definisikan Layanan: Tulis definisi layanan RPC Anda dalam file
.proto. Untuk Raft, ini akan mencakup layananRaftdengan RPCRequestVotedanAppendEntries.Protocol Buffers
syntax = “proto3”;
package raft;
service Raft {
rpc RequestVote (RequestVoteRequest) returns (RequestVoteReply);
rpc AppendEntries (AppendEntriesRequest) returns (AppendEntriesReply);
}
//... definisi pesan
Hasilkan Kode: Gunakan
tonic-builddalam skripbuild.rsuntuk mengkompilasi file.protomenjadi kode Rust. Ini akan menghasilkan trait untuk layanan server danstructuntuk klien.Implementasikan Server: Buat
structdan implementasikan trait layanan yang dihasilkan untuknya. Metode-metode dalam implementasi Anda akan menjadiasync fn.Jalankan Server: Dalam fungsi
mainAnda, buat instansi dari implementasi layanan Anda dan gunakantonic::transport::Serveruntuk mulai mendengarkan permintaan masuk.
Serialisasi Pesan: serde
Serialisasi adalah proses mengubah struktur data dalam memori (seperti struct Rust) menjadi format yang dapat disimpan atau ditransmisikan (seperti JSON, atau format biner), dan kemudian merekonstruksinya kembali (deserialisasi). serde adalah kerangka kerja standar untuk serialisasi dan deserialisasi di Rust.
Penggunaan paling umum dari serde adalah melalui makro derive :
Rust
use serde::{Serialize, Deserialize};
#
struct RaftMessage {
term: u64,
payload: Vec<u8>,
}
Dengan menambahkan #, Anda secara otomatis mengimplementasikan trait Serialize dan Deserialize untuk struct Anda. Ini memungkinkan Anda untuk dengan mudah mengubah instansi RaftMessage menjadi dan dari berbagai format. Misalnya, untuk mengirimnya melalui jaringan sebagai JSON:
Rust
let message = RaftMessage { term: 1, payload: vec! };
// Serialisasi ke string JSON
let json_string = serde_json::to_string(&message).unwrap();
// Deserialisasi dari string JSON
let deserialized_message: RaftMessage = serde_json::from_str(&json_string).unwrap();
serde sangat kuat dan dapat dikonfigurasi, dengan atribut untuk menangani kasus-kasus seperti mengganti nama bidang, memberikan nilai default, dan banyak lagi.
https://github.com/tokio-rs/tokio
https://github.com/hyperium/tonic
https://github.com/serde-rs/serde
https://grpc.io/docs/what-is-grpc/
https://protobuf.dev/overview/


