Blog
gRPC — Remote Procedure Call cz. 1
Tagi: backend, dotnet, grpc, komunikacja, mikroserwisy

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.

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.

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#.