เริ่มต้นใช้งาน gRPC-Python - การสตรีม

1. บทนำ

ในโค้ดแล็บนี้ คุณจะได้ใช้ gRPC-Python เพื่อสร้างไคลเอ็นต์และเซิร์ฟเวอร์ซึ่งเป็นรากฐานของแอปพลิเคชันการแมปเส้นทางที่เขียนด้วย Python

เมื่อจบบทแนะนำนี้ คุณจะมีไคลเอ็นต์ที่เชื่อมต่อกับเซิร์ฟเวอร์ระยะไกลโดยใช้ gRPC เพื่อรับข้อมูลเกี่ยวกับฟีเจอร์ในเส้นทางของไคลเอ็นต์ สร้างข้อมูลสรุปของเส้นทางของไคลเอ็นต์ และแลกเปลี่ยนข้อมูลเส้นทาง เช่น ข้อมูลอัปเดตการจราจร กับเซิร์ฟเวอร์และไคลเอ็นต์อื่นๆ

บริการนี้กำหนดไว้ในไฟล์ Protocol Buffers ซึ่งจะใช้เพื่อสร้างโค้ดมาตรฐานสำหรับไคลเอ็นต์และเซิร์ฟเวอร์เพื่อให้สื่อสารกันได้ ซึ่งจะช่วยประหยัดเวลาและแรงในการติดตั้งใช้งานฟังก์ชันดังกล่าว

โค้ดที่สร้างขึ้นนี้ไม่เพียงแต่จัดการความซับซ้อนของการสื่อสารระหว่างเซิร์ฟเวอร์และไคลเอ็นต์เท่านั้น แต่ยังจัดการการซีเรียลไลซ์และการดีซีเรียลไลซ์ข้อมูลด้วย

สิ่งที่คุณจะได้เรียนรู้

  • วิธีใช้ Protocol Buffers เพื่อกำหนด API ของบริการ
  • วิธีสร้างไคลเอ็นต์และเซิร์ฟเวอร์ที่ใช้ gRPC จากคำจำกัดความของ Protocol Buffers โดยใช้การสร้างโค้ดอัตโนมัติ
  • ความเข้าใจเกี่ยวกับการสื่อสารแบบสตรีมมิงไคลเอ็นต์-เซิร์ฟเวอร์ด้วย gRPC

Codelab นี้มีไว้สำหรับนักพัฒนา Python ที่เพิ่งเริ่มใช้ gRPC หรือต้องการทบทวน gRPC หรือผู้ที่สนใจสร้างระบบแบบกระจาย ไม่จำเป็นต้องมีประสบการณ์เกี่ยวกับ gRPC มาก่อน

2. ก่อนเริ่มต้น

สิ่งที่คุณต้องมี

  • Python 3.9 ขึ้นไป เราขอแนะนำให้ใช้ Python 3.13 ดูวิธีการติดตั้งเฉพาะแพลตฟอร์มได้ที่การตั้งค่าและการใช้งาน Python หรือจะติดตั้ง Python ที่ไม่ใช่ระบบโดยใช้เครื่องมืออย่าง uv หรือ pyenv ก็ได้
  • pip เพื่อติดตั้งแพ็กเกจ Python
  • venv เพื่อสร้างสภาพแวดล้อมเสมือนของ Python

แพ็กเกจ ensurepip และ venv เป็นส่วนหนึ่งของไลบรารีมาตรฐานของ Python และมักจะพร้อมใช้งานโดยค่าเริ่มต้น

อย่างไรก็ตาม การกระจายที่อิงตาม Debian บางรายการ (รวมถึง Ubuntu) เลือกที่จะยกเว้นเมื่อแจกจ่าย Python อีกครั้ง หากต้องการติดตั้งแพ็กเกจ ให้เรียกใช้คำสั่งต่อไปนี้

sudo apt install python3-pip python3-venv

รับโค้ด

Codelab นี้มีโครงร่างซอร์สโค้ดที่สร้างไว้ล่วงหน้าเพื่อช่วยให้คุณเริ่มต้นใช้งานได้ง่ายขึ้น ขั้นตอนต่อไปนี้จะแนะนําวิธีสมัครให้เสร็จสมบูรณ์ รวมถึงการสร้างโค้ด gRPC โดยใช้ปลั๊กอินคอมไพเลอร์ grpc_tools.protoc Protocol Buffer

grpc-codelabs

