gRPC-Rust ile Başlarken - Akış

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, bir istemcinin rotasındaki özellikler hakkında bilgi almak, istemcinin rotasının özetini oluşturmak ve trafik güncellemeleri gibi rota bilgilerini sunucu ve diğer istemcilerle paylaşmak için gRPC kullanarak uzak bir sunucuya bağlanan bir istemciniz olacak.

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şturma özelliğini kullanarak Protokol Arabellekleri tanımından gRPC tabanlı bir istemci ve sunucu oluşturma.
  • gRPC ile istemci-sunucu akışı 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, 1.88.0 sürümü. Yükleme talimatlarını buradan inceleyin.

Kodu alın

Bu codelab'de, uygulamayı sıfırdan oluşturmak zorunda kalmamanız için uygulamanın kaynak kodunun tamamlayabileceğiniz bir iskeleti sağlanır. Aşağıdaki adımlarda, protokol arabelleği derleyici eklentilerini kullanarak standart gRPC kodu oluşturma da dahil olmak üzere uygulamanın nasıl tamamlanacağı gösterilmektedir.

Öncelikle codelab çalışma dizinini oluşturun ve cd içine gidin:

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

Codelab'i indirip ayıklayı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-streaming/start_here

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

Bir uygulamayı yazmayı atlamak isterseniz tamamlanmış kaynak kodunu GitHub'da bulabilirsiniz.

3. Mesajları ve hizmetleri tanımlama

İlk adımınız, Protocol Buffers'ı kullanarak uygulamanın gRPC hizmetini, RPC yöntemlerini, istek ve yanıt mesajı türlerini tanımlamaktır. Hizmetinizde sunulacaklar:

  • Sunucunun uyguladığı ve istemcinin çağırdığı ListFeatures, RecordRoute ve RouteChat adlı RPC yöntemleri.
  • Yukarıdaki yöntemler çağrıldığında istemci ile sunucu arasında değiştirilen veri yapıları olan Point, Feature, Rectangle, RouteNote ve RouteSummary mesaj türleri.

Bu RPC yöntemleri ve mesaj türleri, sağlanan kaynak kodun proto/routeguide.proto dosyasında tanımlanır.

Protocol Buffers genellikle protobuf olarak bilinir. gRPC terminolojisi hakkında daha fazla bilgi için gRPC'nin Temel kavramlar, mimari ve yaşam döngüsü başlıklı makalesine bakın.

Mesaj türlerini tanımlama

Öncelikle RPC'lerimiz tarafından kullanılacak mesajlarımızı tanımlayalım. Kaynak kodun proto/routeguide.proto dosyasında öncelikle Point mesaj türünü tanımlayın. Point, haritadaki bir enlem-boylam koordinat çiftini temsil eder. Bu codelab'de koordinatlar için tam sayıları kullanın:

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

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

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

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

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

Ardından, enlem-boylam dikdörtgenini temsil eden bir Rectangle mesajı gelir. Bu dikdörtgen, çapraz olarak zıt iki nokta ("lo" ve "hi") olarak gösterilir.

message Rectangle {
  // One corner of the rectangle.
  Point lo = 1;

  // The other corner of the rectangle.
  Point hi = 2;
}

Ayrıca, belirli bir noktada gönderilen mesajı temsil eden bir RouteNote mesajı.

message RouteNote {
  // The location from which the message is sent.
  Point location = 1;

  // The message to be sent.
  string message = 2;
}

Ayrıca RouteSummary mesajı da gereklidir. Bu mesaj, sonraki bölümde açıklanan bir RecordRoute RPC'ye yanıt olarak alınır. Alınan bireysel puanların sayısı, algılanan özelliklerin sayısı ve her nokta arasındaki mesafenin kümülatif toplamı olarak kapsanan toplam mesafeyi içerir.

message RouteSummary {
  // The number of points received.
  int32 point_count = 1;

  // The number of known features passed while traversing the route.
  int32 feature_count = 2;

  // The distance covered in metres.
  int32 distance = 3;

  // The duration of the traversal in seconds.
  int32 elapsed_time = 4;
}

Hizmet yöntemlerini tanımlama

Öncelikle hizmetimizi tanımlayalım, daha sonra da mesajlarımızı tanımlayalım. Bir hizmeti tanımlamak için .proto dosyanızda adlandırılmış bir hizmet belirtirsiniz. 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.

Hizmet tanımınızda TBG yöntemlerini tanımlayın, istek ve yanıt türlerini belirtin. Bu codelab bölümünde şunları tanımlayalım:

