gRPC-Rust'ı kullanmaya başlama

1. Giriş

Bu codelab'de, Rust dilinde yazılmış bir rota eşleme uygulamasının temelini oluşturan bir istemci ve sunucu oluşturmak için gRPC-Rust'ı kullanacaksınız.

Eğitimin sonunda, haritada belirli koordinatlardaki bir konumun adını veya posta adresini getirmek için gRPC protokolünün resmi Rust uygulamasını kullanarak uzak bir sunucuya bağlanan bir istemciniz olacak. Tam teşekküllü bir uygulama, bir rota üzerindeki önemli yerleri listelemek veya özetlemek için bu istemci-sunucu tasarımını kullanabilir.

Hizmet, istemci ve sunucu için ortak metin kodu oluşturmak üzere kullanılan bir Protocol Buffers dosyasında tanımlanır. Böylece, bu işlevselliği uygularken zamandan ve emekten tasarruf edersiniz.

Oluşturulan bu kod, sunucu ile istemci arasındaki iletişimin karmaşıklıklarının yanı sıra veri serileştirme ve seri durumdan çıkarma işlemlerini de yönetir.

Neler öğreneceksiniz?

  • Hizmet API'sini tanımlamak için Protocol Buffers'ı kullanma
  • Otomatik kod oluşturmayı kullanarak bir Protokol Arabellek tanımından gRPC tabanlı bir istemci oluşturma.
  • gRPC ile istemci-sunucu iletişimi hakkında bilgi sahibi olmanız gerekir.

Bu codelab, gRPC'ye yeni başlayan veya gRPC bilgilerini tazelemek isteyen Rust geliştiricilerin yanı sıra dağıtılmış sistemler oluşturmakla ilgilenen herkes için hazırlanmıştır. Daha önce gRPC deneyimi gerekmez.

2. Başlamadan önce

Ön koşullar

Aşağıdakileri yüklediğinizden emin olun:

  • GCC. Buradaki talimatları uygulayın.
  • Git: Yükleme talimatları için buraya bakın.
  • Rust, sürüm 1.88.0. Kurulum talimatlarını burada takip edin.

Kodu alın

Böylece tamamen sıfırdan başlamak zorunda kalmazsınız; bu kod laboratuvarı, tamamlamanız için uygulamanın kaynak kodunun bir iskeletini sunar. Aşağıdaki adımlar, gRPC kodunu oluşturmak için protokol arabellek derleyici eklentilerini kullanmak da dahil olmak üzere, uygulamayı nasıl tamamlayacağınızı gösterecektir.

Öncelikle, codelab çalışma dizinini oluşturun ve ardından bu dizine geçin:

mkdir grpc-rust-getting-started && cd grpc-rust-getting-started

Kod laboratuvarı dosyasını indirin ve çıkarın:

curl -sL https://github.com/grpc-ecosystem/grpc-codelabs/archive/refs/heads/2026.tar.gz \
  | tar xvz --strip-components=4 \
  grpc-codelabs-2026/codelabs/grpc-rust-getting-started/start_here

Alternatif olarak, yalnızca codelab dizinini içeren .zip dosyasını indirebilir ve manuel olarak açabilirsiniz.

Eğer bir uygulama yazmaktan kaçınmak istiyorsanız, tamamlanmış kaynak kod GitHub'da mevcuttur.

3. Hizmeti tanımlayın

İlk adımınız, Protokol Tamponları kullanarak uygulamanın gRPC hizmetini, RPC yöntemini ve istek ve yanıt mesaj türlerini tanımlamaktır. Hizmetiniz şunları sağlayacaktır:

  • Sunucunun uyguladığı ve istemcinin çağırdığı GetFeature adlı bir RPC yöntemi.
  • GetFeature yöntemi kullanılırken istemci ve sunucu arasında değiş tokuş edilen veri yapıları olan Point ve Feature mesaj türleri. İstemci, sunucuya gönderdiği GetFeature isteğinde harita koordinatlarını Point olarak sağlar ve sunucu, bu koordinatlarda bulunan her şeyi açıklayan karşılık gelen bir Feature ile yanıt verir.

