Blog

28 stycznia 2022 Marcin Cholewik

gRPC — Remote Procedure Call cz. 1

gRPC — Remote Procedure Call cz. 1

Koncepcja komunikacji pomiędzy wieloma maszynami nie jest nowa i już od wielu dekad wykorzystywana w branży IT. Mocną stroną takiego podejścia jest możliwość wykorzystania większej ilości zasobów komputerowych w sposób efektywny i dostosowany do potrzeb.

Systemy rozproszone swoją zaletę zawdzięczają możliwości wymiany danych pomiędzy urządzeniami często znajdującymi się w odległych od siebie lokalizacjach. Jedną z metod takiej wymiany danych jest zdalne wywoływanie procedur (Remote Procedure Calling), które w różnej formie od lat pozostaje w łaskach programistów. RPC, jak sama nazwa wskazuje, to uruchamianie procedur bądź metod przez klienta znajdującego się poza maszyną wykonującą kod. Istotne jest, że wywołanie procedury zdalnej powoduje zlecenie określonego zachowania, a więc, biorąc pod uwagę kod, nie różni się niczym od wywołania procedury lokalnej.

Remote Procedure Call

Jak już wspomniałem wcześniej, protokół ten nie jest nowy i istnieje wiele implementacji bądź wariantów RPC. Jednym z najistotniejszych elementów całego rozwiązania jest jednak kanał komunikacyjny. Może nim być, np.: TCP, UDP, czy też HTTP. To właśnie ten ostatni, czyli protokół HTTP, jest wykorzystywany przez jedną z implementacji RPC, której dotyczyć będzie niniejsza seria artykułów, czyli…

gRPC

Jest to framework implementujący RPC, utworzony przez Google i udostępniony na licencji open-source. Przygotowany został z zamysłem wykorzystania w mikroserwisach i posiada wiele cech, które znacznie usprawniają komunikację względem REST:

  • zmniejszenie wielkości transferów m.in. poprzez serializację binarną za pomocą protobuf,
  • wykorzystanie protokołu HTTP/2,
  • możliwość komunikacji dwukierunkowej,
  • wsparcie dla najpopularniejszych języków C#, JAVA, Node.js i wielu innych.
gRPC

Na szczególną uwagę zasługuje protobuf, który opisuje kontrakt pomiędzy klientem a serwerem. Kontrakt ten wykorzystać możemy w usługach napisanych w dowolnej technologii wykorzystującej gRPC.

protobuf

Jako przykład rozważmy kontrakt wykorzystywany podczas wyszukiwania noclegów, a dokładniej hoteli z wolnymi pokojami.

Po stronie serwera zostaje utworzony następujący plik .proto:

syntax = "proto3";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";

option csharp_namespace = "Esky.Hotels.ApiContract.Protos";

service Search {
    rpc SearchHotels (HotelSearchParameters) returns (stream HotelSearchResult);
}

message HotelSearchParameters {
    repeated int32 hotelCodes = 1;
    string currency = 2;
    string language = 3;
}

message HotelSearchResult {
    repeated Hotel hotels = 1;
}

message Hotel {
    int32 Code = 1;
    repeated RoomPackage roomPackages = 2;
}

message RoomConfiguration {
    int32 adults = 1;
    repeated int32 childrenAges = 2;
}

Ponadto w pliku .csproj zamieszczamy następującą konfigurację:

<ItemGroup>
<Protobuf Include="Protos\hapi.proto" GrpcServices="Server" />
</ItemGroup>

Na podstawie powyższego kontraktu oraz konfiguracji zostaje wygenerowana klasa SearchBase, będąca bazową klasą po stronie serwera. Klasa ta zawiera wirtualną metodę SearchHotels:

public virtual global::System.Threading.Tasks.Task SearchHotels(global::Esky.Hotels.ApiContract.Protos.HotelSearchParameters request, grpc::IServerStreamWriter<global::Esky.Hotels.ApiContract.Protos.HotelSearchResult> responseStream, grpc::ServerCallContext context)
{
  throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
}

Po utworzeniu klasy dziedziczącej z SearchBase nadpisujemy metodę wirtualną SearchHotels własną implementacją zawierającą mechanizm wyszukiwania wolnych pokoi hotelowych. 

Podczas tworzenia klienta posługujemy się tym samym plikiem .proto co po stronie serwera. W tym przypadku wygenerowaną klasą jest SearchClient, zawierająca metodę SearchHotels:

public virtual grpc::AsyncServerStreamingCall<global::Esky.Hotels.ApiContract.Protos.HotelSearchResult> SearchHotels(global::Esky.Hotels.ApiContract.Protos.HotelSearchParameters request, grpc::CallOptions options)
{
  return CallInvoker.AsyncServerStreamingCall(__Method_SearchHotels, null, options, request);
}

Metody tej możemy użyć w kliencie bez konieczności dodatkowej implementacji. Wykonanie jej powoduje wywołanie zdalne metody SearchHotels znajdującej się po stronie serwera.

Podsumowanie

W niniejszym artykule opisałem podstawowe zagadnienia związane ze zdalnym wywoływaniem procedur RPC. Ponadto skupiłem się na przedstawieniu podstawowych koncepcji gRPC takich jak kanał komunikacji, czy protobuf. Kolejny artykuł z serii będzie zawierał opis dwóch usług wraz z kompletną implementacją w języku C#.

Zobacz na blogu

09.09.2022
Marcin Jahn
It’s Not Just HTTP It’s Not Just HTTP

In today’s world of cloud-based solutions, distributed systems, and microservices-based architectures, network communication is a...

23.08.2022
Adam Mrowiec
Konferencja IPC 2022 Berlin Konferencja IPC 2022 Berlin

Pandemia wreszcie się kończy, dlatego w tym roku postanowiliśmy wrócić do naszych wyjazdów na konferencje....