Pierwsze kroki z gRPC-Python

1. Wprowadzenie

W tym module nauczysz się tworzyć za pomocą gRPC-Python klienta i serwera, które stanowią podstawę aplikacji do mapowania tras napisanej w Pythonie.

Po ukończeniu tego samouczka będziesz mieć klienta, który łączy się z serwerem zdalnym za pomocą gRPC, aby uzyskać nazwę lub adres pocztowy miejsca znajdującego się pod określonymi współrzędnymi na mapie. W pełni funkcjonalna aplikacja może korzystać z tego modelu klient-serwer, aby wyliczać lub podsumowywać ważne miejsca na trasie.

Usługa jest zdefiniowana w pliku Protocol Buffers, który będzie używany do generowania kodu szablonowego dla klienta i serwera, aby mogły się ze sobą komunikować. Dzięki temu zaoszczędzisz czas i wysiłek potrzebny na wdrożenie tej funkcji.

Wygenerowany kod obsługuje nie tylko złożoność komunikacji między serwerem a klientem, ale także serializację i deserializację danych.

Czego się nauczysz

  • Jak używać buforów protokołu do definiowania interfejsu API usługi.
  • Jak utworzyć klienta i serwer oparte na gRPC na podstawie definicji Protocol Buffers za pomocą automatycznego generowania kodu.
  • Znajomość komunikacji klient-serwer z użyciem gRPC.

Te warsztaty są przeznaczone dla programistów Pythona, którzy dopiero zaczynają korzystać z gRPC lub chcą sobie przypomnieć podstawy tej technologii, a także dla wszystkich innych osób zainteresowanych tworzeniem systemów rozproszonych. Nie musisz mieć wcześniejszego doświadczenia z gRPC.

2. Zanim zaczniesz

Czego potrzebujesz

  • Python w wersji 3.9 lub nowszej. Zalecamy Pythona 3.13. Instrukcje instalacji na poszczególnych platformach znajdziesz w artykule Python Setup and Usage (Konfigurowanie i używanie Pythona). Możesz też zainstalować Pythona, który nie jest częścią systemu, za pomocą narzędzi takich jak uv lub pyenv.
  • pip do instalowania pakietów Pythona.
  • venv do tworzenia wirtualnych środowisk Pythona.

Pakiety ensurepipvenv są częścią standardowej biblioteki Pythona i zazwyczaj są dostępne domyślnie.

Niektóre dystrybucje oparte na Debianie (w tym Ubuntu) decydują się jednak na wykluczenie ich podczas redystrybucji Pythona. Aby zainstalować pakiety, uruchom to polecenie:

sudo apt install python3-pip python3-venv

Pobierz kod

Aby ułatwić Ci naukę, w tym ćwiczeniu w Codelabs znajdziesz gotowy szkielet kodu źródłowego, który pomoże Ci zacząć. Poniższe kroki pomogą Ci wypełnić aplikację, w tym wygenerować kod gRPC za pomocą wtyczki kompilatora grpc_tools.protoc Protocol Buffer.

grpc-codelabs

Kod źródłowy szkieletu tego modułu jest dostępny w katalogu codelabs/grpc-python-getting-started/start_here. Jeśli nie chcesz samodzielnie implementować kodu, gotowy kod źródłowy znajdziesz w katalogu completed.

Najpierw utwórz katalog roboczy z ćwiczeniami i przejdź do niego:

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

Pobierz i rozpakuj ćwiczenia z programowania:

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

Możesz też pobrać plik ZIP zawierający tylko katalog z instrukcjami i rozpakować go ręcznie.

3. Określ usługę

Pierwszym krokiem jest zdefiniowanie usługi gRPC aplikacji, jej metody RPC oraz typów wiadomości żądania i odpowiedzi za pomocą języka definicji interfejsu Protocol Buffers. Twoja usługa będzie zapewniać:

  • Metoda RPC o nazwie GetFeature, która jest zaimplementowana na serwerze i wywoływana przez klienta.
  • Typy wiadomości PointFeature to struktury danych wymieniane między klientem a serwerem podczas korzystania z metody GetFeature. Klient podaje współrzędne mapy jako Point w żądaniu GetFeature wysyłanym do serwera, a serwer odpowiada, przesyłając odpowiedni Feature, który opisuje wszystko, co znajduje się pod tymi współrzędnymi.

