gRPC-Python - স্ট্রিমিং দিয়ে শুরু করা

১. ভূমিকা

এই কোডল্যাবে, আপনি gRPC-Python ব্যবহার করে একটি ক্লায়েন্ট ও সার্ভার তৈরি করবেন, যা পাইথনে লেখা একটি রাউট-ম্যাপিং অ্যাপ্লিকেশনের ভিত্তি তৈরি করবে।

এই টিউটোরিয়ালটি শেষ করার পর, আপনি এমন একটি ক্লায়েন্ট তৈরি করতে পারবেন যা gRPC ব্যবহার করে একটি রিমোট সার্ভারের সাথে সংযোগ স্থাপন করে ক্লায়েন্টের রুটের ফিচারগুলো সম্পর্কে তথ্য সংগ্রহ করতে, ক্লায়েন্টের রুটের একটি সারাংশ তৈরি করতে এবং সার্ভার ও অন্যান্য ক্লায়েন্টদের সাথে ট্র্যাফিক আপডেটের মতো রুটের তথ্য আদান-প্রদান করতে পারবে।

সার্ভিসটি একটি প্রোটোকল বাফারস ফাইলে সংজ্ঞায়িত করা আছে, যা ক্লায়েন্ট এবং সার্ভারের জন্য বয়লারপ্লেট কোড তৈরি করতে ব্যবহৃত হবে, যাতে তারা একে অপরের সাথে যোগাযোগ করতে পারে। এর ফলে ঐ কার্যকারিতাটি বাস্তবায়নে আপনার সময় ও শ্রম বাঁচবে।

এই জেনারেট করা কোডটি শুধু সার্ভার ও ক্লায়েন্টের মধ্যকার যোগাযোগের জটিলতাই নয়, ডেটার সিরিয়ালাইজেশন এবং ডিসিরিয়ালাইজেশনও সামলে নেয়।

আপনি যা শিখবেন

  • সার্ভিস এপিআই সংজ্ঞায়িত করতে প্রোটোকল বাফার কীভাবে ব্যবহার করবেন
  • স্বয়ংক্রিয় কোড জেনারেশন ব্যবহার করে প্রোটোকল বাফারস ডেফিনিশন থেকে কীভাবে একটি gRPC-ভিত্তিক ক্লায়েন্ট এবং সার্ভার তৈরি করা যায়।
  • gRPC ব্যবহার করে ক্লায়েন্ট-সার্ভার স্ট্রিমিং যোগাযোগ সম্পর্কে ধারণা।

এই কোডল্যাবটি সেইসব পাইথন ডেভেলপারদের জন্য তৈরি করা হয়েছে যারা gRPC-তে নতুন অথবা এর বিষয়ে নিজেদের জ্ঞান ঝালিয়ে নিতে চান, কিংবা যারা ডিস্ট্রিবিউটেড সিস্টেম তৈরিতে আগ্রহী। এর জন্য gRPC-তে পূর্ব অভিজ্ঞতার প্রয়োজন নেই।

২. শুরু করার আগে

তোমার যা লাগবে

  • পাইথন ৩.৯ বা তার উচ্চতর সংস্করণ। আমরা পাইথন ৩.১৩ ব্যবহারের পরামর্শ দিই। প্ল্যাটফর্ম-ভিত্তিক ইনস্টলেশন নির্দেশাবলীর জন্য, ‘পাইথন সেটআপ এবং ব্যবহার’ দেখুন। বিকল্পভাবে, uv বা pyenv-এর মতো টুল ব্যবহার করে একটি নন-সিস্টেম পাইথন ইনস্টল করুন।
  • পাইথন প্যাকেজ ইনস্টল করতে pip ব্যবহার করা হয়।
  • পাইথন ভার্চুয়াল এনভায়রনমেন্ট তৈরি করার জন্য venv ব্যবহৃত হয়।

ensurepip এবং venv প্যাকেজগুলো পাইথন স্ট্যান্ডার্ড লাইব্রেরির অংশ এবং সাধারণত ডিফল্টরূপে উপলব্ধ থাকে।

তবে, কিছু ডেবিয়ান-ভিত্তিক ডিস্ট্রিবিউশন (উবুন্টু সহ) পাইথন পুনঃবিতরণের সময় এগুলিকে বাদ দিয়ে থাকে। প্যাকেজগুলি ইনস্টল করতে, চালান:

