شروع کار با gRPC-Python

1. مقدمه

در این کد لبه، شما از gRPC-Python برای ایجاد یک کلاینت و سرور استفاده خواهید کرد که پایه و اساس برنامه نقشه برداری مسیر نوشته شده در پایتون را تشکیل می دهد.

در پایان آموزش، یک کلاینت خواهید داشت که با استفاده از gRPC به یک سرور راه دور متصل می شود تا نام یا آدرس پستی آنچه در مختصات خاصی روی نقشه قرار دارد را دریافت کند. یک برنامه کاربردی کاملاً پیشرفته ممکن است از این طراحی سرویس گیرنده-سرور برای برشمردن یا خلاصه کردن نقاط مورد علاقه در طول مسیر استفاده کند.

این سرویس در یک فایل Protocol Buffers تعریف شده است که از آن برای تولید کد boilerplate برای کلاینت و سرور استفاده می شود تا بتوانند با یکدیگر ارتباط برقرار کنند و در زمان و تلاش شما در اجرای آن عملکرد صرفه جویی شود.

این کد تولید شده نه تنها از پیچیدگی های ارتباط بین سرور و کلاینت، بلکه سریال سازی و سریال سازی داده ها نیز مراقبت می کند.

چیزی که یاد خواهید گرفت

  • نحوه استفاده از بافرهای پروتکل برای تعریف API سرویس.
  • نحوه ساخت یک کلاینت و سرور مبتنی بر gRPC از تعریف بافرهای پروتکل با استفاده از تولید کد خودکار.
  • درک ارتباط مشتری-سرور با gRPC.

این کد لبه برای توسعه دهندگان Python که تازه به gRPC می پردازند یا به دنبال یک تجدید کننده gRPC هستند یا هر فرد دیگری که علاقه مند به ساختن سیستم های توزیع شده است، طراحی شده است. هیچ تجربه قبلی gRPC مورد نیاز نیست.

2. قبل از شروع

آنچه شما نیاز دارید

  • پایتون 3.9 یا بالاتر. ما پایتون 3.13 را توصیه می کنیم. برای دستورالعمل‌های نصب پلتفرم خاص، تنظیمات و استفاده پایتون را ببینید. روش دیگر، نصب پایتون غیر سیستمی با استفاده از ابزارهایی مانند uv یا pyenv .
  • pip برای نصب بسته های پایتون.
  • venv برای ایجاد محیط های مجازی پایتون.

بسته های ensurepip و venv بخشی از کتابخانه استاندارد پایتون هستند و معمولاً به طور پیش فرض در دسترس هستند.

با این حال، برخی از توزیع‌های مبتنی بر دبیان (از جمله اوبونتو) هنگام توزیع مجدد پایتون، آنها را حذف می‌کنند. برای نصب بسته ها، اجرا کنید:

sudo apt install python3-pip python3-venv

کد را دریافت کنید

برای ساده‌سازی یادگیری شما، این Codelab یک داربست کد منبع از پیش ساخته شده برای کمک به شما برای شروع ارائه می‌کند. مراحل زیر شما را در تکمیل برنامه، از جمله تولید کد gRPC با استفاده از افزونه کامپایلر grpc_tools.protoc Protocol Buffer راهنمایی می کند.

grpc-codelabs

کد منبع داربست برای این کد لبه در دایرکتوری codelabs/grpc-python-getting-started/start_here موجود است. اگر ترجیح می دهید خودتان کد را پیاده سازی نکنید، کد منبع تکمیل شده در فهرست completed موجود است.

ابتدا پوشه کاری codelab و cd را در آن ایجاد کنید:

mkdir grpc-python-getting-started && cd grpc-python-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-python-getting-started/start_here

یا می توانید فایل .zip را که فقط شامل دایرکتوری Codelab است دانلود کرده و به صورت دستی آن را از حالت فشرده خارج کنید.

3. سرویس را تعریف کنید

اولین قدم شما این است که سرویس gRPC برنامه، روش RPC آن، و انواع پیام درخواست و پاسخ آن را با استفاده از زبان تعریف رابط پروتکل بافر تعریف کنید. خدمات شما ارائه خواهد کرد:

  • یک روش RPC به نام GetFeature که سرور پیاده سازی می کند و کلاینت فراخوانی می کند.
  • نوع پیام Point و Feature ساختارهای داده ای هستند که هنگام استفاده از روش GetFeature بین مشتری و سرور رد و بدل می شوند. مشتری مختصات نقشه را به عنوان یک Point در درخواست GetFeature خود به سرور ارائه می دهد و سرور با یک Feature مربوطه پاسخ می دهد که هر چیزی را که در آن مختصات قرار دارد را توصیف می کند.

این روش 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 از یک فیلد string برای نام یا آدرس پستی چیزی در مکانی مشخص شده توسط یک Point استفاده می کند:

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

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

روش سرویس دهی

فایل route_guide.proto دارای یک ساختار service به نام RouteGuide است که یک یا چند روش ارائه شده توسط سرویس برنامه را تعریف می کند.

روش 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 یکنواخت است: یک RPC ساده که در آن کلاینت درخواستی را به سرور ارسال می کند و منتظر می ماند تا یک پاسخ بازگردد، درست مانند یک فراخوانی تابع محلی.

