1. บทนำ
ใน Codelab นี้ คุณจะได้ใช้ gRPC-Rust เพื่อสร้างไคลเอ็นต์และเซิร์ฟเวอร์ที่เป็นรากฐานของแอปพลิเคชันการกำหนดเส้นทางที่เขียนด้วย Rust
เมื่อจบบทแนะนำ คุณจะมีไคลเอ็นต์ที่เชื่อมต่อกับเซิร์ฟเวอร์ระยะไกลโดยใช้ การใช้งานอย่างเป็นทางการของ Rust ของ gRPC Protocol เพื่อดึงชื่อหรือที่อยู่ทางไปรษณีย์ของสถานที่หนึ่งๆ ที่พิกัดเฉพาะบนแผนที่ แอปพลิเคชันที่สมบูรณ์อาจใช้การออกแบบไคลเอ็นต์-เซิร์ฟเวอร์นี้เพื่อแสดงรายการหรือสรุปจุดที่น่าสนใจตามเส้นทาง
บริการนี้กำหนดไว้ในไฟล์ Protocol Buffers ซึ่งจะใช้เพื่อสร้างโค้ดเริ่มต้นสำหรับไคลเอ็นต์และเซิร์ฟเวอร์เพื่อให้สื่อสารกันได้ ซึ่งจะช่วยประหยัดเวลาและความพยายามในการใช้งานฟังก์ชันการทำงานดังกล่าว
โค้ดที่สร้างขึ้นนี้จะจัดการกับความซับซ้อนของการสื่อสารระหว่างเซิร์ฟเวอร์และไคลเอ็นต์ รวมถึงการซีเรียลไลซ์และดีซีเรียลไลซ์ข้อมูล
สิ่งที่คุณจะได้เรียนรู้
- วิธีใช้ Protocol Buffers เพื่อกำหนด API ของบริการ
- วิธีสร้างไคลเอ็นต์ที่ใช้ gRPC จากคำจำกัดความของ Protocol Buffer โดยใช้การสร้างโค้ดอัตโนมัติ
- ความเข้าใจเกี่ยวกับการสื่อสารระหว่างไคลเอ็นต์กับเซิร์ฟเวอร์ด้วย gRPC
Codelab นี้เหมาะสำหรับนักพัฒนาแอป Rust ที่เพิ่งเริ่มใช้ gRPC หรือต้องการทบทวน gRPC หรือผู้ที่สนใจสร้างระบบแบบกระจาย ไม่จำเป็นต้องมีประสบการณ์ในการใช้ gRPC มาก่อน
2. ก่อนเริ่มต้น
ข้อกำหนดเบื้องต้น
ตรวจสอบว่าคุณได้ติดตั้งสิ่งต่อไปนี้แล้ว
รับโค้ด
Codelab นี้มีโครงสร้างพื้นฐานของซอร์สโค้ดของแอปพลิเคชันให้คุณดำเนินการต่อได้เลยโดยไม่ต้องเริ่มต้นจากศูนย์ ขั้นตอนต่อไปนี้จะแสดงวิธีสร้างแอปพลิเคชันให้เสร็จสมบูรณ์ ซึ่งรวมถึงการใช้ปลั๊กอินคอมไพเลอร์บัฟเฟอร์โปรโตคอลเพื่อสร้างโค้ด gRPC เริ่มต้น
ขั้นแรก ให้สร้างไดเรกทอรีการทำงานของ Codelab แล้วเปลี่ยนไดเรกทอรีเป็นไดเรกทอรีนั้น
mkdir grpc-rust-getting-started && cd grpc-rust-getting-started
ดาวน์โหลดและแตกไฟล์ Codelab
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
หรือคุณจะดาวน์โหลดไฟล์ .zip ที่มีเฉพาะไดเรกทอรี Codelab แล้วแตกไฟล์ด้วยตนเองก็ได้
ซอร์สโค้ดที่เสร็จสมบูรณ์แล้วพร้อมใช้งานบน GitHub หากคุณไม่ต้องการพิมพ์การใช้งาน
3. กำหนดบริการ
ขั้นตอนแรกคือการกำหนดบริการ gRPC ของแอปพลิเคชัน เมธอด RPC รวมถึงประเภทข้อความคำขอและการตอบกลับโดยใช้ Protocol Buffers บริการของคุณจะมีสิ่งต่อไปนี้
- เมธอด RPC ชื่อ
GetFeatureที่เซิร์ฟเวอร์ใช้งานและไคลเอ็นต์เรียก - ประเภทข้อความ
PointและFeatureซึ่งเป็นโครงสร้างข้อมูลที่ไคลเอ็นต์และเซิร์ฟเวอร์แลกเปลี่ยนกันเมื่อใช้เมธอดGetFeatureไคลเอ็นต์จะระบุพิกัดแผนที่เป็นPointในคำขอGetFeatureไปยังเซิร์ฟเวอร์ และเซิร์ฟเวอร์จะตอบกลับด้วยFeatureที่เกี่ยวข้องซึ่งอธิบายสิ่งที่อยู่ในพิกัดดังกล่าว
เมธอด RPC และประเภทข้อความทั้งหมดนี้จะกำหนดไว้ในไฟล์ proto/routeguide.proto ของซอร์สโค้ดที่ให้มา
Protocol Buffers หรือที่รู้จักกันโดยทั่วไปว่า protobuf ดูข้อมูลเพิ่มเติมเกี่ยวกับคำศัพท์ gRPC ได้ที่ แนวคิดหลัก สถาปัตยกรรม และวงจรชีวิตของ gRPC
เมธอดบริการ
ก่อนอื่น เรามากำหนดเมธอดบริการ แล้วกำหนดประเภทข้อความ Point และ Feature ไฟล์ proto/routeguide.proto มีโครงสร้าง service ชื่อ RouteGuide ซึ่งกำหนดเมธอดอย่างน้อย 1 รายการที่บริการของแอปพลิเคชันมีให้
เพิ่มเมธอด rpc ชื่อ GetFeature ภายในคำจำกัดความ RouteGuide ดังที่อธิบายไว้ก่อนหน้านี้ เมธอดนี้จะค้นหาชื่อหรือที่อยู่ของสถานที่จากชุดพิกัดที่กำหนด ดังนั้นให้ GetFeature แสดงผล Feature สำหรับ Point ที่กำหนด
service RouteGuide {
// Definition of the service goes here
// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}
}
นี่คือเมธอด RPC แบบ Unary อย่างง่าย ซึ่งเป็น RPC อย่างง่ายที่ไคลเอ็นต์ส่งคำขอไปยังเซิร์ฟเวอร์และรอการตอบกลับ เช่นเดียวกับการเรียกใช้ฟังก์ชันภายใน
ประเภทข้อความ
ในไฟล์ proto/routeguide.proto ของซอร์สโค้ด ให้กำหนดประเภทข้อความ Point ก่อน Point แสดงถึงคู่พิกัดละติจูด-ลองจิจูดบนแผนที่ สำหรับ Codelab นี้ ให้ใช้จำนวนเต็มสำหรับพิกัด
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
ตัวเลข 1 และ 2 เป็นหมายเลขรหัสที่ไม่ซ้ำกันสำหรับแต่ละช่องในโครงสร้าง message
จากนั้นกำหนดประเภทข้อความ Feature Feature ใช้ช่อง string สำหรับชื่อหรือที่อยู่ทางไปรษณีย์ของสิ่งหนึ่งๆ ในสถานที่ที่ระบุโดย Point
message Feature {
// The name or address of the feature.
string name = 1;
// The point where the feature is located.
Point location = 2;
}
4. สร้างโค้ดไคลเอ็นต์และเซิร์ฟเวอร์
เราได้ให้โค้ดที่สร้างขึ้นจากไฟล์ .proto ในไดเรกทอรี generated/ แก่คุณแล้ว ซึ่งรวมถึงส่วนเพิ่มเติมทั้งหมดที่คุณทำไว้ข้างต้น อย่างไรก็ตาม เราขอใช้เวลาสักครู่เพื่ออธิบายวิธีการสร้างโค้ด
ไฟล์ .proto ของเราอธิบายโครงสร้างและฟังก์ชันทั้งหมดที่ไคลเอ็นต์หรือเซิร์ฟเวอร์ใช้ เราใช้สคริปต์บิลด์ Cargo (build.rs) ร่วมกับกล่อง grpc-protobuf-build เพื่อสร้างโค้ดนี้โดยอัตโนมัติ
ใน Cargo.toml เราจะเพิ่ม grpc-protobuf-build ไว้ใต้ [build-dependencies]
ใน build.rs เราจะกำหนดค่า grpc_protobuf_build::CodeGen เพื่อคอมไพล์ proto/routeguide.proto ลงในไดเรกทอรี generated/ บรรทัดสำคัญมีดังนี้
grpc_protobuf_build::CodeGen::new()
.include("proto")
.input("routeguide.proto")
.output_dir("generated")
.compile()
.unwrap();
ซึ่งจะเรียกใช้การสร้างโค้ดของกล่อง grpc_protobuf_build โดยส่ง routeguide.proto ไปยังกล่องดังกล่าว เราได้รวมโค้ดนี้ไว้ในโค้ดบางส่วนเพื่อให้ทำงานเฉพาะเมื่อมีการส่งแฟล็กฟีเจอร์เท่านั้น เพื่อให้ระบบสร้างโค้ดใหม่เมื่อคุณต้องการเท่านั้น คุณไม่จำเป็นต้องเรียกใช้โค้ดนี้ในตอนนี้ เนื่องจากเราได้สร้างโค้ดให้คุณแล้ว
cargo build --bin routeguide-server --features regenerate_proto
เมื่อคุณเรียกใช้ cargo build build.rs จะคอมไพล์คำจำกัดความของบัฟเฟอร์โปรโตคอลลงในไดเรกทอรี generate/ ซึ่งรวมถึง
- คำจำกัดความของโครงสร้างสำหรับประเภทข้อความ
PointและFeature - ลักษณะบริการ Tonic ที่เราจะต้องใช้งานสำหรับเซิร์ฟเวอร์:
route_guide_server::RouteGuide - ประเภทไคลเอ็นต์ gRPC-Rust ที่เราจะใช้เพื่อเรียกเซิร์ฟเวอร์:
route_guide_client::RouteGuideClient<T>
ดูข้อมูลเพิ่มเติมได้ที่คู่มือ protoc-gen-rust-grpc
จากนั้นเราจะใช้งานเมธอดบริการในเซิร์ฟเวอร์
5. ใช้งานบริการ
ใน src/server/server.rs เราสามารถนำโค้ดที่สร้างขึ้นมาใช้ได้ผ่านทางมาโคร include_generated_proto! ของ gRPC และนำเข้าลักษณะ RouteGuide และ Point
mod grpc_pb {
grpc::include_generated_proto!("generated", "routeguide");
}
use grpc_pb::{
route_guide_server::{RouteGuideServer, RouteGuide},
Point, Feature,
};
เรามาเริ่มจากการกำหนดโครงสร้างเพื่อแสดงบริการของเรา โดยตอนนี้เราจะทำใน src/server/server.rs
#[derive(Debug)]
pub struct RouteGuideService {
features: Vec<Feature>,
}
ตอนนี้เราต้องใช้งานลักษณะภาษา route_guide_server::RouteGuide จากโค้ดที่สร้างขึ้น
RPC แบบ Unary อย่างง่าย
RouteGuideService ใช้งานเมธอดบริการทั้งหมดของเรา ฟังก์ชัน get_feature ในฝั่งเซิร์ฟเวอร์เป็นที่ที่การทำงานหลักเกิดขึ้น โดยฟังก์ชันนี้จะรับข้อความ Point จากไคลเอ็นต์และแสดงผลข้อมูลสถานที่ที่เกี่ยวข้องจากรายการสถานที่ที่รู้จักในข้อความ Feature นี่คือการใช้งานฟังก์ชันใน src/server/server.rs
#[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()))
}
}
เมื่อใช้งานเมธอดนี้แล้ว เราต้องเริ่มต้นเซิร์ฟเวอร์ gRPC ด้วยเพื่อให้ไคลเอ็นต์ใช้บริการของเราได้จริง แทนที่ main() ด้วยโค้ดนี้
#[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() ทีละขั้นตอน:
- ระบุพอร์ตที่เราต้องการใช้เพื่อรับฟังคำขอของไคลเอ็นต์
- สร้าง
RouteGuideServiceที่โหลดฟีเจอร์โดยการเรียกใช้ฟังก์ชันตัวช่วยload() - สร้างอินสแตนซ์ของเซิร์ฟเวอร์ gRPC โดยใช้
RouteGuideServer::new()ด้วยบริการที่เราสร้างขึ้น - ลงทะเบียนการใช้งานบริการของเรากับเซิร์ฟเวอร์ gRPC
- เรียกใช้
serve()ในเซิร์ฟเวอร์พร้อมรายละเอียดพอร์ตเพื่อรอแบบบล็อกจนกว่าจะมีการหยุดกระบวนการ
6. สร้างไคลเอ็นต์
ในส่วนนี้ เราจะดูการสร้างไคลเอ็นต์ Rust สำหรับบริการ RouteGuide ใน src/client/client.rs
เช่นเดียวกับที่เราทำใน src/server/server.rs เราสามารถนำโค้ดที่สร้างขึ้นมาใช้ได้ผ่านทางมาโคร include_generated_proto! ของ gRPC และนำเข้าประเภท RouteGuideClient
mod grpc_pb {
grpc::include_generated_proto!("generated", "routeguide");
}
use grpc_pb::{
route_guide_client::RouteGuideClient,
Point,
};
เรียกใช้เมธอดบริการ
ใน gRPC-Rust, RPC จะทำงานแบบอะซิงโครนัสและไม่บล็อก โดยใช้ไวยากรณ์ async/await ของ Rust เพื่อรอการตอบกลับจากเซิร์ฟเวอร์
หากต้องการเรียกใช้เมธอดบริการ ก่อนอื่นเราต้องสร้าง Channel โดยใช้ Channel::builder() โดยระบุที่อยู่เซิร์ฟเวอร์ (dns:///[::1]:10000) และข้อมูลเข้าสู่ระบบการเชื่อมต่อ (LocalChannelCredentials) จากนั้นส่งช่องไปยัง RouteGuideClient::new() เพื่อสร้างอินสแตนซ์ไคลเอ็นต์
#[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);
}
ในฟังก์ชันนี้ RouteGuideClient::new() จะผูกช่องทั่วไปกับ Stub ไคลเอ็นต์ที่สร้างขึ้นซึ่งใช้งานเมธอดที่กำหนดไว้ในคำจำกัดความบริการ .proto
RPC อย่างง่าย
การเรียกใช้ RPC อย่างง่าย GetFeature นั้นง่ายเหมือนกับการเรียกใช้เมธอดภายใน เพิ่มโค้ดนี้ใน main()
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 เราจะส่งข้อความ protobuf Point ไปยัง client.get_feature(point) โดยตรง การรออนาคตที่แสดงผลจะให้การตอบกลับ Feature โดยตรงโดยไม่จำเป็นต้องเรียกใช้เมธอดเพิ่มเติม
จากนั้นพิมพ์ช่องจากการตอบกลับ
println!(
"Response = Name = \"{}\", Latitude = {}, Longitude = {}",
response.name(),
response.location().latitude(),
response.location().longitude()
);
โดยรวมแล้ว ฟังก์ชัน main() ของไคลเอ็นต์ควรมีลักษณะดังนี้
#[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. ลองเลย
หากต้องการเรียกใช้ไคลเอ็นต์และเซิร์ฟเวอร์ ให้ตรวจสอบก่อนว่ามีการกำหนดเป้าหมายไบนารีทั้ง 2 รายการไว้ใน Cargo.toml
[[bin]]
name = "routeguide-server"
path = "src/server/server.rs"
[[bin]]
name = "routeguide-client"
path = "src/client/client.rs"
จากนั้นเรียกใช้คำสั่งต่อไปนี้จากไดเรกทอรีการทำงาน
- เรียกใช้เซิร์ฟเวอร์ในเทอร์มินัลหนึ่ง
cargo run --bin routeguide-server
- เรียกใช้ไคลเอ็นต์จากเทอร์มินัลอื่น
cargo run --bin routeguide-client
คุณจะเห็นเอาต์พุตลักษณะนี้ โดยเราได้ละเว้นการประทับเวลาเพื่อความชัดเจน
*** SIMPLE RPC ***
Response = Name = "Berkshire Valley Management Area Trail, Jefferson, NJ, USA", Latitude = 409146138, Longitude = -746188906
8. ขั้นตอนถัดไป
- ทำ Codelab การเริ่มต้นใช้งาน gRPC-Rust (สตรีมมิง) ต่อไป
- สำรวจที่เก็บ gRPC-Rust อย่างเป็นทางการ
- ดูข้อมูลเพิ่มเติมเกี่ยวกับสถาปัตยกรรม gRPC ใน แนวคิดหลัก
- ดูเอกสารประกอบ gRPC-Rust ใน gRPC.io
9. ผู้ร่วมให้ข้อมูลใน Codelab นี้
- Cathy Zhao
- Lucio Franco
- Arvind Bright
- Nathaniel Ford