১. ভূমিকা
এই কোডল্যাবে, আপনি gRPC-Rust ব্যবহার করে একটি ক্লায়েন্ট ও সার্ভার তৈরি করবেন, যা Rust-এ লেখা একটি রাউট-ম্যাপিং অ্যাপ্লিকেশনের ভিত্তি স্থাপন করবে।
এই টিউটোরিয়ালটি শেষ করার পর, আপনি এমন একটি ক্লায়েন্ট তৈরি করতে পারবেন যা gRPC ব্যবহার করে একটি রিমোট সার্ভারের সাথে সংযোগ স্থাপন করে মানচিত্রের নির্দিষ্ট স্থানাঙ্কে অবস্থিত কোনো কিছুর নাম বা ডাক ঠিকানা সংগ্রহ করতে পারবে। একটি পূর্ণাঙ্গ অ্যাপ্লিকেশন কোনো পথের গুরুত্বপূর্ণ স্থানগুলোর তালিকা তৈরি বা সারসংক্ষেপ করার জন্য এই ক্লায়েন্ট-সার্ভার ডিজাইনটি ব্যবহার করতে পারে।
সার্ভিসটি একটি প্রোটোকল বাফারস ফাইলে সংজ্ঞায়িত করা আছে, যা ক্লায়েন্ট এবং সার্ভারের জন্য বয়লারপ্লেট কোড তৈরি করতে ব্যবহৃত হবে, যাতে তারা একে অপরের সাথে যোগাযোগ করতে পারে। এর ফলে ঐ কার্যকারিতাটি বাস্তবায়নে আপনার সময় ও শ্রম বাঁচবে।
এই জেনারেট করা কোডটি শুধু সার্ভার ও ক্লায়েন্টের মধ্যকার যোগাযোগের জটিলতাই নয়, ডেটার সিরিয়ালাইজেশন এবং ডিসিরিয়ালাইজেশনও সামলে নেয়।
আপনি যা শিখবেন
- সার্ভিস এপিআই সংজ্ঞায়িত করতে প্রোটোকল বাফার কীভাবে ব্যবহার করবেন
- স্বয়ংক্রিয় কোড জেনারেশন ব্যবহার করে প্রোটোকল বাফারস ডেফিনিশন থেকে কীভাবে একটি gRPC-ভিত্তিক ক্লায়েন্ট এবং সার্ভার তৈরি করা যায়।
- gRPC ব্যবহার করে ক্লায়েন্ট-সার্ভার যোগাযোগ সম্পর্কে ধারণা।
এই কোডল্যাবটি সেইসব রাস্ট ডেভেলপারদের জন্য তৈরি করা হয়েছে যারা gRPC-তে নতুন অথবা এর বিষয়ে নিজেদের জ্ঞান ঝালিয়ে নিতে চান, কিংবা যারা ডিস্ট্রিবিউটেড সিস্টেম তৈরিতে আগ্রহী। gRPC-তে পূর্ব অভিজ্ঞতার কোনো প্রয়োজন নেই।
২. শুরু করার আগে
পূর্বশর্ত
নিশ্চিত করুন যে আপনি নিম্নলিখিতগুলি ইনস্টল করেছেন:
- জিসিসি। এখানে দেওয়া নির্দেশাবলী অনুসরণ করুন।
- রাস্ট , সংস্করণ ১.৮৯.০। এখানে দেওয়া ইনস্টলেশন নির্দেশাবলী অনুসরণ করুন।
কোডটি নিন
যাতে আপনাকে একেবারে গোড়া থেকে শুরু করতে না হয়, সেজন্য এই কোডল্যাবটি অ্যাপ্লিকেশনটির সোর্স কোডের একটি কাঠামো প্রদান করে, যা আপনাকে সম্পূর্ণ করতে হবে। নিম্নলিখিত ধাপগুলো আপনাকে দেখাবে কীভাবে অ্যাপ্লিকেশনটি শেষ করতে হয়, যার মধ্যে প্রোটোকল বাফার কম্পাইলার প্লাগইন ব্যবহার করে বয়লারপ্লেট gRPC কোড তৈরি করার পদ্ধতিও অন্তর্ভুক্ত রয়েছে।
প্রথমে, কোডল্যাব ওয়ার্কিং ডিরেক্টরি তৈরি করুন এবং তার ভেতরে যান:
mkdir grpc-rust-getting-started && cd grpc-rust-getting-started
কোডল্যাবটি ডাউনলোড এবং এক্সট্র্যাক্ট করুন:
curl -sL https://github.com/grpc-ecosystem/grpc-codelabs/archive/refs/heads/v1.tar.gz \
| tar xvz --strip-components=4 \
grpc-codelabs-1/codelabs/grpc-rust-getting-started/start_here
বিকল্পভাবে, আপনি শুধু কোডল্যাব ডিরেক্টরি সম্বলিত .zip ফাইলটি ডাউনলোড করে ম্যানুয়ালি আনজিপ করতে পারেন।
আপনি যদি ইমপ্লিমেন্টেশন টাইপ করা এড়াতে চান, তাহলে সম্পূর্ণ সোর্স কোডটি গিটহাবে পাওয়া যাবে ।
৩. পরিষেবাটি সংজ্ঞায়িত করুন
আপনার প্রথম পদক্ষেপ হলো প্রোটোকল বাফার ব্যবহার করে অ্যাপ্লিকেশনটির gRPC সার্ভিস, এর RPC মেথড এবং এর রিকোয়েস্ট ও রেসপন্স মেসেজ টাইপগুলো সংজ্ঞায়িত করা। আপনার সার্ভিসটি প্রদান করবে:
-
GetFeatureনামক একটি RPC মেথড, যা সার্ভার ইমপ্লিমেন্ট করে এবং ক্লায়েন্ট কল করে। -
GetFeatureমেথড ব্যবহার করার সময় ক্লায়েন্ট এবং সার্ভারের মধ্যেPointএবংFeatureনামক ডেটা স্ট্রাকচার আদান-প্রদান করা হয়। ক্লায়েন্ট তারGetFeatureঅনুরোধে সার্ভারে একটিPointহিসেবে মানচিত্রের স্থানাঙ্ক প্রদান করে এবং সার্ভার সেই স্থানাঙ্কে অবস্থিত যেকোনো কিছুর বর্ণনা দিয়ে একটি সংশ্লিষ্টFeatureপাঠিয়ে উত্তর দেয়।
এই RPC মেথড এবং এর মেসেজ টাইপগুলো প্রদত্ত সোর্স কোডের proto/route_guide.proto ফাইলে সংজ্ঞায়িত করা থাকবে।
প্রোটোকল বাফারগুলো সাধারণত প্রোটোবাফ নামে পরিচিত। gRPC পরিভাষা সম্পর্কে আরও তথ্যের জন্য, gRPC-এর মূল ধারণা, স্থাপত্য এবং জীবনচক্র দেখুন।
পরিষেবা পদ্ধতি
চলুন প্রথমে আমাদের সার্ভিস মেথডগুলো সংজ্ঞায়িত করি এবং তারপর আমাদের মেসেজ টাইপ Point ও Feature সংজ্ঞায়িত করি। proto/routeguide.proto ফাইলটিতে RouteGuide নামের একটি service স্ট্রাকচার রয়েছে, যা অ্যাপ্লিকেশনটির সার্ভিস দ্বারা প্রদত্ত এক বা একাধিক মেথড সংজ্ঞায়িত করে।
RouteGuide ডেফিনিশনের ভিতরে GetFeature rpc মেথডটি যোগ করুন। আগেই যেমন ব্যাখ্যা করা হয়েছে, এই মেথডটি প্রদত্ত স্থানাঙ্কের সেট থেকে কোনো অবস্থানের নাম বা ঠিকানা খুঁজে বের করবে, তাই GetFeature এমনভাবে তৈরি করুন যেন এটি প্রদত্ত Point জন্য একটি Feature রিটার্ন করে।
service RouteGuide {
// Definition of the service goes here
// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}
}
এটি একটি ইউনারি আরপিসি মেথড: এটি একটি সাধারণ আরপিসি যেখানে ক্লায়েন্ট সার্ভারে একটি অনুরোধ পাঠায় এবং একটি লোকাল ফাংশন কলের মতোই প্রতিক্রিয়া ফিরে আসার জন্য অপেক্ষা করে।
বার্তার প্রকারভেদ
সোর্স কোডের proto/route_guide.proto ফাইলে, প্রথমে Point মেসেজ টাইপটি সংজ্ঞায়িত করুন। একটি Point মানচিত্রে একটি অক্ষাংশ-দ্রাঘিমাংশ স্থানাঙ্ক জোড়াকে বোঝায়। এই কোডল্যাবের জন্য, স্থানাঙ্ক হিসেবে পূর্ণসংখ্যা ব্যবহার করুন:
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
1 এবং 2 সংখ্যা দুটি হলো message কাঠামোর প্রতিটি ফিল্ডের অনন্য শনাক্তকরণ নম্বর।
এরপর, Feature মেসেজ টাইপটি সংজ্ঞায়িত করুন। একটি Feature , Point দ্বারা নির্দিষ্ট কোনো অবস্থানে থাকা কোনো কিছুর নাম বা ডাক ঠিকানার জন্য একটি string ফিল্ড ব্যবহার করে।
message Feature {
// The name or address of the feature.
string name = 1;
// The point where the feature is located.
Point location = 2;
}
৪. ক্লায়েন্ট এবং সার্ভার কোড তৈরি করুন
আমরা আপনাকে generated ডিরেক্টরিতে থাকা .proto ফাইল থেকে তৈরি করা কোডটি ইতিমধ্যেই দিয়ে দিয়েছি।
যেকোনো প্রজেক্টের মতোই, আমাদের কোডের জন্য প্রয়োজনীয় ডিপেন্ডেন্সিগুলো নিয়ে ভাবতে হবে। রাস্ট প্রজেক্টের ক্ষেত্রে, ডিপেন্ডেন্সিগুলো Cargo.toml ফাইলে থাকবে। আমরা ইতিমধ্যেই Cargo.toml ফাইলে প্রয়োজনীয় ডিপেন্ডেন্সিগুলোর তালিকা করে দিয়েছি।
আপনি যদি নিজে .proto ফাইল থেকে কোড তৈরি করার পদ্ধতি শিখতে চান, তাহলে এই নির্দেশাবলী দেখুন।
তৈরি করা কোডটিতে রয়েছে:
-
PointএবংFeatureমেসেজ টাইপের জন্য স্ট্রাক্ট সংজ্ঞা। - আমাদের একটি সার্ভিস ট্রেইট ইমপ্লিমেন্ট করতে হবে:
route_guide_server::RouteGuide। - সার্ভারকে কল করার জন্য আমরা যে ক্লায়েন্ট টাইপটি ব্যবহার করব তা হলো:
route_guide_client::RouteGuideClient<T>।
এরপরে, আমরা সার্ভার-সাইডে GetFeature মেথডটি ইমপ্লিমেন্ট করব, যাতে ক্লায়েন্ট যখন কোনো রিকোয়েস্ট পাঠাবে, সার্ভার তখন তার উত্তর দিতে পারে।
৫. পরিষেবাটি বাস্তবায়ন করুন
src/server/server.rs ফাইলে, আমরা gRPC-এর include_generated_proto! ম্যাক্রো ব্যবহার করে জেনারেট করা কোডকে স্কোপের মধ্যে আনতে পারি এবং RouteGuide ট্রেইট ও Point ইম্পোর্ট করতে পারি।
mod grpc_pb {
grpc::include_generated_proto!("generated", "routeguide");
}
pub 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 ট্রেইটটি ইমপ্লিমেন্ট করতে হবে।
একক আরপিসি
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()))
}
}
মেথডটির মধ্যে, প্রদত্ত Point জন্য উপযুক্ত তথ্য দিয়ে একটি Feature অবজেক্ট পূরণ করুন এবং তারপর সেটি রিটার্ন করুন।
এই মেথডটি ইমপ্লিমেন্ট করার পর, আমাদের একটি 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() ফাংশনে যা ঘটছে, তা ধাপে ধাপে নিচে দেওয়া হলো:
- ক্লায়েন্টের অনুরোধ শোনার জন্য আমরা যে পোর্টটি ব্যবহার করতে চাই তা নির্দিষ্ট করুন।
-
load()হেল্পার ফাংশনটি কল করে ফিচারগুলো লোড করে একটিRouteGuideServiceতৈরি করুন। - আমাদের তৈরি করা সার্ভিসটি ব্যবহার করে
RouteGuideServer::new()এর মাধ্যমে gRPC সার্ভারের একটি ইনস্ট্যান্স তৈরি করুন। - আমাদের পরিষেবা বাস্তবায়নটি gRPC সার্ভারে নিবন্ধন করুন।
- প্রসেসটি কিল না হওয়া পর্যন্ত ব্লকিং ওয়েট করার জন্য, আমাদের পোর্ট ডিটেইলস সহ সার্ভারে
serve()কল করুন।
৬. ক্লায়েন্ট তৈরি করুন
এই অংশে, আমরা src/client/client.rs এ থাকা আমাদের RouteGuide সার্ভিসের জন্য একটি রাস্ট ক্লায়েন্ট তৈরি করা দেখব।
যেমনটি আমরা src/server/server.rs এ করেছিলাম, তেমনি gRPC-এর include_generated_code! ম্যাক্রো ব্যবহার করে জেনারেট করা কোডকে স্কোপের মধ্যে আনতে পারি এবং RouteGuideClient টাইপটি ইম্পোর্ট করতে পারি।
mod grpc_pb {
grpc::include_generated_proto!("generated", "routeguide");
}
use grpc_pb::{
route_guide_client::RouteGuideClient,
Point,
};
কল পরিষেবা পদ্ধতি
gRPC-Rust-এ, RPC-গুলো ব্লকিং/সিঙ্ক্রোনাস মোডে কাজ করে, যার অর্থ হলো RPC কলটি সার্ভারের প্রতিক্রিয়ার জন্য অপেক্ষা করে এবং হয় একটি প্রতিক্রিয়া অথবা একটি ত্রুটি ফেরত দেয়।
সার্ভিস মেথড কল করার জন্য, আমাদের প্রথমে সার্ভারের সাথে যোগাযোগের জন্য একটি চ্যানেল তৈরি করতে হবে। এটি তৈরি করার জন্য, প্রথমে একটি এন্ডপয়েন্ট তৈরি করতে হবে, সেই এন্ডপয়েন্টে কানেক্ট করতে হবে এবং কানেক্ট করার সময় তৈরি হওয়া চ্যানেলটি RouteGuideClient::new() ফাংশনে নিম্নোক্তভাবে পাস করতে হবে:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create endpoint to connect to
let endpoint = Endpoint::new("http://[::1]:10000")?;
let channel = endpoint.connect().await?;
// Create a new client
let mut client = RouteGuideClient::new(channel);
Ok(())
}
এই ফাংশনে, ক্লায়েন্ট তৈরি করার সময়, আমরা উপরে তৈরি করা জেনেরিক চ্যানেলটিকে জেনারেটেড কোড স্টাব দিয়ে র্যাপ করি, যা .proto সার্ভিসে সংজ্ঞায়িত নির্দিষ্ট মেথডগুলো ইমপ্লিমেন্ট করে।
সরল আরপিসি
সাধারণ RPC GetFeature কল করা প্রায় একটি লোকাল মেথড কল করার মতোই সহজ। main() -এ এটি যোগ করুন।
println!("*** SIMPLE RPC ***");
let point = proto!(Point{
latitude: 409_146_138,
longitude: -746_188_906
});
let response = client
.get_feature(Request::new(point))
.await?.into_inner();
Ok(())
যেমনটা দেখতে পাচ্ছেন, আমরা আগে পাওয়া স্টাবটির উপর মেথডটি কল করছি। আমাদের মেথড প্যারামিটারগুলোতে আমরা একটি রিকোয়েস্ট প্রোটোকল বাফার অবজেক্ট (আমাদের ক্ষেত্রে Point ) তৈরি ও তাতে ডেটা যুক্ত করি। যদি কলটি কোনো এরর রিটার্ন না করে, তাহলে আমরা প্রথম রিটার্ন ভ্যালুটি থেকে সার্ভার থেকে রেসপন্স ইনফরমেশন পড়ে নিতে পারি।
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>> {
//Create endpoint to connect to
let endpoint = Endpoint::new("http://[::1]:10000")?;
let channel = endpoint.connect().await?;
// Create a new client
let mut client = RouteGuideClient::new(channel);
println!("*** SIMPLE RPC ***");
let point = proto!(Point{
latitude: 409_146_138,
longitude: -746_188_906
});
let response = client
.get_feature(Request::new(point))
.await?.into_inner();
println!("Response = Name = \"{}\", Latitude = {}, Longitude = {}",
response.name(),
response.location().latitude(),
response.location().longitude());
Ok(())
}
৭. এটি পরীক্ষা করে দেখুন
প্রথমে, আমাদের ক্লায়েন্ট এবং সার্ভার চালানোর জন্য, সেগুলোকে আমাদের ক্রেটে বাইনারি টার্গেট হিসেবে যুক্ত করতে হবে। সেই অনুযায়ী আমাদের Cargo.toml সম্পাদনা করতে হবে এবং নিম্নলিখিত বিষয়গুলো যোগ করতে হবে:
[[bin]]
name = "routeguide-server"
path = "src/server/server.rs"
[[bin]]
name = "routeguide-client"
path = "src/client/client.rs"
তারপর, আমাদের ওয়ার্কিং ডিরেক্টরি থেকে নিম্নলিখিত কমান্ডগুলো চালান:
- একটি টার্মিনালে সার্ভারটি চালান:
RUSTFLAGS="-Awarnings" cargo run --bin routeguide-server
- অন্য একটি টার্মিনাল থেকে ক্লায়েন্টটি চালান:
RUSTFLAGS="-Awarnings" cargo run --bin routeguide-client
আপনি এইরকম আউটপুট দেখতে পাবেন, স্পষ্টতার জন্য টাইমস্ট্যাম্পগুলো বাদ দেওয়া হয়েছে:
*** SIMPLE RPC *** FEATURE: Name = "Berkshire Valley Management Area Trail, Jefferson, NJ, USA", Lat = 409146138, Lon = -746188906
৮. এরপর কী?
- gRPC-এর পরিচিতি এবং মূল ধারণা অংশে gRPC কীভাবে কাজ করে তা জানুন।
- বেসিক টিউটোরিয়ালটি অনুসরণ করুন