เริ่มต้นใช้งาน 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-getting-started/start_here หากไม่ต้องการติดตั้งใช้งานโค้ดด้วยตนเอง คุณจะดูซอร์สโค้ดที่เสร็จสมบูรณ์ได้ในไดเรกทอรี completed

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

mkdir grpc-python-getting-started && cd grpc-python-getting-started

ดาวน์โหลดและแตกไฟล์ 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-getting-started/start_here

หรือคุณจะดาวน์โหลดไฟล์ .zip ที่มีเฉพาะไดเรกทอรี Codelab แล้วแตกไฟล์ด้วยตนเองก็ได้

3. กำหนดบริการ

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

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

วิธีการ 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;
}

วิธีการบริการ

ไฟล์ route_guide.proto มีโครงสร้าง service ชื่อ RouteGuide ซึ่งกำหนดวิธีการอย่างน้อย 1 วิธีที่บริการของแอปพลิเคชันมีให้

เพิ่มrpc method 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 โดยใช้คอมไพเลอร์ 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 ที่พอร์ตที่เฉพาะเจาะจงเพื่อรอรับคำขอจากไคลเอ็นต์และส่งการตอบกลับ

คุณดูเซิร์ฟเวอร์ 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

สร้าง Stub

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

เราสร้างอินสแตนซ์ของคลาส RouteGuideStub ของโมดูล route_guide_pb2_grpc ซึ่งสร้างจาก .proto ภายในไฟล์ route_guide_client.py

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

วิธีการใช้บริการโทร

สำหรับเมธอด RPC ที่ส่งคืนการตอบกลับรายการเดียว ซึ่งเรียกว่าเมธอด response-unary นั้น 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)

คุณสามารถตรวจสอบฟิลด์ของออบเจ็กต์ฟีเจอร์และแสดงผลลัพธ์ของคำขอได้โดยทำดังนี้

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. ขั้นตอนถัดไป