ซอร์สโค้ดโครงร่างสำหรับ Codelab นี้อยู่ในไดเรกทอรี codelabs/grpc-python-streaming/start_here หากไม่ต้องการติดตั้งใช้งานโค้ดด้วยตนเอง คุณจะดูซอร์สโค้ดที่เสร็จสมบูรณ์ได้ในไดเรกทอรี completed

ก่อนอื่น ให้สร้างไดเรกทอรีการทำงานของ Codelab แล้วใช้คำสั่ง cd เพื่อเข้าไปในไดเรกทอรีดังกล่าว

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

ดาวน์โหลดและแตกไฟล์ Codelab โดยทำดังนี้

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 แล้วแตกไฟล์ด้วยตนเองก็ได้

3. กำหนดข้อความและบริการ

ขั้นตอนแรกคือการกำหนดบริการ gRPC ของแอปพลิเคชัน เมธอด RPC รวมถึงประเภทข้อความคำขอและการตอบกลับโดยใช้ Protocol Buffers บริการของคุณจะให้ข้อมูลต่อไปนี้

  • เมธอด RPC ที่เรียกว่า ListFeatures, RecordRoute และ RouteChat ซึ่งเซิร์ฟเวอร์ใช้และไคลเอ็นต์เรียก
  • ประเภทข้อความ Point, Feature, Rectangle, RouteNote และ RouteSummary ซึ่งเป็นโครงสร้างข้อมูลที่แลกเปลี่ยนระหว่างไคลเอ็นต์และเซิร์ฟเวอร์เมื่อเรียกใช้เมธอด RPC

วิธีการ RPC และประเภทข้อความเหล่านี้จะกำหนดไว้ในไฟล์ protos/route_guide.proto ของซอร์สโค้ดที่ให้ไว้

Protocol Buffers เรียกกันโดยทั่วไปว่า protobuf ดูข้อมูลเพิ่มเติมเกี่ยวกับคำศัพท์ gRPC ได้ที่แนวคิดหลัก สถาปัตยกรรม และวงจรของ gRPC

กำหนดประเภทข้อความ

ในprotos/route_guide.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;
}

หากต้องการสตรีมจุดหลายจุดภายในพื้นที่ไปยังไคลเอ็นต์ คุณจะต้องมีRectangleข้อความที่แสดงสี่เหลี่ยมผืนผ้าละติจูด-ลองจิจูด ซึ่งแสดงเป็น 2 จุดที่อยู่ตรงข้ามกันในแนวทแยง 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ข้อความ คุณจะได้รับข้อความนี้เป็นการตอบกลับ RPC ของ RecordRoute ซึ่งจะอธิบายในส่วนถัดไป โดยจะมีจำนวนจุดแต่ละจุดที่ได้รับ จำนวนฟีเจอร์ที่ตรวจพบ และระยะทางทั้งหมดที่ครอบคลุมเป็นผลรวมสะสมของระยะทางระหว่างแต่ละจุด

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 ซึ่งกำหนดวิธีการอย่างน้อย 1 วิธีที่บริการของแอปพลิเคชันมีให้

เมื่อกำหนดRPCเมธอดภายในคำจำกัดความของบริการ คุณจะระบุประเภทคำขอและการตอบกลับของเมธอดเหล่านั้น ในส่วนนี้ของโค้ดแล็บ เราจะกำหนดค่าต่อไปนี้

ListFeatures

รับออบเจ็กต์ Feature ที่มีอยู่ภายใน Rectangle ที่ระบุ ระบบจะสตรีมผลการค้นหาแทนที่จะแสดงผลทั้งหมดในครั้งเดียว เนื่องจากสี่เหลี่ยมผืนผ้าอาจครอบคลุมพื้นที่ขนาดใหญ่และมีฟีเจอร์จำนวนมาก

สำหรับแอปพลิเคชันนี้ คุณจะใช้ RPC แบบสตรีมมิงฝั่งเซิร์ฟเวอร์ ซึ่งไคลเอ็นต์จะส่งคำขอไปยังเซิร์ฟเวอร์และรับสตรีมเพื่ออ่านลำดับข้อความกลับ ไคลเอ็นต์จะอ่านจากสตรีมที่ส่งคืนจนกว่าจะไม่มีข้อความเหลือ ดังที่เห็นในตัวอย่าง คุณระบุวิธีการสตรีมฝั่งเซิร์ฟเวอร์ได้โดยวางคีย์เวิร์ดสตรีมไว้ก่อนประเภทการตอบกลับ

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

