شروع با gRPC-Python - استریم

۱. مقدمه

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

در پایان آموزش، شما یک کلاینت خواهید داشت که با استفاده از gRPC به یک سرور راه دور متصل می‌شود تا اطلاعاتی در مورد ویژگی‌های مسیر کلاینت دریافت کند، خلاصه‌ای از مسیر کلاینت ایجاد کند و اطلاعات مسیر مانند به‌روزرسانی‌های ترافیک را با سرور و سایر کلاینت‌ها تبادل کند.

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

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

آنچه یاد خواهید گرفت

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

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

۲. قبل از شروع

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

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

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

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

sudo apt install python3-pip python3-venv

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

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

grpc-codelabs

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

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

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 که فقط شامل دایرکتوری codelab است را دانلود کرده و به صورت دستی آن را از حالت فشرده خارج کنید.

۳. تعریف پیام‌ها و خدمات

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

  • متدهای RPC به نام‌های ListFeatures ، RecordRoute و RouteChat که سرور پیاده‌سازی می‌کند و کلاینت آنها را فراخوانی می‌کند.
  • انواع پیام‌ها Point ، Feature ، Rectangle ، RouteNote و RouteSummary هستند که ساختارهای داده‌ای هستند که هنگام فراخوانی متدهای RPC بین کلاینت و سرور رد و بدل می‌شوند.

این متدهای RPC و انواع پیام‌های آنها، همگی در فایل protos/route_guide.proto از کد منبع ارائه شده تعریف خواهند شد.

بافرهای پروتکل معمولاً به عنوان protobufs شناخته می‌شوند. برای اطلاعات بیشتر در مورد اصطلاحات 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;
}

برای اینکه چندین نقطه در یک منطقه بتوانند به یک کلاینت ارسال شوند، به یک پیام 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 دارای یک ساختار service به نام RouteGuide است که یک یا چند متد ارائه شده توسط سرویس برنامه را تعریف می‌کند.

وقتی متدهای RPC را در تعریف سرویس خود تعریف می‌کنید، نوع درخواست و پاسخ آنها را مشخص می‌کنید. در این بخش از codelab، بیایید موارد زیر را تعریف کنیم:

ویژگی‌ها

اشیاء Feature موجود در Rectangle داده شده را دریافت می‌کند. نتایج به جای اینکه به طور همزمان بازگردانده شوند، به صورت جریانی ارسال می‌شوند، زیرا مستطیل ممکن است ناحیه بزرگی را پوشش دهد و شامل تعداد زیادی ویژگی باشد.

برای این برنامه، شما از یک 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) {}

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

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

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

  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 یک "فایل stub" یا "فایل type hint" است که از تعاریف message تولید شده است. این فایل فقط شامل امضاها بدون هیچ پیاده‌سازی است. فایل‌های stub می‌توانند توسط IDEها برای ارائه تکمیل خودکار بهتر و تشخیص خطا استفاده شوند.
  3. route_guide_pb2_grpc.py از تعاریف service تولید می‌شود و شامل کلاس‌ها و توابع مخصوص gRPC است.

کد مخصوص gRPC شامل موارد زیر است:

  1. RouteGuideStub ، که می‌تواند توسط یک کلاینت gRPC برای فراخوانی RPCهای RouteGuide استفاده شود.
  2. RouteGuideServicer که رابط کاربری پیاده‌سازی‌های سرویس RouteGuide را تعریف می‌کند.
  3. تابع add_RouteGuideServicer_to_server که برای ثبت یک RouteGuideServicer در یک سرور gRPC استفاده می‌شود.

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

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

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

بیایید نگاهی به 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 استریمینگ سمت سرور

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 ها را در آن پیدا کند. به جای برگرداندن یک پاسخ واحد، این متد صفر یا چند پاسخ می‌دهد.

RPC استریمینگ سمت کلاینت

متد RecordRoute که برای ارسال درخواست استفاده می‌شود، از یک تکرارکننده (iterator) از مقادیر درخواست استفاده می‌کند و یک مقدار پاسخ واحد را برمی‌گرداند.

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 استریمینگ دوطرفه

در نهایت، بیایید نگاهی به استریمینگ دوطرفه 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)

معنای این متد ترکیبی از روش request-streaming و روش response-streaming است. یک iterator از مقادیر درخواست به آن ارسال می‌شود و خود نیز یک iterator از مقادیر پاسخ است.

سرور را شروع کنید

پس از پیاده‌سازی تمام متدهای 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() را فراخوانی کنید تا نخ فراخوانی‌کننده را تا زمان خاتمه سرور مسدود کنید.

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

بیایید نگاهی به route_guide_client.py بیندازیم.

ایجاد یک مقاله خرد

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

ما کلاس RouteGuideStub از ماژول route_guide_pb2_grpc را که از .proto. در متد run() :

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

توجه داشته باشید که در اینجا channel به عنوان مدیر زمینه استفاده می‌شود و به محض اینکه مفسر از بلوک with خارج شود، به طور خودکار بسته خواهد شد.

روش‌های سرویس تماس

برای متدهای RPC که یک پاسخ واحد برمی‌گردانند (متدهای "response-unary")، 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)}"
        )

RPC استریمینگ سمت کلاینت

فراخوانی RecordRoute به صورت request-streaming اجرا می‌شود، مشابه ارسال یک iterator به یک متد محلی است. مانند 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")

RPC استریمینگ دوطرفه

فراخوانی 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}"
        )

فراخوانی متدهای کمکی

در run، متدهایی که ایجاد کردیم را اجرا کنید و 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

بیایید نگاهی به خروجی بیندازیم.

ویژگی‌ها

ابتدا، لیست ویژگی‌ها را خواهید یافت. هر ویژگی از سرور ( RPC استریمینگ سمت سرور ) پخش می‌شود، زیرا سرور متوجه می‌شود که آنها در مستطیل درخواستی قرار دارند:

-------------- 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)

پس از اینکه کلاینت استریمینگ تمام نقاط بازدید شده را به پایان رساند، یک پاسخ غیر استریمینگ ( یک RPC یونیاری ) از سرور دریافت خواهد کرد. این پاسخ شامل خلاصه‌ای از محاسبات انجام شده روی کل مسیر کلاینت خواهد بود.

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

۸. قدم بعدی چیست؟