Grpc to nowoczesna platforma RPC o wysokiej wydajności, która może działać w dowolnym środowisku. Projekt został stworzony na licencji open-source przez firmę Google. Jest częścią Cloud Native Computation Foundation (CNCF). Z użyciem gRPC aplikacja kliencka może bezpośrednio wywołać metodę w aplikacji serwera na innej maszynie, tak jakby była obiektem lokalnym. Podobnie jak w wielu systemach RPC, gRPC polega na definiowaniu usług, określaniu metod, które mogą być wywoływane zdalnie, wraz z parametrami żądań i typami zwracanymi. Klient oraz serwer gRPC mogą być uruchamiane i komunikować ze sobą w różnych środowiskach. Dodatkowym atutem jest niezależność od języka. Kod serwera oraz klienta mogą być implementowane w dowolnym języku programowania wspieranym przez gRPC np. możemy napisać kod serwera z wykorzystaniem języka Java oraz kod klienta np. w językach Go, Python lub innym wspieranym przez gRPC.
Obecnie systemy informatyczne projektowane są w architekturze mikroserwisów. Mikroserwisy najczęściej komunikują się z wykorzystaniem REST bazującym na protokole HTTP/1.1, wymieniając dane z formacie JSON. REST jest łatwy, wygodny oraz szeroko używany do wymiany informacji między mikroserwisami. Posiada jednak następujące problemy:
Wszystkie wymienione problemy mogą skutkować znacznym spadkiem wydajności naszych systemów. REST jest bardzo dobry do komunikacji pomiędzy przeglądarką a serwerem, natomiast od komunikacji między mikroserwisami potrzebujemy czegoś bardziej wydajnego. gRPC jest szybszy niż REST. Wzrost wydajności osiągamy, dzięki wbudowanym narzędziom, z których wewnętrznie korzysta gRPC:
Rozpoczęcie przygody z gRPC, a wiec definiowania zawartości obiektów żądań oraz odpowiedzi, wymaga zapoznania się z Protocol Buffers. Jest to niezależny od języka, rozszerzalny mechanizm serializacji danych strukturalnych pomiędzy usługami. Wykorzystując Protocol Buffers definiujemy kontrakt komunikacji pomiędzy dwoma systemami w postaci pliku o rozszerzeniu .proto, na podstawie którego możemy wygenerować kod w wybranym języku programowania wspieranym przez gRPC. Zalety użycia Protocol Buffers:
GRPC wykorzystuje protokół HTTP/2 jako szkielet komunikacji, który jest nowym standardem do komunikacji w Internecie. Z wykorzystaniem HTTP/2 klient oraz serwer mogą wysyłać wiadomości równolegle z wykorzystaniem tego samego połączenia TCP w przeciwieństwie do HTTP/1.1, który tworzy nowe połączenie dla każdego żądania do serwera, co efektywnie zmniejsza opóźnienia transmisji. HTTP/2 wprowadza binarny format komunikacji oraz zaawansowaną kompresję nagłówków. HTTP/1.1 vs HTTP/2
HTTP/1.1 | HTTP/2 |
---|---|
Format tekstowy | Format binarny |
Nagłówki przesyłane jako zwykły tekst | Skompresowane nagłówki |
Żądanie oraz odpowiedź w jednym połączeniu TCP | To samo połączenie TCP można ponownie wykorzystać, wysyłając kilka żądań |
Wykorzystując gRPC możemy definiować 4 typy API:
Implementację stworzono w języku programowania Java z wykorzystaniem frameworka SpringBoot. Projekt został podzielony na następujące moduły:
Zawiera plik calculator.proto, który zawiera definicję API kalkulatora – dodawania dwóch liczb.
syntax = „proto3”;
package calculator;
option java_package = „com.proto.calculator”;
option java_multiple_files = true;
message SumRequest {
int64 first_number = 1;
int64 second_number = 2;
}
message SumResponse {
int64 result = 1;
}
service CalculatorService {
rpc Sum(SumRequest) returns (SumResponse) {};
}
Zdefiniowane obiekty:
Aplikacja serwera, która definiuje logikę kalkulatora. Po stronie serwera należy zaimplementować metodę sum, rozszerzając konkretną klasę oraz nadpisując implementację wybranej metody.
@GrpcService
public class CalculatorService extends CalculatorServiceGrpc.CalculatorServiceImplBase {
@Override
public void sum(SumRequest request, StreamObserver responseObserver) {
long firstNumber = request.getFirstNumber();
long secondNumber = request.getSecondNumber();
SumResponse response = SumResponse.newBuilder()
.setResult(firstNumber + secondNumber)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
Dodatkowo w pliku application.properties należy zdefiniować port serwera:
grpc.server.port=6565
Ostatnim krokiem jest zdefiniowanie aplikacji klienta. Wygenerowany kod dostarcza wzorca Builder, z użyciem którego możemy stworzyć obiekt SumRequest, a następnie przekazać jako parametr wywołania metody.
@Service
public class CalculatorService {
@GrpcClient(„calculator-service”)
private CalculatorServiceGrpc.CalculatorServiceBlockingStub blockingStub;
public long sum(long firstNumber, long secondNumber) {
SumRequest sumRequest = SumRequest.newBuilder()
.setFirstNumber(firstNumber)
.setSecondNumber(secondNumber)
.build();
SumResponse sum = blockingStub.sum(sumRequest);
return sum.getResult();
}
}
W pliku application.yaml zdefiniowano szczegóły dostępu serwera gRPC, do którego odwołuje się aplikacja klienta.
server:
port: 8080
grpc:
client:
calculator-service:
address: static://localhost:6565
negotiationType: plaintext
W artykule przedstawiono nowy sposób komunikacji między mikroserwisami – gRPC. Przedstawiono modele API jakie mogą zostać stworzone z jego użyciem. Przykładowa implementacja przedstawia typowy model komunikacji żądanie – odpowiedź (unary api), natomiast nic nie stoi na przeszkodzie, aby w podobny sposób zaimplementować inne modele API. Kompletny kod źródłowy dostępny tutaj.