Bu RPC yöntemi ve mesaj türlerinin tümü, sağlanan kaynak kodunun proto/routeguide.proto dosyasında tanımlanacaktır.

Protokol tamponları genellikle protobuf olarak bilinir. gRPC terminolojisi hakkında daha fazla bilgi için gRPC'nin Temel kavramlar, mimari ve yaşam döngüsü bölümüne bakın.

Hizmet yöntemi

Öncelikle servis yöntemlerimizi, ardından da mesaj türlerimizi Point ve Feature tanımlayalım. proto/routeguide.proto dosyasında, uygulamanın hizmeti tarafından sağlanan bir veya daha fazla yöntemi tanımlayan RouteGuide adlı bir service yapı bulunur.

rpc metodunu GetFeature RouteGuide tanımının içine ekleyin. Daha önce açıklandığı gibi, bu yöntem verilen koordinat kümesinden bir konumun adını veya adresini arayacak, bu nedenle GetFeature verilen bir Point için Feature döndürecektir:

service RouteGuide {
  // Definition of the service goes here

  // Obtains the feature at a given position.
  rpc GetFeature(Point) returns (Feature) {}
}

Bu tekli bir RPC yöntemidir: abasit RPC İstemci sunucuya bir istek gönderir ve tıpkı yerel bir fonksiyon çağrısı gibi yanıt gelmesini bekler.

Mesaj türleri

Kaynak kodun proto/routeguide.proto dosyasında öncelikle Point mesaj türünü tanımlayın. Point bir haritada enlem-boylam koordinat çiftini temsil eder. Bu kod laboratuvarı çalışmasında, koordinatlar için tamsayılar kullanın:

message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

1 ve 2 sayıları, message yapısındaki alanların her biri için benzersiz kimlik numaralarıdır.

Ardından, Feature mesaj türünü tanımlayın. Bir Feature, bir Point tarafından belirtilen bir konumdaki bir şeyin adı veya posta adresi için bir string alanı kullanır:

message Feature {
  // The name or address of the feature.
  string name = 1;

  // The point where the feature is located.
  Point location = 2;
}

4. İstemci ve sunucu kodunu oluşturun.

Yukarıda yaptığınız tüm eklemeler de dahil olmak üzere, generated/ dizinindeki .proto dosyasından oluşturulan kodu size zaten verdik. Ancak, kod oluşturma işleminin nasıl çalıştığını kısaca açıklamak istiyoruz.

.proto dosyamız, bir istemci veya sunucunun kullandığı tüm yapıları ve işlevleri tanımlar. Bu kodu otomatik olarak oluşturmak için grpc-protobuf-build crate'i ile birlikte bir Cargo derleme komut dosyası (build.rs) kullanıyoruz.

Cargo.toml'da [build-dependencies]'nin altına grpc-protobuf-build ekliyoruz.

build.rs içinde, grpc_protobuf_build::CodeGen dosyasını proto/routeguide.proto dosyasını generated/ dizinine derleyecek şekilde yapılandırıyoruz. İşte en önemli satırlar:

grpc_protobuf_build::CodeGen::new()
    .include("proto")
    .input("routeguide.proto")
    .output_dir("generated")
    .compile()
    .unwrap();

Bu, grpc_protobuf_build crate'inin kod oluşturma işlevini çağırarak routeguide.proto değerini iletir. Bu işlevi, yalnızca bir özellik bayrağı iletildiğinde çalışacak şekilde bir koda yerleştirdik. Böylece, yalnızca istediğiniz zaman yeniden oluşturulur. Kodu sizin için oluşturduğumuzdan bu işlemi şimdi çalıştırmanız gerekmez.

cargo build --bin routeguide-server --features regenerate_proto

