Giriş: Rust ve Actix ile Web Sunucusu Kurulumuna Genel Bakış
Günümüz web geliştirme dünyasında, performans, güvenlik ve eşzamanlılık giderek daha kritik hale gelmektedir. Geleneksel dillerin ve framework’lerin bazı sınırlamaları karşısında, Rust programlama dili ve onun popüler web framework’ü Actix-web, modern ve verimli web sunucuları oluşturmak için güçlü bir alternatif sunar. Rust, bellek güvenliği garantileri, sıfır maliyetli soyutlamaları ve üstün performansı ile bilinirken, Actix-web ise aktör tabanlı eşzamanlılık modeli sayesinde yüksek hızlı ve ölçeklenebilir uygulamalar geliştirmeyi kolaylaştırır.
Bu rehberde, Rust ve Actix kullanarak temel bir web sunucusunu adım adım nasıl kuracağınızı öğreneceksiniz. Rust’ın kurulumundan başlayarak, Actix projesi oluşturmaya, basit “Merhaba Dünya” uygulamasından dinamik yönlendirmeye kadar birçok konuyu ele alacağız. Ayrıca, durum yönetimi, JSON istekleri ve temel hata yönetimi gibi gelişmiş konulara da kısa bir bakış atacağız. Amacımız, Rust ve Actix ile web geliştirme yolculuğunuza sağlam bir başlangıç yapmanızı sağlamaktır.
Rust ve Actix Projesi Başlatma
Herhangi bir Rust projesine başlamadan önce, sisteminizde Rust geliştirme ortamının kurulu olması gerekmektedir. Ardından, bir Actix projesi oluşturmak için Cargo’yu kullanacağız.
Rust Kurulumu
Rust’ı kurmanın en kolay yolu, resmi kurulum aracı olan rustup‘ı kullanmaktır. Terminalinizi açın ve aşağıdaki komutu çalıştırın:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Kurulum tamamlandıktan sonra, Rust araç zincirinin PATH’inize eklendiğinden emin olmak için terminali yeniden başlatmanız veya aşağıdaki komutu çalıştırmanız gerekebilir:
source $HOME/.cargo/env
Kurulumu doğrulamak için rustc --version ve cargo --version komutlarını çalıştırabilirsiniz. Her iki komut da kurulu Rust ve Cargo sürümlerini göstermelidir.
rustc --version
örnek çıktı: rustc 1.76.0 (00d16c7ad 2024-02-15)
cargo --version
örnek çıktı: cargo 1.76.0 (c57e83d6b 2024-02-13)
Yeni Bir Actix Projesi Oluşturma
Rust geliştirme için standart proje yöneticisi ve derleme sistemi Cargo’dur. Yeni bir Actix projesi oluşturmak için Cargo’yu kullanabiliriz:
cargo new my_actix_app --bin
cd my_actix_app
Bu komut, my_actix_app adında yeni bir dizin oluşturur ve içine temel Rust projesi yapısını yerleştirir. Şimdi, Cargo.toml dosyasını düzenleyerek Actix-web bağımlılığını projemize eklememiz gerekiyor. my_actix_app/Cargo.toml dosyasını açın ve [dependencies] bölümüne actix-web‘i ekleyin:
# my_actix_app/Cargo.toml
[package]
name = "my_actix_app"
version = "0.1.0"
edition = "2021"
[dependencies]
actix-web = "4" # En son kararlı sürümü kullanın
tokio = { version = "1", features = ["full"] } # Actix-web genellikle tokio runtime kullanır
serde = { version = "1.0", features = ["derive"] } # JSON işleme için
serde_json = "1.0" # JSON işleme için
Burada actix-web‘in “4” sürümünü belirtiyoruz (her zaman en son kararlı sürümü kontrol edin). tokio Actix’in kullandığı asenkron çalışma zamanıdır. serde ve serde_json ise ileride JSON verilerini işlemek için gerekli olacaktır. Bağımlılıkları kaydettikten sonra, Cargo bunları otomatik olarak indirecektir.
Temel Actix Web Sunucusu Oluşturma
Artık projemiz hazır olduğuna göre, ilk Actix web sunucumuzu yazmaya başlayabiliriz. src/main.rs dosyasını açın ve içeriğini düzenleyin.
“Merhaba Dünya” Uygulaması
İlk adımımız, basit bir “Merhaba Dünya” yanıtı veren bir sunucu oluşturmak olacak.
// src/main.rs
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
// Bu asenkron fonksiyon, bir HTTP GET isteğine yanıt verir.
// #[get("/")] makrosu, bu fonksiyonun kök URL'ye gelen GET isteklerini işleyeceğini belirtir.
async fn greet() -> impl Responder {
HttpResponse::Ok().body("Merhaba Dünya!")
}
// Bu asenkron fonksiyon, dinamik bir yol parametresi kullanarak kişiselleştirilmiş bir selam verir.
// #[get("/hello/{name}")] makrosu, "/hello/bir_isim" gibi URL'leri işleyeceğini belirtir.
async fn hello(name: web::Path) -> impl Responder {
HttpResponse::Ok().body(format!("Merhaba {}!", name))
}
// Ana fonksiyon, Actix web sunucusunu başlatır.
#[actix_web::main] // Bu makro, main fonksiyonunu bir asenkron fonksiyona dönüştürür.
async fn main() -> std::io::Result<()> {
// HttpServer::new ile yeni bir HTTP sunucusu oluşturulur.
// .service() metodu ile yönlendiriciler (route handlers) eklenir.
HttpServer::new(|| {
App::new() // Yeni bir Actix uygulaması oluşturulur.
.service(greet) // Kök URL için greet fonksiyonunu kaydeder.
.service(hello) // "/hello/{name}" URL'si için hello fonksiyonunu kaydeder.
})
// Sunucunun dinleyeceği IP adresi ve port belirtilir.
.bind(("127.0.0.1", 8080))?
// Sunucuyu başlatır ve gelen istekleri dinlemeye başlar.
.run()
.await
}
Yukarıdaki kodu src/main.rs dosyasına yapıştırdıktan sonra, projenizi derlemek ve çalıştırmak için terminalde aşağıdaki komutu kullanın:
cargo run
Eğer her şey yolundaysa, terminalde Actix sunucusunun 127.0.0.1:8080 adresinde çalıştığını belirten bir çıktı görmelisiniz. Şimdi bir web tarayıcısı açın ve aşağıdaki adresleri ziyaret edin:
* http://127.0.0.1:8080/ adresine gittiğinizde “Merhaba Dünya!” yazısını görmelisiniz.
* http://127.0.0.1:8080/hello/Rust adresine gittiğinizde “Merhaba Rust!” yazısını görmelisiniz.
Yönlendirme ve Parametreler
Yukarıdaki örnekte iki farklı yönlendirme türü gördük:
* Sabit Yönlendirme (/): #[get("/")] makrosu, doğrudan kök URL’ye gelen GET isteklerini greet fonksiyonuna yönlendirir.
* Dinamik Yönlendirme (/hello/{name}): #[get("/hello/{name}")] makrosu, {name} kısmının dinamik bir parametre olacağını belirtir. Bu parametre, hello fonksiyonunda web::Path türünde alınır. web::Path extractor’ı, URL yolundan gelen segmentleri Rust türlerine dönüştürmeyi sağlar.
Actix, HTTP metotlarına özel makrolar (#[post], #[put], #[delete], #[patch], #[head], #[options]) sunarak veya genel web::resource().route() yapısını kullanarak daha karmaşık yönlendirme senaryolarını da destekler.
Gelişmiş Konulara Kısa Bir Bakış
Temel bir sunucu kurduktan sonra, gerçek dünya uygulamalarında sıkça karşılaşılan bazı konulara kısaca değinelim.
Durum Yönetimi (State Management)
Web uygulamalarında, veritabanı bağlantıları, yapılandırma ayarları veya paylaşılan sayaçlar gibi uygulama genelinde erişilmesi gereken verilere ihtiyaç duyulur. Actix-web, web::Data extractor’ı aracılığıyla durum yönetimini kolaylaştırır. web::Data ile paylaşılan veriler, Arc (Atomically Reference Counted) ve Mutex (Mutual Exclusion) gibi eşzamanlılık ilkel sarıcıları içinde tutulmalıdır, çünkü Actix birden fazla iş parçacığı üzerinde çalışabilir ve verilere güvenli bir şekilde erişim sağlanmalıdır.
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
use std::sync::{Arc, Mutex}; // Arc ve Mutex'i import ediyoruz
// Paylaşılacak durumu tanımlayan bir struct
struct AppState {
app_name: String,
counter: Mutex, // Sayaç Mutex içinde olmalı
}
async fn index(data: web::Data) -> impl Responder {
let app_name = &data.app_name;
let mut counter = data.counter.lock().unwrap(); // Mutex'i kilitler
*counter += 1; // Sayacı artırır
HttpResponse::Ok().body(format!("Uygulama Adı: {}. İstek Sayısı: {}", app_name, counter))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let app_state = web::Data::new(AppState {
app_name: String::from("Actix Web Uygulamam"),
counter: Mutex::new(0),
});
HttpServer::new(move || { // move closure, app_state'in sahipliğini taşır
App::new()
.app_data(app_state.clone()) // Durumu uygulamaya ekler
.service(
web::resource("/")
.route(web::get().to(index))
)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
Burada, AppState struct’ını web::Data ile sarmalayarak tüm handler’lar tarafından erişilebilir hale getirdik. counter alanı Mutex içinde tutulur, böylece birden fazla eşzamanlı isteğin aynı anda sayacı güncellemeye çalışması durumunda veri yarışları önlenir.
JSON İstekleri ve Yanıtları
Modern web API’leri genellikle JSON formatında veri alışverişi yapar. Actix-web, serde kütüphanesi ile entegrasyonu sayesinde JSON verilerini kolayca işleyebilir.
Önce Cargo.toml dosyanıza serde ve serde_json bağımlılıklarını eklediğinizden emin olun (daha önce eklemiştik).
use actix_web::{post, web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize}; // Serde trait'lerini import ediyoruz
// Gelen JSON verilerini temsil eden struct
#[derive(Deserialize)]
struct MyJsonRequest {
name: String,
age: u8,
}
// Giden JSON verilerini temsil eden struct
#[derive(Serialize)]
struct MyJsonResponse {
message: String,
status: String,
}
#[post("/submit")]
async fn submit_json(item: web::Json) -> impl Responder {
println!("Gelen JSON: Name: {}, Age: {}", item.name, item.age);
let response = MyJsonResponse {
message: format!("Merhaba {}. {} yaşındasın ve isteğin alındı.", item.name, item.age),
status: "success".to_string(),
};
HttpResponse::Ok().json(response) // JSON yanıt döner
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(submit_json)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
Bu örnekte, MyJsonRequest struct’ı #[derive(Deserialize)] ile işaretlenerek gelen JSON istek gövdesinin otomatik olarak bu struct’a dönüştürülmesi sağlanır. web::Json extractor’ı bu dönüşümü gerçekleştirir. Yanıt olarak MyJsonResponse struct’ını #[derive(Serialize)] ile işaretleyerek HttpResponse::Ok().json(response) metoduyla doğrudan JSON formatında bir yanıt dönebiliriz.
Bu endpoint’i test etmek için curl gibi bir araç kullanabilirsiniz:
curl -X POST -H "Content-Type: application/json" -d '{"name": "Alice", "age": 30}' http://127.0.0.1:8080/submit
Hata Yönetimi
Actix-web, actix_web::error modülü aracılığıyla güçlü bir hata yönetimi mekanizması sunar. Temel olarak, handler fonksiyonları Result dönebilir ve Actix bu hataları uygun HTTP yanıtlarına dönüştürür.
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder, error};
use std::io;
// Parametre olarak gelen ID'nin geçerli bir sayı olduğunu varsayalım.
// Eğer değilse, bir hata dönebiliriz.
async fn get_item_by_id(path: web::Path) -> Result {
let id = path.into_inner();
if id == 0 {
// Özel bir hata döndürüyoruz.
// BadRequest, 400 durum kodu ile yanıt verir.
return Err(error::ErrorBadRequest("ID sıfır olamaz."));
}
// Gerçek bir uygulamada burada veritabanından veri çekilirdi.
Ok(HttpResponse::Ok().body(format!("Öğe ID: {}", id)))
}
#[actix_web::main]
async fn main() -> io::Result<()> {
HttpServer::new(|| {
App::new()
.service(web::resource("/item/{id}").route(web::get().to(get_item_by_id)))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
Bu örnekte, get_item_by_id fonksiyonu Result döner. Eğer id 0 ise, error::ErrorBadRequest hatası oluşturulur ve Actix bunu otomatik olarak 400 Bad Request HTTP yanıtına dönüştürür. Diğer durumlarda, başarılı bir HttpResponse döner.
Sonuç ve Sıkça Sorulan Sorular (SSS)
Bu rehberde, Rust programlama dili ve Actix-web framework’ünü kullanarak temel bir web sunucusunu nasıl kuracağınızı öğrendiniz. Rust’ın kurulumundan başlayarak, Actix projesi oluşturma, “Merhaba Dünya” uygulaması geliştirme, dinamik yönlendirme, durum yönetimi, JSON işleme ve temel hata yönetimi gibi konuları ele aldık.
Rust ve Actix-web’in birleşimi, yüksek performanslı, bellek güvenli ve eşzamanlı web uygulamaları geliştirmek için güçlü bir araç seti sunar. Öğrendiğiniz bu temel bilgilerle, daha karmaşık web servisleri ve API’ler geliştirmeye başlayabilir, Rust’ın sunduğu avantajlardan tam olarak yararlanabilirsiniz.
Sıkça Sorulan Sorular (SSS)
Actix-web yerine başka hangi Rust web framework’leri var?
Actix-web popüler olsa da, Rust ekosisteminde başka güçlü web framework’leri de bulunmaktadır. Bunlardan bazıları:
- Rocket: Macro tabanlı, geliştirici deneyimine odaklanmış, hızlı ve güvenli bir framework.
- Axum: Tokio ve Tower üzerine inşa edilmiş, esnek ve modüler bir framework. Sağlam bir temel ve iyi dokümantasyon sunar.
- Warp: Fonksiyonel programlama tarzına yakın, filtreler aracılığıyla istekleri işleyen hafif bir framework.
Seçiminiz, projenizin gereksinimlerine ve kişisel tercihlerinize bağlı olacaktır.
Rust’ta eşzamanlılık neden önemli ve Actix bunu nasıl yönetiyor?
Eşzamanlılık, modern sunucuların aynı anda birden fazla isteği verimli bir şekilde işlemesi için kritik öneme sahiptir. Rust, bellek güvenliğini statik olarak garanti ederken, eşzamanlılık primitive’leri (örneğin Arc, Mutex, RwLock) ve asenkron çalışma zamanları (örneğin tokio) ile güvenli ve yüksek performanslı eşzamanlı kod yazmayı sağlar. Actix-web, aktör modelini kullanarak eşzamanlılığı yönetir. Her aktör, kendi durumuna sahip izole bir birimdir ve mesajlar aracılığıyla diğer aktörlerle iletişim kurar. Bu model, veri yarışlarını önlemeye yardımcı olur ve ölçeklenebilirliği artırır.
Üretim ortamında Actix sunucusunu nasıl çalıştırırım?
Üretim ortamında Actix uygulamanızı çalıştırmak için genellikle şu adımları izlersiniz:
- Uygulamayı Derleme:
cargo build --releasekomutu ile uygulamanızı optimize edilmiş bir şekilde derleyin. Bu, daha küçük ve daha hızlı bir çalıştırılabilir dosya üretecektir. - Çalıştırma: Derlenmiş ikili dosyayı doğrudan çalıştırın:
./target/release/my_actix_app. - Ortam Değişkenleri:
RUST_LOG=infogibi ortam değişkenlerini ayarlayarak Actix’in log seviyesini kontrol edebilirsiniz. - Sürekli Çalıştırma ve Yönetim: Uygulamanızı bir
systemdservisi, Docker konteyneri veya Kubernetes gibi bir orkestrasyon aracı ile yöneterek sürekli çalışmasını, otomatik yeniden başlatılmasını ve kaynak kullanımını izlemesini sağlayabilirsiniz.
Actix’in performans avantajı nereden geliyor?
Actix-web’in yüksek performansı birkaç faktörden kaynaklanır:
- Rust’ın Performansı: Rust, C/C++’a yakın performans sunan derlenmiş bir dildir. Sıfır maliyetli soyutlamalar ve bellek güvenliği garantileri, çalışma zamanı yükünü minimumda tutar.
- Aktör Modeli: Aktör modeli, izole edilmiş, mesajlaşmaya dayalı eşzamanlılık sağlar. Bu, veri yarışlarını azaltır ve sistemin daha verimli ölçeklenmesine olanak tanır.
- Tokio Asenkron Çalışma Zamanı: Actix, hızlı ve verimli bir asenkron çalışma zamanı olan Tokio üzerine inşa edilmiştir. Bu, I/O yoğun işlemleri (ağ istekleri, veritabanı sorguları vb.) bloklamadan paralel olarak işlemeyi sağlar.
- Minimal Overhead: Framework’ün kendisi hafiftir ve gereksiz soyutlamalardan kaçınır, bu da daha az kaynak tüketimi anlamına gelir.
Actix’te veritabanı bağlantısı nasıl yapılır?
Actix-web’de veritabanı bağlantısı, genellikle web::Data ile uygulama durumu olarak yönetilir. Veritabanı istemcileri (örneğin sqlx veya diesel için bağlantı havuzları) AppState içinde saklanır. Handler fonksiyonları daha sonra bu bağlantı havuzunu kullanarak veritabanı işlemleri gerçekleştirir. Örneğin, sqlx ile:
use sqlx::PgPool; // Veya başka bir veritabanı türü
struct AppState {
db_pool: PgPool,
}
async fn get_users(data: web::Data) -> impl Responder {
let users = sqlx::query!("SELECT id, name FROM users")
.fetch_all(&data.db_pool)
.await
.unwrap(); // Hata yönetimi eklenmeli
HttpResponse::Ok().json(users)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let pool = PgPool::connect("postgres://user:pass@localhost/mydb").await.unwrap();
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(AppState { db_pool: pool.clone() }))
.service(web::resource("/users").route(web::get().to(get_users)))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
Bu yaklaşım, her istek için yeni bir bağlantı açma maliyetinden kaçınarak veritabanı erişimini optimize eder.