sudo apt install python3-pip python3-venv

কোডটি নিন

আপনার শেখার প্রক্রিয়াকে সহজ করতে, এই কোডল্যাবটি আপনাকে কাজ শুরু করতে সাহায্য করার জন্য একটি পূর্ব-নির্মিত সোর্স কোড কাঠামো প্রদান করে। নিম্নলিখিত ধাপগুলো আপনাকে অ্যাপ্লিকেশনটি সম্পূর্ণ করতে পথ দেখাবে, যার মধ্যে grpc_tools.protoc প্রোটোকল বাফার কম্পাইলার প্লাগইন ব্যবহার করে gRPC কোড জেনারেশনও অন্তর্ভুক্ত।

grpc-codelabs

এই কোডল্যাবের স্কাফোল্ড সোর্স কোডটি `codelabs/grpc-python-streaming/start_here` ডিরেক্টরিতে পাওয়া যাবে। আপনি যদি নিজে কোডটি ইমপ্লিমেন্ট করতে না চান, তবে সম্পূর্ণ সোর্স কোডটি ` completed ডিরেক্টরিতে পাওয়া যাবে।

প্রথমে, কোডল্যাব ওয়ার্কিং ডিরেক্টরি তৈরি করুন এবং তার ভেতরে যান:

mkdir grpc-python-streaming && cd grpc-python-streaming

কোডল্যাবটি ডাউনলোড এবং এক্সট্র্যাক্ট করুন:

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-python-streaming/start_here

বিকল্পভাবে, আপনি শুধু কোডল্যাব ডিরেক্টরি সম্বলিত .zip ফাইলটি ডাউনলোড করে ম্যানুয়ালি আনজিপ করতে পারেন।

৩. বার্তা এবং পরিষেবাগুলি সংজ্ঞায়িত করুন

আপনার প্রথম পদক্ষেপ হলো প্রোটোকল বাফার ব্যবহার করে অ্যাপ্লিকেশনটির gRPC সার্ভিস, এর RPC মেথড এবং এর রিকোয়েস্ট ও রেসপন্স মেসেজ টাইপগুলো সংজ্ঞায়িত করা। আপনার সার্ভিসটি প্রদান করবে:

  • ListFeatures , RecordRoute , এবং RouteChat নামক RPC মেথডগুলো, যেগুলো সার্ভার ইমপ্লিমেন্ট করে এবং ক্লায়েন্ট কল করে।
  • Point , Feature , Rectangle , RouteNote এবং RouteSummary হলো এমন কিছু ডেটা স্ট্রাকচার, যা RPC মেথড কল করার সময় ক্লায়েন্ট ও সার্ভারের মধ্যে আদান-প্রদান করা হয়।

এই RPC মেথডগুলো এবং এদের মেসেজ টাইপগুলো প্রদত্ত সোর্স কোডের protos/route_guide.proto ফাইলে সংজ্ঞায়িত করা থাকবে।

প্রোটোকল বাফারগুলো সাধারণত প্রোটোবাফ নামে পরিচিত। gRPC পরিভাষা সম্পর্কে আরও তথ্যের জন্য, gRPC-এর মূল ধারণা, স্থাপত্য এবং জীবনচক্র দেখুন।

বার্তার প্রকারগুলি সংজ্ঞায়িত করুন

সোর্স কোডের protos/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;
}

যাতে একটি এলাকার মধ্যে একাধিক পয়েন্ট কোনো ক্লায়েন্টের কাছে স্ট্রিম করা যায়, তার জন্য আপনার একটি Rectangle মেসেজের প্রয়োজন হবে যা একটি অক্ষাংশ-দ্রাঘিমাংশ আয়তক্ষেত্রকে প্রতিনিধিত্ব করে, যা lo এবং hi নামক দুটি তির্যকভাবে বিপরীত বিন্দু দ্বারা উপস্থাপিত হয়।

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

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

এছাড়াও, একটি RouteNote বার্তা যা একটি নির্দিষ্ট পয়েন্টে থাকাকালীন পাঠানো বার্তার প্রতিনিধিত্ব করে:

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

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

অবশেষে, আপনার একটি RouteSummary মেসেজের প্রয়োজন হবে। এই মেসেজটি একটি RecordRoute RPC-এর প্রতিক্রিয়ায় পাওয়া যায়, যা পরবর্তী বিভাগে ব্যাখ্যা করা হয়েছে। এতে প্রাপ্ত স্বতন্ত্র পয়েন্টের সংখ্যা, শনাক্তকৃত ফিচারের সংখ্যা এবং প্রতিটি পয়েন্টের মধ্যবর্তী দূরত্বের ক্রমসঞ্চয়ী যোগফল হিসেবে অতিক্রান্ত মোট দূরত্ব উল্লেখ থাকে।

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

পরিষেবা পদ্ধতিগুলি সংজ্ঞায়িত করুন

একটি সার্ভিস সংজ্ঞায়িত করতে, আপনাকে আপনার .proto ফাইলে একটি নামযুক্ত সার্ভিস উল্লেখ করতে হয়। route_guide.proto ফাইলটিতে RouteGuide নামের একটি service স্ট্রাকচার রয়েছে, যা অ্যাপ্লিকেশনটির সার্ভিস দ্বারা প্রদত্ত এক বা একাধিক মেথড সংজ্ঞায়িত করে।

আপনার সার্ভিস ডেফিনিশনের ভেতরে যখন আপনি RPC মেথডগুলো সংজ্ঞায়িত করেন, তখন সেগুলোর রিকোয়েস্ট এবং রেসপন্স টাইপ নির্দিষ্ট করে দেন। কোডল্যাবের এই অংশে, চলুন সংজ্ঞায়িত করা যাক:

তালিকা বৈশিষ্ট্য

প্রদত্ত Rectangle মধ্যে উপলব্ধ Feature অবজেক্টগুলো সংগ্রহ করে। যেহেতু আয়তক্ষেত্রটি একটি বড় এলাকা জুড়ে থাকতে পারে এবং এতে বিপুল সংখ্যক ফিচার থাকতে পারে, তাই ফলাফলগুলো একবারে ফেরত না দিয়ে ধারাবাহিকভাবে পাঠানো হয়।

এই অ্যাপ্লিকেশনের জন্য, আপনি একটি সার্ভার-সাইড স্ট্রিমিং RPC ব্যবহার করবেন: ক্লায়েন্ট সার্ভারে একটি অনুরোধ পাঠায় এবং ক্রমানুসারে বার্তা পড়ার জন্য একটি স্ট্রিম ফেরত পায়। ক্লায়েন্ট ফেরত আসা স্ট্রিমটি থেকে ততক্ষণ পর্যন্ত পড়তে থাকে যতক্ষণ না আর কোনো বার্তা অবশিষ্ট থাকে। আমাদের উদাহরণে যেমনটি দেখতে পাচ্ছেন, রেসপন্স টাইপের আগে 'stream' কীওয়ার্ডটি বসিয়ে একটি সার্ভার-সাইড স্ট্রিমিং পদ্ধতি নির্দিষ্ট করা হয়।

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

রেকর্ডরুট

ভ্রমণরত কোনো রুটের উপর থাকা পয়েন্টসমূহের একটি ধারা গ্রহণ করে এবং ভ্রমণ সম্পন্ন হলে একটি RouteSummary ফেরত দেয়।

এই ক্ষেত্রে একটি ক্লায়েন্ট-সাইড স্ট্রিমিং RPC উপযুক্ত: ক্লায়েন্ট একটি প্রদত্ত স্ট্রিম ব্যবহার করে ধারাবাহিকভাবে বার্তা লেখে এবং সার্ভারে পাঠায়। ক্লায়েন্টের বার্তা লেখা শেষ হয়ে গেলে, এটি সার্ভারের সব বার্তা পড়া এবং প্রতিক্রিয়া ফেরত পাঠানোর জন্য অপেক্ষা করে। রিকোয়েস্ট টাইপের আগে 'stream' কীওয়ার্ডটি বসিয়ে একটি ক্লায়েন্ট-সাইড স্ট্রিমিং পদ্ধতি নির্দিষ্ট করা হয়।

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

রুটচ্যাট

একটি রুট অতিক্রম করার সময় পাঠানো RouteNotes এর একটি ধারা গ্রহণ করে, এবং একই সাথে অন্যান্য RouteNotes (যেমন অন্য ব্যবহারকারীদের কাছ থেকে) গ্রহণ করে।

দ্বিমুখী স্ট্রিমিং-এর জন্য এটি একদম সঠিক একটি ব্যবহার। একটি দ্বিমুখী স্ট্রিমিং RPC-তে উভয় পক্ষই একটি রিড-রাইট স্ট্রিম ব্যবহার করে ধারাবাহিকভাবে বার্তা পাঠায়। দুটি স্ট্রিম স্বাধীনভাবে কাজ করে, তাই ক্লায়েন্ট এবং সার্ভার তাদের পছন্দমতো যেকোনো ক্রমে পড়তে ও লিখতে পারে: উদাহরণস্বরূপ, সার্ভার তার প্রতিক্রিয়া লেখার আগে ক্লায়েন্টের সমস্ত বার্তা পাওয়ার জন্য অপেক্ষা করতে পারে, অথবা এটি পর্যায়ক্রমে একটি বার্তা পড়ে তারপর একটি বার্তা লিখতে পারে, কিংবা পড়া ও লেখার অন্য কোনো সংমিশ্রণ ব্যবহার করতে পারে। প্রতিটি স্ট্রিমে বার্তাগুলোর ক্রম সংরক্ষিত থাকে। অনুরোধ এবং প্রতিক্রিয়া উভয়ের আগে 'stream' কীওয়ার্ডটি বসিয়ে এই ধরনের পদ্ধতি নির্দিষ্ট করা হয়।

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

৪. ক্লায়েন্ট এবং সার্ভার কোড তৈরি করুন

এরপরে, প্রোটোকল বাফার কম্পাইলার ব্যবহার করে .proto ফাইল থেকে ক্লায়েন্ট এবং সার্ভার উভয়ের জন্য বয়লারপ্লেট gRPC কোড তৈরি করুন।

gRPC পাইথন কোড তৈরির জন্য আমরা grpcio-tools তৈরি করেছি। এর মধ্যে রয়েছে:

  1. সাধারণ প্রোটোক কম্পাইলার যা message ডেফিনিশন থেকে পাইথন কোড তৈরি করে।
  2. gRPC প্রোটোবাফ প্লাগইন যা service ডেফিনিশনগুলো থেকে পাইথন কোড (ক্লায়েন্ট ও সার্ভার স্টাব) তৈরি করে।

আমরা pip ব্যবহার করে grpcio-tools পাইথন প্যাকেজটি ইনস্টল করব। আপনার প্রোজেক্টের ডিপেন্ডেন্সিগুলোকে সিস্টেম প্যাকেজ থেকে আলাদা রাখতে চলুন একটি নতুন পাইথন ভার্চুয়াল এনভায়রনমেন্ট (venv) তৈরি করি:

python3 -m venv --upgrade-deps .venv

bash/zsh শেলে ভার্চুয়াল এনভায়রনমেন্ট সক্রিয় করতে:

source .venv/bin/activate

উইন্ডোজ এবং অ-প্রমিত শেলগুলির জন্য, https://docs.python.org/3/library/venv.html#how-venvs-work -এ দেওয়া সারণিটি দেখুন।

এরপর, grpcio-tools ইনস্টল করুন (এর মাধ্যমে grpcio প্যাকেজটিও ইনস্টল হয়ে যাবে):

pip install grpcio-tools

পাইথন বয়লারপ্লেট কোড তৈরি করতে নিম্নলিখিত কমান্ডটি ব্যবহার করুন:

python -m grpc_tools.protoc --proto_path=./protos  \
 --python_out=. --pyi_out=. --grpc_python_out=. \
 ./protos/route_guide.proto

এটি route_guide.proto তে আমাদের সংজ্ঞায়িত ইন্টারফেসগুলির জন্য নিম্নলিখিত ফাইলগুলি তৈরি করবে:

  1. route_guide_pb2.py ফাইলে সেই কোড রয়েছে যা message ডেফিনিশন থেকে তৈরি ক্লাসগুলোকে ডাইনামিকভাবে তৈরি করে
  2. route_guide_pb2.pyi হলো message ডেফিনিশন থেকে তৈরি একটি 'স্টাব ফাইল' বা 'টাইপ হিন্ট ফাইল'। এতে কোনো ইমপ্লিমেন্টেশন ছাড়াই শুধুমাত্র সিগনেচারগুলো থাকে। IDE-গুলো উন্নততর অটোকমপ্লিশন এবং ত্রুটি শনাক্তকরণের জন্য স্টাব ফাইল ব্যবহার করতে পারে।
  3. route_guide_pb2_grpc.py service ডেফিনিশন থেকে তৈরি করা হয় এবং এতে gRPC-এর জন্য নির্দিষ্ট ক্লাস ও ফাংশন রয়েছে।

gRPC-নির্দিষ্ট কোডে রয়েছে:

  1. RouteGuideStub , যা একটি gRPC ক্লায়েন্ট RouteGuide RPC আহ্বান করার জন্য ব্যবহার করতে পারে।
  2. RouteGuideServicer , যা RouteGuide সার্ভিসের ইমপ্লিমেন্টেশনগুলোর জন্য ইন্টারফেস সংজ্ঞায়িত করে।
  3. add_RouteGuideServicer_to_server ফাংশনটি একটি RouteGuideServicer gRPC সার্ভারে রেজিস্টার করতে ব্যবহৃত হয়।

৫. সার্ভার তৈরি করুন

প্রথমে দেখা যাক কিভাবে একটি RouteGuide সার্ভার তৈরি করতে হয়। একটি RouteGuide সার্ভার তৈরি এবং চালানোর কাজটি দুটি ভাগে বিভক্ত:

  • আমাদের সার্ভিস ডেফিনিশন থেকে তৈরি হওয়া সার্ভিসার ইন্টারফেসটি এমন ফাংশন দিয়ে ইমপ্লিমেন্ট করা, যা সার্ভিসের আসল "কাজ" সম্পাদন করে।
  • ক্লায়েন্টদের কাছ থেকে অনুরোধ শোনার এবং প্রতিক্রিয়া পাঠানোর জন্য একটি gRPC সার্ভার চালানো হচ্ছে।

চলুন route_guide_server.py ফাইলটি দেখি।

রুটগাইড বাস্তবায়ন করুন

route_guide_server.py একটি RouteGuideServicer ক্লাস আছে যা জেনারেট করা ক্লাস route_guide_pb2_grpc.RouteGuideServicer সাবক্লাস:

# RouteGuideServicer provides an implementation of the methods of the RouteGuide service.
class RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer):

RouteGuideServicer RouteGuide সার্ভিসের সমস্ত মেথড বাস্তবায়ন করে।

সার্ভার-সাইড স্ট্রিমিং RPC

ListFeatures হলো একটি রেসপন্স-স্ট্রিমিং RPC যা ক্লায়েন্টের কাছে একাধিক Feature পাঠায়:

def ListFeatures(self, request, context):
    """List all features contained within the given Rectangle."""
    left = min(request.lo.longitude, request.hi.longitude)
    right = max(request.lo.longitude, request.hi.longitude)
    top = max(request.lo.latitude, request.hi.latitude)
    bottom = min(request.lo.latitude, request.hi.latitude)
    for feature in self.db:
        lat, lng = feature.location.latitude, feature.location.longitude
        if left <= lng <= right and bottom <= lat <= top:
            yield feature

এখানে অনুরোধ বার্তাটি হলো একটি route_guide_pb2.Rectangle যার মধ্যে ক্লায়েন্ট Feature গুলো খুঁজে পেতে চায়। এই মেথডটি একটিমাত্র প্রতিক্রিয়া ফেরত দেওয়ার পরিবর্তে শূন্য বা তার বেশি প্রতিক্রিয়া প্রদান করে।

ক্লায়েন্ট-সাইড স্ট্রিমিং আরপিসি

রিকোয়েস্ট-স্ট্রিমিং মেথড RecordRoute রিকোয়েস্ট ভ্যালুগুলোর একটি ইটারেটর ব্যবহার করে এবং একটিমাত্র রেসপন্স ভ্যালু রিটার্ন করে।

def RecordRoute(self, request_iterator, context):
    """Calculate statistics about the trip composed of Points."""
    point_count = 0
    feature_count = 0
    distance = 0.0
    prev_point = None

    start_time = time.time()
    for point in request_iterator:
        point_count += 1
        if get_feature(self.db, point):
            feature_count += 1
        if prev_point:
            distance += get_distance(prev_point, point)
        prev_point = point

    elapsed_time = time.time() - start_time
    return route_guide_pb2.RouteSummary(
        point_count=point_count,
        feature_count=feature_count,
        distance=int(distance),
        elapsed_time=int(elapsed_time),
    )

দ্বিমুখী স্ট্রিমিং আরপিসি

অবশেষে, চলুন আমাদের দ্বিমুখী স্ট্রিমিং RPC RouteChat() দেখি:

def RouteChat(self, request_iterator, context):
    """
    Receive a stream of message/location pairs, and responds with
    a stream of all previous messages for the given location.
    """
    prev_notes = []
    for new_note in request_iterator:
        for prev_note in prev_notes:
            if prev_note.location == new_note.location:
                yield prev_note
        prev_notes.append(new_note)

এই মেথডটির কার্যপ্রণালী রিকোয়েস্ট-স্ট্রিমিং মেথড এবং রেসপন্স-স্ট্রিমিং মেথডের কার্যপ্রণালীর একটি সংমিশ্রণ। এটিকে রিকোয়েস্ট ভ্যালুগুলোর একটি ইটারেটর পাস করা হয় এবং এটি নিজেও রেসপন্স ভ্যালুগুলোর একটি ইটারেটর।

সার্ভার চালু করুন

একবার আপনি সমস্ত RouteGuide মেথড প্রয়োগ করে ফেললে, পরবর্তী ধাপ হলো একটি gRPC সার্ভার চালু করা, যাতে ক্লায়েন্টরা আপনার পরিষেবাটি ব্যবহার করতে পারে:

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    route_guide_pb2_grpc.add_RouteGuideServicer_to_server(
        RouteGuideServicer(),
        server,
    )
    listen_addr = "localhost:50051"
    server.add_insecure_port(listen_addr)
    print(f"Starting server on {listen_addr}")
    server.start()
    server.wait_for_termination()

`server start() ` মেথডটি নন-ব্লকিং। অনুরোধগুলো পরিচালনা করার জন্য একটি নতুন থ্রেড তৈরি করা হবে। যে থ্রেডটি server.start() কল করে, এই অন্তর্বর্তী সময়ে তার প্রায়শই অন্য কোনো কাজ থাকে না। এই ক্ষেত্রে, সার্ভারটি বন্ধ না হওয়া পর্যন্ত কলিং থ্রেডটিকে সুষ্ঠুভাবে ব্লক করার জন্য আপনি server.wait_for_termination() কল করতে পারেন।

৬. ক্লায়েন্ট তৈরি করুন

চলুন route_guide_client.py ফাইলটি দেখি।

একটি স্টাব তৈরি করুন

সার্ভিস মেথড কল করার জন্য, আমাদের প্রথমে একটি স্টাব তৈরি করতে হবে।

আমরা আমাদের .proto. থেকে তৈরি করা route_guide_pb2_grpc মডিউলের RouteGuideStub ক্লাসটিকে ইনস্ট্যানশিয়েট করি। run() মেথডে:

with grpc.insecure_channel("localhost:50051") as channel:
    stub = route_guide_pb2_grpc.RouteGuideStub(channel)

উল্লেখ্য যে, এখানে channel একটি কনটেক্সট ম্যানেজার হিসেবে ব্যবহৃত হয়েছে এবং ইন্টারপ্রেটার with ব্লকটি ছেড়ে গেলেই এটি স্বয়ংক্রিয়ভাবে বন্ধ হয়ে যাবে।

কল পরিষেবা পদ্ধতি

যেসব RPC মেথড একটিমাত্র রেসপন্স রিটার্ন করে ("রেসপন্স-ইউনারি" মেথড), সেগুলোর জন্য gRPC পাইথন সিনক্রোনাস (ব্লকিং) এবং অ্যাসিনক্রোনাস (নন-ব্লকিং) উভয় প্রকার কন্ট্রোল ফ্লো সেম্যান্টিকস সমর্থন করে। রেসপন্স-স্ট্রিমিং RPC মেথডের ক্ষেত্রে, কল করার সাথে সাথেই রেসপন্স ভ্যালুগুলোর একটি ইটারেটর রিটার্ন করা হয়। সেই ইটারেটরের next() মেথডে করা কলগুলো ততক্ষণ পর্যন্ত ব্লক হয়ে থাকে, যতক্ষণ না ইটারেটর থেকে ইয়েল্ড করার মতো রেসপন্সটি উপলব্ধ হয়।

সার্ভার-সাইড স্ট্রিমিং RPC

রেসপন্স-স্ট্রিমিং ListFeatures কল করা সিকোয়েন্স টাইপের সাথে কাজ করার মতোই:

def guide_list_features(stub):
    _lo = route_guide_pb2.Point(latitude=400000000, longitude=-750000000)
    _hi = route_guide_pb2.Point(latitude=420000000, longitude=-730000000)
    rectangle = route_guide_pb2.Rectangle(
        lo=_lo,
        hi=_hi,
    )
    print("Looking for features between 40, -75 and 42, -73")

    features = stub.ListFeatures(rectangle)
    for feature in features:
        print(
            f"Feature called '{feature.name}'"
            f" at {format_point(feature.location)}"
        )

ক্লায়েন্ট-সাইড স্ট্রিমিং আরপিসি

রিকোয়েস্ট-স্ট্রিমিং RecordRoute কল করা অনেকটা কোনো লোকাল মেথডে ইটারেটর পাস করার মতো। উপরের সাধারণ RPC-টির মতো, যা একটিমাত্র রেসপন্স রিটার্ন করে, এটিকেও সিনক্রোনাসলি কল করা যায়:

def guide_record_route(stub):
    feature_list = route_guide_resources.read_route_guide_database()
    route_iterator = generate_route(feature_list)

    route_summary = stub.RecordRoute(route_iterator)
    print(f"Finished trip with {route_summary.point_count} points")
    print(f"Passed {route_summary.feature_count} features")
    print(f"Traveled {route_summary.distance} meters")
    print(f"It took {route_summary.elapsed_time} seconds")

দ্বিমুখী স্ট্রিমিং আরপিসি

দ্বিমুখী-স্ট্রিমিং RouteChat কল করার ক্ষেত্রে (সার্ভিস-সাইডের মতোই) রিকোয়েস্ট-স্ট্রিমিং এবং রেসপন্স-স্ট্রিমিং সেম্যান্টিক্সের একটি সংমিশ্রণ থাকে।

অনুরোধ বার্তাগুলো তৈরি করুন এবং yield ব্যবহার করে এক এক করে পাঠান।

def generate_notes():
    home = route_guide_pb2.Point(latitude=1, longitude=1)
    work = route_guide_pb2.Point(latitude=2, longitude=2)
    notes = [
        make_route_note("Departing from home", home),
        make_route_note("Arrived at work", work),
        make_route_note("Having lunch at work", work),
        make_route_note("Departing from work", work),
        make_route_note("Arrived home", home),
    ]
    for note in notes:
        print(
            f"Sending RouteNote for {format_point(note.location)}:"
            f" {note.message}"
        )
        yield note
        # Sleep to simulate moving from one point to another.
        # Only for demonstrating the order of the messages.
        time.sleep(0.1)

সার্ভারের প্রতিক্রিয়া গ্রহণ ও প্রক্রিয়া করুন:

def guide_route_chat(stub):
    responses = stub.RouteChat(generate_notes())
    for response in responses:
        print(
            "< Found previous note at"
            f" {format_point(response.location)}: {response.message}"
        )

সহায়ক পদ্ধতিগুলো কল করুন

রান করার সময়, আমরা এইমাত্র যে মেথডগুলো তৈরি করেছি সেগুলো এক্সিকিউট করুন এবং সেগুলোতে stub পাস করুন।

print("-------------- ListFeatures --------------")
guide_list_features(stub)
print("-------------- RecordRoute --------------")
guide_record_route(stub)
print("-------------- RouteChat --------------")
guide_route_chat(stub)

৭. এটি পরীক্ষা করে দেখুন

সার্ভারটি চালান:

python route_guide_server.py

অন্য একটি টার্মিনাল থেকে ভার্চুয়াল এনভায়রনমেন্টটি আবার সক্রিয় করুন ( source .venv/bin/activate) , তারপর ক্লায়েন্টটি চালান:

python route_guide_client.py

চলুন আউটপুটটি দেখে নেওয়া যাক।

তালিকা বৈশিষ্ট্য

প্রথমে, আপনি ফিচারগুলোর তালিকা দেখতে পাবেন। সার্ভার যখনই অনুরোধ করা আয়তক্ষেত্রের মধ্যে কোনো ফিচার খুঁজে পায়, তখনই তা সার্ভার থেকে স্ট্রিম করা হয় ( সার্ভার-সাইড স্ট্রিমিং আরপিসি )।

-------------- ListFeatures --------------
Looking for features between 40, -75 and 42, -73
Feature called 'Patriots Path, Mendham, NJ 07945, USA' at (lat=407838351, lng=-746143763)
Feature called '101 New Jersey 10, Whippany, NJ 07981, USA' at (lat=408122808, lng=-743999179)
Feature called 'U.S. 6, Shohola, PA 18458, USA' at (lat=413628156, lng=-749015468)
Feature called '5 Conners Road, Kingston, NY 12401, USA' at (lat=419999544, lng=-740371136)
...

রেকর্ডরুট

দ্বিতীয়ত, RecordRoute ক্লায়েন্ট থেকে সার্ভারে এলোমেলোভাবে পরিদর্শন করা পয়েন্টগুলির তালিকা স্ট্রিম করা প্রদর্শন করে (ক্লায়েন্ট-সাইড স্ট্রিমিং RPC) :

-------------- RecordRoute --------------
Visiting point (lat=410395868, lng=-744972325)
Visiting point (lat=404310607, lng=-740282632)
Visiting point (lat=403966326, lng=-748519297)
Visiting point (lat=407586880, lng=-741670168)
Visiting point (lat=406589790, lng=-743560121)
Visiting point (lat=410322033, lng=-747871659)
Visiting point (lat=415464475, lng=-747175374)
Visiting point (lat=407586880, lng=-741670168)
Visiting point (lat=402647019, lng=-747071791)
Visiting point (lat=414638017, lng=-745957854)

ক্লায়েন্ট তার পরিদর্শন করা সমস্ত পয়েন্ট স্ট্রিমিং শেষ করার পর, সার্ভারের কাছ থেকে একটি নন-স্ট্রিমিং প্রতিক্রিয়া ( একটি ইউনারি আরপিসি ) পাবে। এই প্রতিক্রিয়াটিতে ক্লায়েন্টের সম্পূর্ণ রুটের উপর সম্পাদিত গণনাগুলির একটি সারসংক্ষেপ থাকবে।

Finished trip with 10 points
Passed 10 features
Traveled 654743 meters
It took 0 seconds

রুটচ্যাট

সবশেষে, RouteChat আউটপুট দ্বিমুখী স্ট্রিমিং প্রদর্শন করে। যখন ক্লায়েন্ট home বা work পয়েন্ট "ভিজিট" করে, তখন এটি সার্ভারে একটি RouteNote পাঠিয়ে সেই পয়েন্টের জন্য একটি নোট রেকর্ড করে। যখন কোনো পয়েন্ট ইতিমধ্যেই ভিজিট করা হয়ে যায়, তখন সার্ভার সেই পয়েন্টের জন্য পূর্ববর্তী সমস্ত নোট স্ট্রিম করে ফেরত পাঠায়।

-------------- RouteChat --------------
Sending RouteNote for (lat=1, lng=1): Departing from home
Sending RouteNote for (lat=2, lng=2): Arrived at work
Sending RouteNote for (lat=2, lng=2): Having lunch at work
< Found previous note at (lat=2, lng=2): Arrived at work
Sending RouteNote for (lat=2, lng=2): Departing from work
< Found previous note at (lat=2, lng=2): Arrived at work
< Found previous note at (lat=2, lng=2): Having lunch at work
Sending RouteNote for (lat=1, lng=1): Arrived home
< Found previous note at (lat=1, lng=1): Departing from home

৮. এরপর কী?