ListFeatures

Belirtilen Rectangle içinde bulunan Feature'ları alır. Dikdörtgen geniş bir alanı kaplayıp çok sayıda özellik içerebileceğinden sonuçlar tek seferde döndürülmek yerine (ör. tekrarlanan alan içeren bir yanıt mesajında) yayınlanır.

Bu RPC için uygun bir tür, sunucu tarafı akışlı RPC'dir: istemci sunucuya bir istek gönderir ve bir dizi mesajı okumak için bir akış alır. İstemci, döndürülen akışı okuyarak mesajları tüketir. Örneğimizde de görebileceğiniz gibi, yanıt türünün önüne stream anahtar kelimesini koyarak sunucu tarafı akış yöntemini belirtiyorsunuz.

rpc ListFeatures(Rectangle) returns (stream Feature) {}

RecordRoute

Geçilen bir rotadaki Point akışını kabul eder ve geçiş tamamlandığında RouteSummary döndürür.

Bu durumda istemci tarafı akış RPC uygun görünmektedir: İstemci, bir ileti dizisi yazar ve bunları yine sağlanan bir akışı kullanarak sunucuya gönderir. İstemci mesajları yazmayı bitirdikten sonra sunucunun hepsini okumasını ve yanıtını döndürmesini bekler. İstemci tarafı akış yöntemini, istek türünün önüne stream anahtar kelimesini yerleştirerek belirtirsiniz.

rpc RecordRoute(stream Point) returns (RouteSummary) {}

RouteChat

Bir rota geçilirken gönderilen RouteNote akışını kabul ederken diğer RouteNote'ları (ör. diğer kullanıcılardan) alır.

Bu, çift yönlü akışın tam olarak kullanım alanıdır. Çift yönlü akış RPC'sinde her iki taraf da okuma/yazma akışı kullanarak bir ileti dizisi gönderir. İki akış birbirinden bağımsız olarak çalışır. Bu nedenle, istemciler ve sunucular istedikleri sırada okuma ve yazma işlemi yapabilir.

Örneğin, sunucu yanıtlarını yazmadan önce tüm istemci mesajlarını almayı bekleyebilir veya alternatif olarak bir mesajı okuyup ardından bir mesaj yazabilir ya da okuma ve yazma işlemlerinin başka bir kombinasyonunu kullanabilir.

Her akıştaki iletilerin sırası korunur. Bu tür bir yöntemi, hem istekten hem de yanıttan önce stream anahtar kelimesini yerleştirerek belirtirsiniz.

rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}

4. İstemci ve sunucu kodunu oluşturma

.proto dosyasından oluşturulan kodu, yukarıda yaptığınız tüm eklemeler de dahil olmak üzere generated/ dizininde sizinle paylaştık. Ancak kod oluşturma özelliğinin nasıl çalıştığını açıklamak istiyoruz.

.proto dosyamız, bir istemcinin veya sunucunun kullandığı tüm yapıları ve işlevleri açıklar. 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 grpc-protobuf-build'yi derleme bağımlılığı olarak ekledik.

build.rs içinde, proto/routeguide.proto öğesini generated/ dizinine derlemek için grpc_protobuf_build::CodeGen öğesini yapılandırırız. Önemli satırları burada bulabilirsiniz:

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

Öncelikle RouteGuide sunucuyu nasıl oluşturduğumuza bakalım. RouteGuide hizmetimizin işini yapmasını sağlamak için iki bölüm vardır:

  • Hizmet tanımımızdan oluşturulan hizmet arayüzünü uygulama: Hizmetimizin asıl "işini" yapma.
  • İstemcilerden gelen istekleri dinlemek ve bunları doğru yöntem uygulamasına yönlendirmek için bir gRPC sunucusu çalıştırmak.

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");
}

pub use grpc_pb::{
    route_guide_server::{RouteGuideServer, RouteGuide},
    Point, Feature, Rectangle, RouteNote, RouteSummary
};

Hizmetimizi temsil edecek bir yapı tanımlayarak başlayabiliriz. Şimdilik bunu 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.

RouteGuide'ı uygulama

Oluşturulan RouteGuide arayüzünü uygulamamız gerekir. Uygulama bu şekilde görünür. Bu zaten şablonda mevcut.

#[tonic::async_trait]
impl RouteGuide for RouteGuideService {
    async fn list_features(
        &self,
        request: Request<Rectangle>,
    ) -> Result<Response<ListFeaturesStream>, Status> {
        ...
    }