4. کد کلاینت و سرور را تولید کنید

سپس، کد gRPC را برای مشتری و سرور از فایل .proto با استفاده از کامپایلر بافر پروتکل تولید کنید.

برای تولید کد gRPC پایتون، ابزارهای grpcio را ایجاد کردیم. شامل:

  1. کامپایلر پروتک معمولی که کد پایتون را از تعاریف message تولید می کند.
  2. پلاگین gRPC protobuf که کد پایتون (خرد مشتری و سرور) را از تعاریف service تولید می کند.

ما بسته پایتون grpcio-tools با استفاده از pip نصب می کنیم. بیایید یک محیط مجازی پایتون جدید (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 ایجاد شده است. فقط شامل امضاهایی است که هیچ اجرائی ندارند. فایل‌های Stub را می‌توان توسط 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 استفاده می شود.

5. سرویس را ایجاد کنید

ابتدا اجازه دهید نحوه ایجاد سرور RouteGuide را بررسی کنیم. ایجاد و اجرای سرور RouteGuide به دو مورد تقسیم می شود:

  • پیاده سازی رابط سرویس دهنده تولید شده از تعریف سرویس ما با توابعی که "کار" واقعی سرویس را انجام می دهند.
  • اجرای یک سرور gRPC در یک پورت خاص برای گوش دادن به درخواست های مشتریان و ارسال پاسخ ها.

می‌توانید سرور RouteGuide اولیه را در start_here/route_guide_server.py پیدا کنید.

RouteGuide را پیاده سازی کنید

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 را با جزئیات بررسی کنیم. متد GetFeature یک Point از مشتری دریافت می کند و اطلاعات ویژگی مربوطه را از پایگاه داده خود در Feature برمی گرداند.

def GetFeature(self, request, context):
    feature = get_feature(self.db, request)
    if feature is None:
        return route_guide_pb2.Feature(name="", location=request)
    else:
        return feature

این روش یک درخواست route_guide_pb2.Point برای RPC، و یک شی grpc.ServicerContext ارسال می‌شود که اطلاعات خاص RPC مانند محدودیت‌های مهلت زمانی را ارائه می‌دهد. پاسخ route_guide_pb2.Feature را برمی گرداند.

راه اندازی سرور

هنگامی که تمام روش‌های 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()

متد start() سرور غیر مسدود کننده است. یک رشته جدید برای رسیدگی به درخواست ها ایجاد می شود. رشته فراخوانی server.start() اغلب در این مدت کار دیگری برای انجام ندارد. در این مورد، می‌توانید server.wait_for_termination() را فراخوانی کنید تا تا زمانی که سرور خاتمه یابد، رشته فراخوانی را به طور تمیز مسدود کند.

6. مشتری ایجاد کنید

در این بخش، ایجاد یک کلاینت برای سرویس RouteGuide خود را بررسی خواهیم کرد. می‌توانید کد مشتری اولیه را در start_here/route_guide_client.py ببینید.

یک خرد ایجاد کنید

برای فراخوانی روش های سرویس، ابتدا باید یک خرد ایجاد کنیم.

ما کلاس RouteGuideStub ماژول route_guide_pb2_grpc را که از .proto ما در داخل فایل route_guide_client.py تولید شده است، نمونه سازی می کنیم.

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

روش های خدمات تماس

برای روش‌های RPC که یک پاسخ واحد را برمی‌گردانند – معروف به روش‌های پاسخ-یونری – gRPC Python از معناشناسی جریان کنترل همزمان (مسدودکننده) و ناهمزمان (غیر مسدودکننده) پشتیبانی می‌کند.

RPC ساده

ابتدا بیایید یک Point برای فراخوانی سرویس تعریف کنیم. این باید به سادگی نمونه‌برداری از یک شی از بسته route_guide_pb2 با برخی ویژگی‌ها باشد:

point = route_guide_pb2.Point(latitude=412346009, longitude=-744026814)

فراخوانی همزمان با RPC GetFeature ساده تقریباً به اندازه فراخوانی یک متد محلی است. فراخوانی RPC منتظر می ماند تا سرور پاسخ دهد و یا پاسخی را برمی گرداند یا استثنایی ایجاد می کند. می توانیم متد را فراخوانی کنیم و پاسخ را به صورت زیر ببینیم:

feature = stub.GetFeature(point)
print(feature)

می‌توانید فیلدهای شی Feature را بررسی کنید و نتیجه درخواست را به دست آورید:

if feature.name:
    print(f"Feature called '{feature.name}' at {format_point(feature.location)}")
else:
    print(f"Found no feature at at {format_point(feature.location)}")

7. آن را امتحان کنید

سرور را اجرا کنید:

python route_guide_server.py

از یک ترمینال دیگر، دوباره محیط مجازی را فعال کنید، سپس کلاینت را اجرا کنید:

python route_guide_client.py

خروجی را به صورت زیر مشاهده خواهید کرد که برای وضوح از مهرهای زمانی حذف شده است:

name: "16 Old Brook Lane, Warwick, NY 10990, USA"
location {
  latitude: 412346009
  longitude: -744026814
}

Feature called '16 Old Brook Lane, Warwick, NY 10990, USA' at latitude: 412346009, longitude: -744026814

8. بعدی چه خواهد شد