cargo build komutunu çalıştırdığınızda build.rs, protokol arabelleği tanımlarını generate/ dizininde derler. Bu tanımlar şunları içerir:

  • Point ve Feature mesaj türleri için yapı tanımları.
  • Sunucu için uygulamamız gereken bir Tonic hizmet özelliği: route_guide_server::RouteGuide.
  • Sunucuyu çağırmak için kullanacağımız bir gRPC-Rust istemci türü: route_guide_client::RouteGuideClient<T>.

Daha fazla bilgi için protoc-gen-rust-grpc kılavuzuna bakabilirsiniz.

Ardından, hizmet yöntemlerini sunucuda uygulayacağız.

5. Hizmeti uygulama

src/server/server.rs içinde, oluşturulan kodu gRPC'nin include_generated_proto! makrosu aracılığıyla kapsam içine alabilir, RouteGuide özelliğini ve Point öğesini içe aktarabiliriz.

mod grpc_pb {
    grpc::include_generated_proto!("generated", "routeguide");
}

use grpc_pb::{
    route_guide_server::{RouteGuideServer, RouteGuide},
    Point, Feature,
};

Hizmetimizi temsil edecek bir yapı tanımlayarak başlayabiliriz. Bunu şimdilik src/server/server.rs üzerinde yapabiliriz:

#[derive(Debug)]
pub struct RouteGuideService {
    features: Vec<Feature>,
}

Şimdi, oluşturduğumuz koddan route_guide_server::RouteGuide özelliğini uygulamamız gerekiyor.

Basit Tekli RPC

RouteGuideService, tüm hizmet yöntemlerimizi uygular. Sunucu tarafındaki get_feature işlevi, asıl işin yapıldığı yerdir: Bu işlev, istemciden bir Point mesajı alır ve bilinen yerlerin listesinden ilgili konum bilgilerini Feature mesajıyla döndürür. İşlevin src/server/server.rs'daki uygulanma şekli:

#[tonic::async_trait]
impl RouteGuide for RouteGuideService {
    async fn get_feature(&self, request: Request<Point>) -> Result<Response<Feature>, Status> {
        println!("GetFeature = {:?}", request);
        let requested_point = request.get_ref();
        for feature in self.features.iter() {
            if feature.location().latitude() == requested_point.latitude() {
                if feature.location().longitude() == requested_point.longitude(){
                    return Ok(Response::new(feature.clone()))
                };
            };    
        }
        Ok(Response::new(Feature::default()))
    }
}

Bu yöntemi uyguladıktan sonra, istemcilerin hizmetimizi kullanabilmesi için bir gRPC sunucusu da başlatmamız gerekir. main() yerine bunu kullanın.

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "[::1]:10000".parse().unwrap();
    println!("RouteGuideServer listening on: {addr}");
    let route_guide = RouteGuideService {
        features: load(),
    };
    let svc = RouteGuideServer::new(route_guide);
    Server::builder().add_service(svc).serve(addr).await?;
    Ok(())
}

main() bölgesinde adım adım neler oluyor?

  1. İstemci isteklerini dinlemek için kullanmak istediğimiz bağlantı noktasını belirtin.
  2. Yardımcı fonksiyon load()'i çağırarak özellikleri yüklenmiş bir RouteGuideService oluşturun.
  3. Oluşturduğumuz servisi kullanarak RouteGuideServer::new() gRPC sunucusunun bir örneğini oluşturun.
  4. Hizmet uygulamamızı gRPC sunucusuna kaydedin.
  5. Sunucuda port bilgilerimizi kullanarak serve() çağrısı yapın ve işlem sonlandırılana kadar engelleme beklemesi gerçekleştirin.

6. Müşteriyi oluşturun

Bu bölümde, src/client/client.rs içindeki RouteGuide servisimiz için bir Rust istemcisi oluşturmaya bakacağız.

src/server/server.rs'da yaptığımız gibi, oluşturulan kodu gRPC'nin include_generated_proto! makrosu aracılığıyla kapsam içine alabilir ve RouteGuideClient türünü içe aktarabiliriz.