    async fn record_route(
        &self,
        request: Request<tonic::Streaming<Point>>,
    ) -> Result<Response<RouteSummary>, Status> {
        ...
    }

    async fn route_chat(
        &self,
        request: Request<tonic::Streaming<RouteNote>>,
    ) -> Result<Response<RouteChatStream>, Status> {
        ...
    }
}

Her RPC uygulamasını ayrıntılı olarak inceleyelim.

Sunucu tarafı akış RPC'si: ListFeatures

ListFeatures ile başlayalım. Bu, sunucu tarafı akış RPC'sidir (istemci bir mesaj gönderir, sunucu ise birçok mesajla yanıt verir). Bu nedenle, istemcimize birden fazla Feature göndermemiz gerekir.

async fn list_features(
        &self,
        request: Request<Rectangle>,
    ) -> Result<Response<ListFeaturesStream>, Status> {
    println!("ListFeatures = {:?}", request);

    let (tx, rx) = mpsc::channel(4);
    let features = self.features.clone();

    tokio::spawn(async move {
        for feature in &features[..] {
            if in_range(&feature.location().to_owned(), request.get_ref()) {
                println!("  => send {feature:?}");
                tx.send(Ok(feature.clone())).await.unwrap();
            }
        }
        println!(" /// done sending");
    });

    let output_stream = ReceiverStream::new(rx);
    Ok(Response::new(Box::pin(output_stream)))
}

Gördüğünüz gibi, bir istek nesnesi (istemcimizin Rectangle içinde Features bulmak istediği) alıyoruz. Bu kez bir değer akışı döndürmemiz gerekiyor. Bir kanal oluşturup yeni bir eşzamansız görev başlatıyoruz. Bu görevde bir arama gerçekleştirip kısıtlamalarımızı karşılayan özellikleri kanala gönderiyoruz. Kanalın yayın kısmı, arayana tonic::Response içinde döndürülür.

İstemci tarafı yayın RPC'si: RecordRoute

Şimdi biraz daha karmaşık bir yönteme, yani istemci tarafı akış yöntemine RecordRoute bakalım. Bu yöntemde istemciden bir Points akışı alıp yolculuklarıyla ilgili bilgileri içeren tek bir RouteSummary döndürüyoruz. Giriş olarak bir akış alır. Sunucu, bu akışı hem iletileri okumak hem de yazmak için kullanabilir. next() yöntemini kullanarak istemci mesajlarını yineleyebilir ve tek yanıtını döndürebilir.

async fn record_route(
        &self,
        request: Request<tonic::Streaming<Point>>,
    ) -> Result<Response<RouteSummary>, Status> {
    println!("RecordRoute");
    let mut stream = request.into_inner();
    let mut summary = RouteSummary::default();
    let mut last_point = None;
    let now = Instant::now();

    while let Some(point) = stream.next().await {
        let point = point?;
        println!("  ==> Point = {point:?}");

        // Increment the point count
        summary.set_point_count(summary.point_count() + 1);

        // Find features
        for feature in &self.features[..] {
            if feature.location().latitude() == point.latitude() {
                if feature.location().longitude() == point.longitude(){
                    summary.set_feature_count(summary.feature_count() + 1);
                }
            }
        }

        // Calculate the distance
        if let Some(ref last_point) = last_point {
            let new_dist = summary.distance() + calc_distance(last_point, &point);
            summary.set_distance(new_dist);
        }
        last_point = Some(point);
    }
    summary.set_elapsed_time(now.elapsed().as_secs() as i32);
    Ok(Response::new(summary))
}

Yöntem gövdesinde, istemcimizin isteklerini bir istek nesnesine (bu örnekte Point) tekrar tekrar okumak için akışın next() yöntemini kullanırız. Bu işlem, başka mesaj kalmayana kadar devam eder. Bu değer None ise akış hala iyi durumdadır ve okumaya devam edebilir.

Çift yönlü akış RPC'si: RouteChat

Son olarak, çift yönlü yayın RPC'mize RouteChat() bakalım.

async fn route_chat(
        &self,
        request: Request<tonic::Streaming<RouteNote>>,
    ) -> Result<Response<RouteChatStream>, Status> {
    println!("RouteChat");

    let mut notes: HashMap<(i32, i32), Vec<RouteNote>> = HashMap::new();
    let mut stream = request.into_inner();

    let output = async_stream::try_stream! {
        while let Some(note) = stream.next().await {
            let note = note?;
            let location = note.location();
            let key = (location.latitude(), location.longitude());
            let location_notes = notes.entry(key).or_insert(vec![]);
            location_notes.push(note);
            for note in location_notes {
                yield note.clone();
            }
        }
    };
    Ok(Response::new(Box::pin(output)))
}