Ta metoda RPC i jej typy wiadomości będą zdefiniowane w pliku protos/route_guide.proto podanego kodu źródłowego.

Bufory protokołu są powszechnie znane jako protobuf. Więcej informacji o terminologii gRPC znajdziesz w artykule Podstawowe koncepcje, architektura i cykl życia.

Rodzaje wiadomości

protos/route_guide.proto pliku kodu źródłowego najpierw zdefiniuj Point typ wiadomości. Symbol Point reprezentuje parę współrzędnych szerokości i długości geograficznej na mapie. W tym ćwiczeniu używaj liczb całkowitych jako współrzędnych:

message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

Numery 12 to unikalne identyfikatory poszczególnych pól w strukturze message.

Następnie określ Featuretyp wiadomości. Feature używa pola string na nazwę lub adres pocztowy czegoś w lokalizacji określonej przez Point:

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

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

Metoda usługi

Plik route_guide.proto ma strukturę service o nazwie RouteGuide, która definiuje co najmniej 1 metodę udostępnianą przez usługę aplikacji.

Dodaj metodę rpc GetFeature w definicji RouteGuide. Jak wspomnieliśmy wcześniej, ta metoda wyszukuje nazwę lub adres lokalizacji na podstawie podanego zestawu współrzędnych, więc w przypadku danego Point zwraca GetFeature Feature:

service RouteGuide {
  // Definition of the service goes here

  // Obtains the feature at a given position.
  rpc GetFeature(Point) returns (Feature) {}
}

Jest to metoda RPC typu unary: prosta procedura RPC, w której klient wysyła żądanie do serwera i czeka na odpowiedź, podobnie jak w przypadku lokalnego wywołania funkcji.

4. Generowanie kodu klienta i serwera

Następnie wygeneruj kod gRPC dla klienta i serwera z pliku .proto za pomocą kompilatora bufora protokołu.

Do generowania kodu Pythona gRPC stworzyliśmy narzędzie grpcio-tools. Usługa ta obejmuje:

  1. Zwykły kompilator protoc, który generuje kod Pythona z definicji message.
  2. wtyczka gRPC protobuf, która generuje kod Pythona (namiastki klienta i serwera) z definicji service;

Zainstalujemy pakiet Pythona grpcio-tools za pomocą narzędzia pip. Utwórz nowe wirtualne środowisko Pythona (venv), aby odizolować zależności projektu od pakietów systemowych:

python3 -m venv --upgrade-deps .venv

Aby aktywować środowisko wirtualne w powłoce bash/zsh:

source .venv/bin/activate

W przypadku systemu Windows i niestandardowych powłok zobacz tabelę na stronie https://docs.python.org/3/library/venv.html#how-venvs-work.

Następnie zainstaluj grpcio-tools (spowoduje to również zainstalowanie pakietu grpcio):

pip install grpcio-tools

Aby wygenerować kod początkowy w Pythonie, użyj tego polecenia:

python -m grpc_tools.protoc --proto_path=./protos  \
 --python_out=. --pyi_out=. --grpc_python_out=. \
 ./protos/route_guide.proto

Spowoduje to wygenerowanie tych plików dla interfejsów zdefiniowanych w route_guide.proto:

  1. route_guide_pb2.py zawiera kod dynamicznie tworzący klasy wygenerowane na podstawie definicji message.
  2. route_guide_pb2.pyi to „plik stub” lub „plik wskazówek dotyczących typu” wygenerowany na podstawie definicji message. Zawiera tylko sygnatury bez implementacji. Pliki stub mogą być używane przez środowiska IDE, aby zapewnić lepsze autouzupełnianie i wykrywanie błędów.
  3. route_guide_pb2_grpc.py jest generowany na podstawie definicji service i zawiera klasy oraz funkcje specyficzne dla gRPC.