mod grpc_pb {
    grpc::include_generated_proto!("generated", "routeguide");
}

use grpc_pb::{
    route_guide_client::RouteGuideClient,
    Point,
};

Çağrı hizmeti yöntemleri

gRPC-Rust'ta, RPC'ler eşzamansız ve engellemeyen yapıdadır ve sunucudan yanıt beklemek için Rust'ın async/await sözdizimini kullanır.

Servis metotlarını çağırmak için öncelikle Channel::builder() kullanarak bir Channel oluşturuyoruz, sunucu adresini (dns:///[::1]:10000) ve bağlantı kimlik bilgilerini (LocalChannelCredentials) belirtiyoruz. Ardından istemcimizi başlatmak için kanalı RouteGuideClient::new()'e iletiyoruz:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a new gRPC channel
    let channel = Channel::builder(
        "dns:///[::1]:10000",
        Arc::new(LocalChannelCredentials::new()),
    )
    .build();

    // Create a new client
    let client = RouteGuideClient::new(channel);



}

Bu fonksiyonda, RouteGuideClient::new() genel kanalı, .proto hizmet tanımımızda tanımlanan yöntemleri uygulayan oluşturulmuş istemci saplamasına bağlar.

Basit RPC

Basit RPC GetFeature'ı çağırmak, yerel bir metodu çağırmak kadar kolaydır. Bunu main() içine ekleyin:

println!("*** SIMPLE RPC ***");
let point = proto!(Point {
    latitude: 409_146_138,
    longitude: -746_188_906,
});
let response = client
    .get_feature(point)
    .await
    .expect("RPC error");

gRPC-Rust'ta, protobuf mesajı Point'ı doğrudan client.get_feature(point)'e iletiyoruz. Döndürülen geleceği beklemek, başka yöntem çağrılarına gerek kalmadan doğrudan Feature yanıtını verir.

Ardından, yanıttaki alanları yazdırın:

println!(
    "Response = Name = \"{}\", Latitude = {}, Longitude = {}",
    response.name(),
    response.location().latitude(),
    response.location().longitude()
);

Özetle, istemcinin main() işlevi şu şekilde görünmelidir:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. Create a new gRPC channel
    let channel = Channel::builder(
        "dns:///[::1]:10000",
        Arc::new(LocalChannelCredentials::new()),
    )
    .build();

    // 2. Instantiate the generated RouteGuideClient
    let client = RouteGuideClient::new(channel);

    println!("*** SIMPLE RPC ***");
    let point = proto!(Point {
        latitude: 409_146_138,
        longitude: -746_188_906,
    });
    let response = client
        .get_feature(point)
        .await
        .expect("RPC error");

    println!(
        "Response = Name = \"{}\", Latitude = {}, Longitude = {}",
        response.name(),
        response.location().latitude(),
        response.location().longitude()
    );
    Ok(())
}

7. Deneyin

İstemci ve sunucunuzu çalıştırmak için öncelikle her iki ikili hedef dosyasının da Cargo.toml içinde tanımlanmış olduğundan emin olun:

[[bin]]
name = "routeguide-server"
path = "src/server/server.rs"

[[bin]]
name = "routeguide-client"
path = "src/client/client.rs"

Ardından, çalışma dizinimizden aşağıdaki komutları çalıştırın:

  1. Sunucuyu tek bir terminalde çalıştırın:
cargo run --bin routeguide-server
  1. İstemciyi başka bir terminalden çalıştırın:
cargo run --bin routeguide-client

Aşağıdakine benzer bir çıktı göreceksiniz (anlaşılırlık açısından zaman damgaları çıkarılmıştır):

*** SIMPLE RPC ***
Response = Name = "Berkshire Valley Management Area Trail, Jefferson, NJ, USA", Latitude = 409146138, Longitude = -746188906

8. Sırada ne var?

9. Bu Codelab'e katkıda bulunanlar

  • Cathy Zhao
  • Lucio Franco
  • Arvind Bright
  • Nathaniel Ford