Bu kez, istemci tarafı akış örneğimizde olduğu gibi mesaj okumak ve yazmak için kullanılabilecek bir akış elde ederiz. Ancak bu kez, istemci mesaj akışına mesaj yazmaya devam ederken değerleri yöntemimizin akışı üzerinden döndürüyoruz. Burada okuma ve yazma söz dizimi, sunucunun RouteChatStream döndürmesi dışında istemci akışı yöntemimize çok benzer. Her iki taraf da diğerinin iletilerini her zaman yazıldıkları sırayla alsa da hem istemci hem de sunucu, iletileri herhangi bir sırada okuyup yazabilir. Akışlar tamamen bağımsız olarak çalışır.

Çıkış akışını try_stream! kullanarak oluştururuz. Bu, akışın hata döndürebileceğini gösterir.

Sunucuyu başlatma

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

#[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. Özellikleri yüklenmiş bir RouteGuideService oluşturun.
  3. Oluşturduğumuz hizmeti kullanarak RouteGuideServer::new() ile gRPC sunucusunun bir örneğini oluşturun.
  4. Hizmet uygulamamızı gRPC sunucusuna kaydedin.
  5. İşlem sonlandırılana kadar engelleme beklemesi yapmak için bağlantı noktası ayrıntılarımızla birlikte sunucuda serve() komutunu çalıştırın.

6. İstemciyi oluşturma

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

Öncelikle, oluşturulan kodu kapsam içine alın.

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

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

Hizmet yöntemlerini çağırma

Şimdi de hizmet yöntemlerimizi nasıl çağırdığımıza bakalım. gRPC-Rust'ta akış RPC'leri, Rust'ın async/await söz dizimini ve Tokio akışlarını kullanarak eşzamansız ve engellemeyen bir şekilde çalışır.

Sunucu tarafı yayın RPC'si: PrintFeatures

Sunucu akışlı RPC'lerde, istemci sunucuya tek bir istek mesajı gönderir ve karşılığında bir yanıt mesajı akışı alır. client.rs içinde sunucu tarafı yayın yöntemini list_features() (proto'muzda bulunan ListFeatures rpc bildirimiyle eşleşir) çağırdığımız yer aşağıda gösterilmiştir. Sunucu da sırayla bir Feature mesajı akışı geri gönderir:

async fn print_features(client: &RouteGuideClient<Channel>) -> Result<(), Box<dyn Error>> {
    let rectangle = proto!(Rectangle {
        lo: proto!(Point {
            latitude: 400_000_000,
            longitude: -750_000_000,
        }),
        hi: proto!(Point {
            latitude: 420_000_000,
            longitude: -730_000_000,
        }),
    });

    let mut stream = client.list_features(rectangle).await;

    while let Some(feature) = stream.recv().await {
        println!(
            "FEATURE: Name = \"{}\", Lat = {}, Lon = {}",
            feature.name(),
            feature.location().latitude(),
            feature.location().longitude()
        );
    }
    let status = stream.status().await;
    assert!(status.is_ok(), "{:?}", status);
    Ok(())
}

İstemci tarafı yayın RPC'si: RecordRoute

İstemci tarafı akışını kullandığımızda istemci, sunucuya bir akış açar ve bir ileti dizisi gönderir. Yayın tamamlandığında tek bir yanıt mesajı alacaktır.

Burada, client.record_route().await ile aramayı başlatıyoruz, stream.send(point).await kullanarak akış boyunca tek tek birkaç oluşturulmuş Point koordinatı gönderiyoruz ve ardından tek sunucu RouteSummary mesajını almak için akışı stream.close_and_recv().await ile kapatıyoruz.

async fn run_record_route(client: &RouteGuideClient<Channel>) -> Result<(), Box<dyn Error>> {
    let mut rng = rand::rng();
    let point_count: i32 = rng.random_range(2..100);

    let mut points = vec![];
    for _ in 0..=point_count {
        points.push(random_point(&mut rng));
    }

    println!("Traversing {} points", points.len());
    let mut stream = client.record_route().await;

    for point in &points {
        if stream.send(point).await.is_err() {
            break;
        }
    }

    match stream.close_and_recv().await {
        Ok(response) => {
            println!(
                "SUMMARY: Feature Count = {}, Distance = {}",
                response.feature_count(),
                response.distance()
            );
        }
        Err(e) => println!("something went wrong: {e:?}"),
    }
    Ok(())
}

