1. Pengantar
Dalam codelab ini, Anda akan menggunakan gRPC-Python untuk membuat klien dan server yang membentuk dasar aplikasi pemetaan rute yang ditulis dalam Python.
Di akhir tutorial, Anda akan memiliki klien yang terhubung ke server jarak jauh menggunakan gRPC untuk mendapatkan informasi tentang fitur di rute klien, membuat ringkasan rute klien, dan bertukar informasi rute seperti info terbaru lalu lintas dengan server dan klien lain.
Layanan ini ditentukan dalam file Protocol Buffers, yang akan digunakan untuk membuat kode boilerplate bagi klien dan server sehingga keduanya dapat berkomunikasi satu sama lain, sehingga menghemat waktu dan upaya Anda dalam menerapkan fungsi tersebut.
Kode yang dihasilkan ini tidak hanya menangani kompleksitas komunikasi antara server dan klien, tetapi juga serialisasi dan deserialisasi data.
Yang akan Anda pelajari
- Cara menggunakan Protocol Buffers untuk menentukan API layanan.
- Cara membangun klien dan server berbasis gRPC dari definisi Protocol Buffers menggunakan pembuatan kode otomatis.
- Pemahaman tentang komunikasi streaming klien-server dengan gRPC.
Codelab ini ditujukan bagi developer Python yang baru menggunakan gRPC atau ingin mempelajari kembali gRPC, atau siapa pun yang tertarik untuk membangun sistem terdistribusi. Tidak diperlukan pengalaman gRPC sebelumnya.
2. Sebelum memulai
Yang Anda butuhkan
- Python 3.9 atau yang lebih tinggi. Sebaiknya gunakan Python 3.13. Untuk mengetahui petunjuk penginstalan khusus platform, lihat Penyiapan dan Penggunaan Python. Atau, instal Python non-sistem menggunakan alat seperti uv atau pyenv.
- pip untuk menginstal paket Python.
- venv untuk membuat lingkungan virtual Python.
Paket ensurepip
dan venv
adalah bagian dari Python Standard Library dan biasanya tersedia secara default.
Namun, beberapa distribusi berbasis Debian (termasuk Ubuntu) memilih untuk mengecualikannya saat mendistribusikan ulang Python. Untuk menginstal paket, jalankan:
sudo apt install python3-pip python3-venv
Mendapatkan kode
Untuk menyederhanakan pembelajaran Anda, codelab ini menawarkan struktur kode sumber bawaan untuk membantu Anda memulai. Langkah-langkah berikut akan memandu Anda menyelesaikan aplikasi, termasuk pembuatan kode gRPC menggunakan plugin compiler Protocol Buffer grpc_tools.protoc
.
grpc-codelabs
Kode sumber scaffold untuk codelab ini tersedia di direktori codelabs/grpc-python-streaming/start_here. Jika Anda memilih untuk tidak menerapkan kode sendiri, kode sumber yang sudah selesai tersedia di direktori completed
.
Pertama, buat direktori kerja codelab dan cd ke dalamnya:
mkdir grpc-python-streaming && cd grpc-python-streaming
Download dan ekstrak 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
Atau, Anda dapat mendownload file .zip yang hanya berisi direktori codelab dan mengekstraknya secara manual.
3. Menentukan pesan dan layanan
Langkah pertama Anda adalah menentukan layanan gRPC aplikasi, metode RPC, serta jenis pesan permintaan dan responsnya menggunakan Protocol Buffers. Layanan Anda akan menyediakan:
- Metode RPC yang disebut
ListFeatures
,RecordRoute
, danRouteChat
yang diimplementasikan server dan dipanggil klien. - Jenis pesan
Point
,Feature
,Rectangle
,RouteNote
, danRouteSummary
, yang merupakan struktur data yang dipertukarkan antara klien dan server saat memanggil metode RPC.
Metode RPC ini dan jenis pesannya akan ditentukan dalam file protos/route_guide.proto
kode sumber yang diberikan.
Protocol Buffers biasanya dikenal sebagai protobuf. Untuk mengetahui informasi selengkapnya tentang terminologi gRPC, lihat Konsep inti, arsitektur, dan siklus proses gRPC.
Menentukan jenis pesan
Dalam file protos/route_guide.proto
kode sumber, tentukan terlebih dahulu jenis pesan Point
. Point
mewakili pasangan koordinat lintang-bujur di peta. Untuk codelab ini, gunakan bilangan bulat untuk koordinat:
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
Angka 1
dan 2
adalah nomor ID unik untuk setiap kolom dalam struktur message
.
Selanjutnya, tentukan jenis pesan Feature
. Feature
menggunakan kolom string
untuk nama atau alamat pos sesuatu di lokasi yang ditentukan oleh Point
:
message Feature {
// The name or address of the feature.
string name = 1;
// The point where the feature is located.
Point location = 2;
}
Agar beberapa titik dalam suatu area dapat di-streaming ke klien, Anda memerlukan pesan Rectangle
yang merepresentasikan persegi panjang lintang-bujur, yang direpresentasikan sebagai dua titik yang berlawanan secara diagonal lo
dan hi
:
message Rectangle {
// One corner of the rectangle.
Point lo = 1;
// The other corner of the rectangle.
Point hi = 2;
}
Selain itu, pesan RouteNote
yang mewakili pesan yang dikirim saat berada di titik tertentu:
message RouteNote {
// The location from which the message is sent.
Point location = 1;
// The message to be sent.
string message = 2;
}
Terakhir, Anda memerlukan pesan RouteSummary
. Pesan ini diterima sebagai respons terhadap RPC RecordRoute
, yang dijelaskan di bagian berikutnya. Objek ini berisi jumlah titik individual yang diterima, jumlah fitur yang terdeteksi, dan total jarak yang ditempuh sebagai jumlah kumulatif jarak antara setiap titik.
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;
}
Menentukan metode layanan
Untuk menentukan layanan, Anda menentukan layanan bernama dalam file .proto
. File route_guide.proto
memiliki struktur service
bernama RouteGuide
yang menentukan satu atau beberapa metode yang disediakan oleh layanan aplikasi.
Saat menentukan metode RPC
di dalam definisi layanan, Anda menentukan jenis permintaan dan responsnya. Di bagian codelab ini, mari kita tentukan:
ListFeatures
Mendapatkan objek Feature
yang tersedia dalam Rectangle
tertentu. Hasil di-streaming, bukan ditampilkan sekaligus karena persegi panjang dapat mencakup area yang luas dan berisi sejumlah besar fitur.
Untuk aplikasi ini, Anda akan menggunakan RPC streaming sisi server: klien mengirimkan permintaan ke server dan mendapatkan stream untuk membaca kembali urutan pesan. Klien membaca dari stream yang ditampilkan hingga tidak ada lagi pesan. Seperti yang dapat Anda lihat dalam contoh kami, Anda menentukan metode streaming sisi server dengan menempatkan kata kunci stream sebelum jenis respons.
rpc ListFeatures(Rectangle) returns (stream Feature) {}
RecordRoute
Menerima aliran Poin pada rute yang dilalui, menampilkan RouteSummary
saat penelusuran selesai.
RPC streaming sisi klien cocok dalam kasus ini: klien menulis urutan pesan dan mengirimkannya ke server, lagi-lagi menggunakan stream yang disediakan. Setelah klien selesai menulis pesan, klien akan menunggu server membaca semua pesan tersebut dan menampilkan responsnya. Anda menentukan metode streaming sisi klien dengan menempatkan kata kunci stream sebelum jenis permintaan.
rpc RecordRoute(stream Point) returns (RouteSummary) {}
RouteChat
Menerima aliran RouteNotes
yang dikirim saat rute sedang dilalui, sambil menerima RouteNotes
lain (misalnya dari pengguna lain).
Inilah kasus penggunaan yang tepat untuk streaming dua arah. RPC streaming dua arah di mana kedua sisi mengirimkan urutan pesan menggunakan stream baca-tulis. Kedua aliran beroperasi secara independen, sehingga klien dan server dapat membaca dan menulis dalam urutan apa pun yang mereka inginkan: misalnya, server dapat menunggu untuk menerima semua pesan klien sebelum menulis responsnya, atau dapat membaca pesan lalu menulis pesan, atau kombinasi pembacaan dan penulisan lainnya. Urutan pesan di setiap aliran akan tetap terjaga. Anda menentukan jenis metode ini dengan menempatkan kata kunci stream sebelum permintaan dan respons.
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
4. Buat kode klien dan server
Selanjutnya, buat kode gRPC boilerplate untuk klien dan server dari file .proto
menggunakan compiler protocol buffer.
Untuk pembuatan kode gRPC Python, kami membuat grpcio-tools. Ini mencakup:
- Compiler protoc reguler yang menghasilkan kode Python dari definisi
message
. - Plugin protobuf gRPC yang menghasilkan kode Python (stub klien dan server) dari definisi
service
.
Kita akan menginstal paket Python grpcio-tools
menggunakan pip. Mari buat lingkungan virtual python (venv) baru untuk mengisolasi dependensi project Anda dari paket sistem:
python3 -m venv --upgrade-deps .venv
Untuk mengaktifkan lingkungan virtual di shell bash/zsh:
source .venv/bin/activate
Untuk Windows dan shell non-standar, lihat tabel di https://docs.python.org/3/library/venv.html#how-venvs-work.
Selanjutnya, instal grpcio-tools (tindakan ini juga akan menginstal paket grpcio):
pip install grpcio-tools
Gunakan perintah berikut untuk membuat kode boilerplate Python:
python -m grpc_tools.protoc --proto_path=./protos \
--python_out=. --pyi_out=. --grpc_python_out=. \
./protos/route_guide.proto
Tindakan ini akan menghasilkan file berikut untuk antarmuka yang kita tentukan di route_guide.proto
:
route_guide_pb2.py
berisi kode yang membuat class secara dinamis yang dihasilkan dari definisimessage
.route_guide_pb2.pyi
adalah "file stub" atau "file petunjuk jenis" yang dihasilkan dari definisimessage
. Hanya berisi tanda tangan tanpa penerapan. File stub dapat digunakan oleh IDE untuk memberikan pelengkapan otomatis dan deteksi error yang lebih baik.route_guide_pb2_grpc.py
dihasilkan dari definisiservice
dan berisi class serta fungsi khusus gRPC.
Kode khusus gRPC berisi:
RouteGuideStub
, yang dapat digunakan oleh klien gRPC untuk memanggil RPC RouteGuide.RouteGuideServicer
, yang menentukan antarmuka untuk penerapan layananRouteGuide
.- Fungsi
add_RouteGuideServicer_to_server
yang digunakan untuk mendaftarkanRouteGuideServicer
ke server gRPC.
5. Buat server
Pertama, mari kita lihat cara membuat server RouteGuide
. Membuat dan menjalankan server RouteGuide
dibagi menjadi dua item pekerjaan:
- Mengimplementasikan antarmuka layanan yang dihasilkan dari definisi layanan dengan fungsi yang melakukan "tugas" layanan yang sebenarnya.
- Menjalankan server gRPC untuk memproses permintaan dari klien dan mengirimkan respons.
Mari kita lihat route_guide_server.py
.
Mengimplementasikan RouteGuide
route_guide_server.py
memiliki class RouteGuideServicer
yang merupakan subclass dari class route_guide_pb2_grpc.RouteGuideServicer
yang dihasilkan:
# RouteGuideServicer provides an implementation of the methods of the RouteGuide service.
class RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer):
RouteGuideServicer
mengimplementasikan semua metode layanan RouteGuide
.
RPC streaming sisi server
ListFeatures
adalah RPC streaming respons yang mengirim beberapa Feature
ke klien:
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
Di sini, pesan permintaan adalah route_guide_pb2.Rectangle
yang di dalamnya klien ingin menemukan Feature
. Daripada menampilkan satu respons, metode ini menghasilkan nol atau lebih respons.
RPC streaming sisi klien
Metode streaming permintaan RecordRoute
menggunakan iterator nilai permintaan dan menampilkan satu nilai respons.
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 streaming dua arah
Terakhir, mari kita lihat RPC streaming dua arah 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)
Semantik metode ini adalah kombinasi dari semantik metode streaming permintaan dan metode streaming respons. Objek ini meneruskan iterator nilai permintaan dan merupakan iterator nilai respons.
Mulai server
Setelah Anda menerapkan semua metode RouteGuide
, langkah berikutnya adalah memulai server gRPC agar klien dapat benar-benar menggunakan layanan Anda:
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()
Metode start()
server tidak memblokir. Thread baru akan di-instansiasi untuk menangani permintaan. Thread yang memanggil server.start()
sering kali tidak memiliki tugas lain yang harus dilakukan pada saat yang sama. Dalam hal ini, Anda dapat memanggil server.wait_for_termination()
untuk memblokir thread panggilan secara bersih hingga server berhenti.
6. Buat klien
Mari kita lihat route_guide_client.py
.
Membuat stub
Untuk memanggil metode layanan, kita harus membuat stub terlebih dahulu.
Kita membuat instance class RouteGuideStub
dari modul route_guide_pb2_grpc
, yang dihasilkan dari metode .proto.
In run()
:
with grpc.insecure_channel("localhost:50051") as channel:
stub = route_guide_pb2_grpc.RouteGuideStub(channel)
Perhatikan bahwa di sini channel
digunakan sebagai pengelola konteks, dan akan otomatis ditutup setelah interpreter keluar dari blok with
.
Panggil metode layanan
Untuk metode RPC yang menampilkan satu respons (metode "response-unary"), gRPC Python mendukung semantik alur kontrol sinkron (pemblokiran) dan asinkron (non-pemblokiran). Untuk metode RPC streaming respons, panggilan akan segera menampilkan iterator nilai respons. Panggilan ke metode next()
iterator tersebut akan diblokir hingga respons yang akan dihasilkan dari iterator tersedia.
RPC streaming sisi server
Memanggil streaming respons ListFeatures
mirip dengan bekerja dengan jenis urutan:
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 streaming sisi klien
Memanggil RecordRoute
streaming permintaan mirip dengan meneruskan iterator ke metode lokal. Seperti RPC sederhana di atas yang juga menampilkan satu respons, RPC ini dapat dipanggil secara serentak:
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 streaming dua arah
Memanggil RouteChat
streaming dua arah memiliki (seperti yang terjadi di sisi layanan) kombinasi semantik streaming permintaan dan streaming respons.
Buat pesan permintaan, lalu kirim satu per satu menggunakan 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)
Menerima dan memproses respons server:
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}"
)
Panggil metode bantuan
Saat dijalankan, eksekusi metode yang baru saja kita buat, dan teruskan stub
ke metode tersebut.
print("-------------- ListFeatures --------------")
guide_list_features(stub)
print("-------------- RecordRoute --------------")
guide_record_route(stub)
print("-------------- RouteChat --------------")
guide_route_chat(stub)
7. Cobalah
Jalankan server:
python route_guide_server.py
Dari terminal lain, aktifkan kembali lingkungan virtual (source .venv/bin/activate)
, lalu jalankan klien:
python route_guide_client.py
Mari kita lihat outputnya.
ListFeatures
Pertama, Anda akan menemukan daftar fitur. Setiap fitur di-streaming dari server (RPC streaming sisi server) saat fitur tersebut ditemukan berada dalam persegi panjang yang diminta:
-------------- 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
Kedua, RecordRoute
menunjukkan daftar titik yang dikunjungi secara acak yang di-streaming dari klien ke server (RPC streaming sisi klien):
-------------- 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)
Setelah klien selesai melakukan streaming semua titik yang dikunjungi, klien akan menerima respons non-streaming (RPC unary) dari server. Respons ini akan berisi ringkasan perhitungan yang dilakukan pada rute lengkap klien.
Finished trip with 10 points Passed 10 features Traveled 654743 meters It took 0 seconds
RouteChat
Terakhir, output RouteChat
menunjukkan streaming dua arah. Saat "mengunjungi" titik home
atau work
, klien akan merekam catatan untuk titik tersebut dengan mengirim RouteNote ke server. Jika suatu titik sudah dikunjungi, server akan melakukan streaming kembali semua catatan sebelumnya untuk titik ini.
-------------- 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. Langkah berikutnya
- Pelajari cara kerja gRPC di Pengantar gRPC dan Konsep inti
- Pelajari tutorial Dasar-Dasar.
- Pelajari referensi Python API