RecordRoute

ยอมรับสตรีมของจุดในเส้นทางที่กำลังเดินทาง และส่งคืน RouteSummary เมื่อการเดินทางเสร็จสมบูรณ์

ในกรณีนี้ RPC การสตรีมฝั่งไคลเอ็นต์จะเหมาะสม โดยไคลเอ็นต์จะเขียนลำดับข้อความและส่งไปยังเซิร์ฟเวอร์อีกครั้งโดยใช้สตรีมที่ระบุ เมื่อไคลเอ็นต์เขียนข้อความเสร็จแล้ว ไคลเอ็นต์จะรอให้เซิร์ฟเวอร์อ่านข้อความทั้งหมดและส่งการตอบกลับ คุณระบุวิธีการสตรีมฝั่งไคลเอ็นต์ได้โดยวางคีย์เวิร์ดสตรีมไว้ก่อนประเภทคำขอ

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

RouteChat

ยอมรับสตรีมของ RouteNotes ที่ส่งขณะที่กำลังเดินทางตามเส้นทาง ขณะเดียวกันก็รับ RouteNotes อื่นๆ (เช่น จากผู้ใช้รายอื่น)

นี่คือกรณีการใช้งานที่เหมาะกับการสตรีมแบบ 2 ทาง RPC แบบสตรีมมิงแบบ 2 ทางซึ่งทั้ง 2 ฝ่ายจะส่งลำดับข้อความโดยใช้สตรีมแบบอ่าน-เขียน สตรีมทั้ง 2 รายการทำงานแยกกัน ดังนั้นไคลเอ็นต์และเซิร์ฟเวอร์จึงอ่านและเขียนได้ตามลำดับที่ต้องการ เช่น เซิร์ฟเวอร์อาจรอรับข้อความทั้งหมดจากไคลเอ็นต์ก่อนที่จะเขียนการตอบกลับ หรืออาจอ่านข้อความแล้วเขียนข้อความสลับกัน หรืออาจใช้การอ่านและการเขียนแบบอื่นๆ ระบบจะรักษลําดับของข้อความในแต่ละสตรีมไว้ คุณระบุประเภทเมธอดนี้ได้โดยวางคีย์เวิร์ดสตรีมไว้ก่อนทั้งคำขอและการตอบกลับ

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

4. สร้างโค้ดไคลเอ็นต์และเซิร์ฟเวอร์

จากนั้นสร้างโค้ด gRPC มาตรฐานสำหรับทั้งไคลเอ็นต์และเซิร์ฟเวอร์จากไฟล์ .proto โดยใช้คอมไพเลอร์ Protocol Buffer

เราได้สร้าง grpcio-tools สำหรับการสร้างโค้ด gRPC Python แอปประกอบด้วย

  1. คอมไพเลอร์ protoc ปกติที่สร้างโค้ด Python จากคำจำกัดความ message
  2. ปลั๊กอิน Protobuf ของ gRPC ที่สร้างโค้ด Python (ต้นขั้วไคลเอ็นต์และเซิร์ฟเวอร์) จากservice

เราจะติดตั้งgrpcio-toolsแพ็กเกจ Python โดยใช้ pip มาสร้างสภาพแวดล้อมเสมือนของ Python (venv) ใหม่เพื่อแยกการอ้างอิงของโปรเจ็กต์ออกจากแพ็กเกจของระบบกัน

python3 -m venv --upgrade-deps .venv

วิธีเปิดใช้งานสภาพแวดล้อมเสมือนใน Bash/Zsh Shell

source .venv/bin/activate

สำหรับ Windows และเชลล์ที่ไม่ใช่แบบมาตรฐาน โปรดดูตารางที่ https://docs.python.org/3/library/venv.html#how-venvs-work

จากนั้นติดตั้ง grpcio-tools (ซึ่งจะติดตั้งแพ็กเกจ grpcio ด้วย)

pip install grpcio-tools

ใช้คำสั่งต่อไปนี้เพื่อสร้างโค้ดบอยเลอร์เพลต Python

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" หรือ "ไฟล์คำแนะนำประเภท" ที่สร้างจากคำจำกัดความของ message โดยจะมีเฉพาะลายเซ็นที่ไม่มีการติดตั้งใช้งาน IDE สามารถใช้ไฟล์ Stub เพื่อให้การเติมข้อความอัตโนมัติและการตรวจหาข้อผิดพลาดดียิ่งขึ้น
  3. route_guide_pb2_grpc.py สร้างขึ้นจากคำจำกัดความของ service และมีคลาสและฟังก์ชันเฉพาะของ gRPC