Çift yönlü akış RPC'si: RouteChat

Son olarak, çift yönlü yayın RPC'mize RouteChat() bakalım. Burada hem istemci hem de sunucu, bir ileti dizisini ileri geri geçirir. tokio görevini oluşturarak sunucuya tx.send(note).await.is_err() ile sürekli mesaj göndeririz. Bu sırada rx.recv().await, sunucudan gelen yanıt mesajlarını dinler ve bunları geldikçe yazdırır.

async fn run_route_chat(client: &RouteGuideClient<Channel>) -> Result<(), Box<dyn Error>> {
    let (mut tx, mut rx) = client.route_chat().await;

    let start = time::Instant::now();
    tokio::spawn(async move {
        let mut interval = time::interval(Duration::from_millis(50));
        for _ in 0..10 {
            let time = interval.tick().await;
            let elapsed = time.duration_since(start);
            let note = proto!(RouteNote {
                location: proto!(Point {
                    latitude: 409146138 + elapsed.as_millis() as i32,
                    longitude: -746188906,
                }),
                message: format!("at {elapsed:?}"),
            });
            if tx.send(note).await.is_err() {
                return;
            }
        }
        tx.close();
    });

    while let Some(note) = rx.recv().await {
        println!(
            "Note: Latitude = {}, Longitude = {}, Message = \"{}\"",
            note.location().latitude(),
            note.location().longitude(),
            note.message()
        );
    }
    let status = rx.status().await;
    assert!(status.is_ok(), "{:?}", status);
    Ok(())
}

Her iki taraf da diğerinin iletilerini her zaman yazıldıkları sırayla alsa da hem istemci hem de sunucu, iletileri herhangi bir sırada okuyup yazabilir. Akışlar tamamen bağımsız olarak çalışır.

İstemci oluşturma ve iletme

Servis yöntemlerini çağırmak için öncelikle sunucuyla iletişim kurmak üzere bir kanal oluşturmamız gerekiyor. Bunu oluşturmak için önce bir uç nokta oluşturur, bu uç noktaya bağlanır ve RouteGuideClient::new()'ya bağlanıldığında oluşturulan kanalı aşağıdaki gibi iletiriz:

// Create channel to connect to server
let channel = Channel::builder(
    "dns:///[::1]:10000",
    Arc::new(LocalChannelCredentials::new()),
)
.build();

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

Bu istemci oluşturulduktan sonra, istemciyi ileterek yukarıda yazdığımız yöntemleri çağırabiliriz. Bu kodun tamamını, Tokio eşzamansız çalışma zamanını kullanan main() içine ekliyoruz. Tam kod:

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

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

    println!("\n*** SERVER STREAMING ***");
    print_features(&client).await?;

    println!("\n*** CLIENT STREAMING ***");
    run_record_route(&client).await?;

    println!("\n*** BIDIRECTIONAL STREAMING ***");
    run_route_chat(&client).await?;

    Ok(())
}

7. Deneyin

İstemcinizi ve sunucunuzu çalıştırmak için öncelikle her iki ikili hedefin de Cargo.toml içinde tanımlandığını doğrulayın:

[[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ırma:
cargo run --bin routeguide-client

Aşağıdakine benzer bir çıkış görürsünüz:

*** SERVER STREAMING ***
FEATURE: Name = "Patriots Path, Mendham, NJ 07945, USA", Lat = 407838351, Lon = -746143763
FEATURE: Name = "101 New Jersey 10, Whippany, NJ 07981, USA", Lat = 408122808, Lon = -743999179
FEATURE: Name = "U.S. 6, Shohola, PA 18458, USA", Lat = 413628156, Lon = -749015468
...
*** CLIENT STREAMING ***
Traversing 86 points
SUMMARY: Feature Count = 0, Distance = 803709356

*** BIDIRECTIONAL STREAMING ***
Note: Latitude = 409146138, Longitude = -746188906, Message = "at 112.45µs"
Note: Latitude = 409146139, Longitude = -746188906, Message = "at 1.00011245s"
Note: Latitude = 409146140, Longitude = -746188906, Message = "at 2.00011245s"

8. Sırada ne var?

9. Bu Codelab'e katkıda bulunanlar

  • Cathy Zhao
  • Arvind Bright
  • Nathaniel Ford