Kod gRPC zawiera:

  1. RouteGuideStub, z którego klient gRPC może korzystać do wywoływania wywołań RPC RouteGuide.
  2. RouteGuideServicer, który definiuje interfejs implementacji usługi RouteGuide.
  3. add_RouteGuideServicer_to_server, która służy do rejestrowania RouteGuideServicer na serwerze gRPC.

5. Tworzenie usługi

Najpierw zobaczmy, jak utworzyć RouteGuideserwerRouteGuide. Tworzenie i uruchamianie serwera RouteGuide dzieli się na 2 etapy:

  • Wdrożenie interfejsu usługi wygenerowanego na podstawie definicji usługi za pomocą funkcji, które wykonują rzeczywistą „pracę” usługi.
  • Uruchomienie serwera gRPC na określonym porcie, aby nasłuchiwać żądań od klientów i przesyłać odpowiedzi.

Początkowy serwer RouteGuide znajdziesz w start_here/route_guide_server.py.

Implementacja RouteGuide

Klasa route_guide_server.py ma klasę RouteGuideServicer, która jest podklasą wygenerowanej klasy route_guide_pb2_grpc.RouteGuideServicer:

# RouteGuideServicer provides an implementation
# of the methods of the RouteGuide service.
class RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer):

RouteGuideServicer implementuje wszystkie metody usługi RouteGuide.

Przyjrzyjmy się szczegółowo prostej implementacji RPC. Metoda GetFeature otrzymuje od klienta Point i zwraca odpowiednie informacje o funkcji z bazy danych w 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

Metoda otrzymuje route_guide_pb2.Pointżądanie RPCgrpc.ServicerContext i obiekt grpc.ServicerContext, który zawiera informacje specyficzne dla RPC, takie jak limity czasu. Zwraca odpowiedź route_guide_pb2.Feature.

Uruchamianie serwera

Po zaimplementowaniu wszystkich RouteGuide metod następnym krokiem jest uruchomienie serwera gRPC, aby klienci mogli korzystać z Twojej usługi:

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

Metoda serwera start() nie blokuje wątku. Do obsługi żądań zostanie utworzony nowy wątek. Wątek wywołujący funkcję server.start() często nie ma w tym czasie nic innego do zrobienia. W takim przypadku możesz wywołać funkcję server.wait_for_termination(), aby prawidłowo zablokować wątek wywołujący do momentu zakończenia działania serwera.

6. Tworzenie klienta

W tej sekcji zajmiemy się tworzeniem klienta dla usługi RouteGuide. Początkowy kod klienta znajdziesz w start_here/route_guide_client.py.

Tworzenie stubu

Aby wywoływać metody usługi, musimy najpierw utworzyć stub.

Tworzymy instancję klasy RouteGuideStub z modułu route_guide_pb2_grpc, wygenerowaną z naszego .proto w pliku route_guide_client.py.

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

Metody wywoływania usługi

W przypadku metod RPC, które zwracają pojedynczą odpowiedź – znanych jako metody response-unary – gRPC Python obsługuje zarówno synchroniczną (blokującą), jak i asynchroniczną (nieblokującą) semantykę przepływu sterowania.

Proste wywołanie RPC

Najpierw zdefiniujmy Point, za pomocą którego będziemy wywoływać usługę. Powinno to być tak proste, jak utworzenie instancji obiektu z pakietu route_guide_pb2 z kilkoma właściwościami:

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

Synchroniczne wywołanie prostego RPC GetFeature jest prawie tak proste jak wywołanie metody lokalnej. Wywołanie RPC czeka na odpowiedź serwera i zwraca odpowiedź lub zgłasza wyjątek. Możemy wywołać metodę i zobaczyć odpowiedź w ten sposób:

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

Możesz sprawdzić pola obiektu Feature i wyświetlić wynik żądania:

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. Wypróbuj

Uruchom serwer:

python route_guide_server.py

W innym terminalu ponownie aktywuj środowisko wirtualne, a potem uruchom klienta:

python route_guide_client.py

Zobaczysz dane wyjściowe podobne do tych (sygnatury czasowe zostały pominięte dla przejrzystości):

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. Co dalej?