โค้ดเฉพาะ gRPC มีดังนี้

  1. RouteGuideStub ซึ่งไคลเอ็นต์ gRPC สามารถใช้เพื่อเรียกใช้ RPC ของ RouteGuide ได้
  2. RouteGuideServicer ซึ่งกำหนดอินเทอร์เฟซสำหรับการใช้งานบริการ RouteGuide
  3. add_RouteGuideServicer_to_server ฟังก์ชันที่ใช้เพื่อลงทะเบียน RouteGuideServicer กับ เซิร์ฟเวอร์ gRPC

5. สร้างเซิร์ฟเวอร์

ก่อนอื่นมาดูวิธีสร้างRouteGuideเซิร์ฟเวอร์กัน การสร้างและเรียกใช้RouteGuideเซิร์ฟเวอร์แบ่งออกเป็น 2 รายการงาน ดังนี้

  • การติดตั้งใช้งานอินเทอร์เฟซผู้ให้บริการที่สร้างขึ้นจากคำจำกัดความของบริการของเราด้วยฟังก์ชันที่ทำ "งาน" จริงของบริการ
  • การเรียกใช้เซิร์ฟเวอร์ 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 เมธอดนี้จะให้คำตอบ 0 รายการขึ้นไปแทนที่จะให้คำตอบรายการเดียว

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 การสตรีมแบบ 2 ทิศทาง

สุดท้าย มาดู RPC แบบสตรีมแบบ 2 ทาง 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()

เมธอดเซิร์ฟเวอร์ start() จะไม่บล็อก ระบบจะสร้างเธรดใหม่เพื่อจัดการคำขอ โดยปกติแล้ว เธรดที่เรียก server.start() จะไม่มีงานอื่นใดให้ทำในระหว่างนี้ ในกรณีนี้ คุณสามารถเรียกใช้ server.wait_for_termination() เพื่อบล็อกเธรดการโทรอย่างสมบูรณ์จนกว่าเซิร์ฟเวอร์จะสิ้นสุด

6. สร้างไคลเอ็นต์

มาดูที่ route_guide_client.py กัน

สร้าง Stub

หากต้องการเรียกใช้เมธอดของบริการ เราต้องสร้างสตับก่อน

เราสร้างอินสแตนซ์ของRouteGuideStubคลาสของโมดูล route_guide_pb2_grpc ที่สร้างขึ้นจากเมธอด .proto. In run() ดังนี้

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

โปรดทราบว่าในที่นี้ channel ใช้เป็นตัวจัดการบริบท และจะปิดโดยอัตโนมัติเมื่ออินเทอร์พรีเตอร์ออกจากบล็อก with

วิธีการเรียกใช้บริการ

สำหรับเมธอด RPC ที่ส่งคืนการตอบกลับรายการเดียว (เมธอด "response-unary") gRPC Python รองรับทั้งซีแมนทิกส์ของโฟลว์การควบคุมแบบซิงโครนัส (การบล็อก) และแบบไม่พร้อมกัน (การไม่บล็อก) สำหรับเมธอด 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 จะคล้ายกับการส่งผ่านตัววนซ้ำไปยังเมธอดในเครื่อง เช่นเดียวกับ 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 การสตรีมแบบ 2 ทิศทาง

การเรียกใช้ 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)

7. ลองเลย

เรียกใช้เซิร์ฟเวอร์

python route_guide_server.py

จากเทอร์มินัลอื่น ให้เปิดใช้งานสภาพแวดล้อมเสมือนอีกครั้ง (source .venv/bin/activate)) แล้วเรียกใช้ไคลเอ็นต์โดยใช้คำสั่งต่อไปนี้

python route_guide_client.py

มาดูเอาต์พุตกัน

ListFeatures

ก่อนอื่น คุณจะเห็นรายการฟีเจอร์ ระบบจะสตรีมแต่ละฟีเจอร์จากเซิร์ฟเวอร์ (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

ประการที่ 2 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

สุดท้าย RouteChat เอาต์พุตแสดงให้เห็นการสตรีมแบบ 2 ทาง เมื่อไคลเอ็นต์ "เยี่ยมชม" จุด 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

8. ขั้นตอนถัดไป