en / de
Expertisen
Methoden
Dienstleistungen
Referenzen
Jobs & Karriere
Firma
Technologie-Trends TechCast WebCast TechBlog News Events Academy

gRPC Tutorial Teil 1: gRPC in ASP.NET Core

1 Einleitung

gRPC bedeutet Google Remote Procedure Call, wurde entwickelt durch Google in 2015 und freigegeben in August 2016. gRPC läuft ausschliesslich HTTP/2 und verwendet Protocol Buffers als Schnittstellebeschreibungssprache.

Noser Engineering gRPC in ASP.NET Core

Geschichte der verteilen APIs

Quelle: Pluragsight ‹Using gRPC in ASP.NET Core› von Shawn Wildermuth.

gRPC ist kein Ersatz für REST weil gRPC nicht geeignet ist für Webseiten:

REST ist optimal für CRUD Operationen und pure WEB Apps.
SignalR is gut für Multicasting und ‘Soft’ Echtzeit-Kommunikation.
GraphQL ist optimal für das offene Durchsuchen von grossen Datenmengen.
gRPC ist ideal zur Kommunikation zwischen Services auf den Servern.

gRPC:

Es ist sehr aufwändig gRPC in Blazor-WebAssembly Anwendungen zu nutzen (mit Blazor-Serverside geht das ohne Probleme). Browser geben im Moment keinen Zugang auf HTTP/2 Framing oder http Response Headers.

1.1 gRPC vs.REST

Parameter gRPC REST
Serialisierung protobuf JSON/XML
Protocol HTTP/2.0 HTTP/1.1
Browser Support No Yes
Data Exchange Messages Resources and Verbs
Request-Response Model Supports all Types of Streaming as based on Http/2 Only supports Request Response as based on Http/1.1
Payload Exchange Format Strong Typing Serialization JSON/XML

Quelle: https://www.c-sharpcorner.com/article/grpc-using-c-sharp-and-net-core-day-one/

1.2 gRPC vs. WCF

gRPC WCF
Service in ProtoFile ServiceContract
RPC Method in ProtoFile OperationContract
Message in ProtoFile DataContract
Richer error Model FaultContract
Unary Streaming Request-Reply
Bidirectional Streaming Duplex
Protobuf IDL WSDL

Quelle: https://www.c-sharpcorner.com/article/grpc-using-c-sharp-and-net-core-day-one/

1.3 Und was ist mit gRPC-Web?

Die Lösung für gRPC und Web scheint gRPC-Web zu sein welches in 2018 zum ersten Mal erschien (vom gRPC-Team). gRPC-Web besteht aus zwei Teilen: einem JavaScript-Client, der alle modernen Browser unterstützt und einem gRPC-Web-Proxy auf dem Server. Der gRPC-Web-Client ruft den Proxy auf und der Proxy leitet die gRPC-Anforderungen an den gRPC-Server weiter.
Nicht alle Funktionen von gRPC werden von gRPC-Web unterstützt. Client- und bidirektionales Streaming werden nicht und das Server-Streaming nur eingeschränkt unterstützt.

2  Vertragsdefinition in gRPC

Die Verträge werden mit Protocol Buffers definiert. Eine Methode hat keinen Parameter (google.protobuf.Empty) oder einen Parameter vom Typ ‘message’ und keinen oder einen Rückgabewert (ebenfalls Typ ‘message’). Die Definition vom Service ‘AddressBookService’ in einer proto-Datei könnte so aussehen:

syntax = "proto3";
import "enums.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
option csharp_namespace = "Addressbook.Services";

service AddressBookService {
    rpc AddPersons (AddressBookMessage) returns (TransferStatusMessage);
    rpc GetAddressBook (google.protobuf.Empty) returns (AddressBookMessage);
    rpc UploadPersonImage (stream PersonImageMessage) returns (TransferStatusMessage);
    rpc DownloadPersonImage (PersonMessage) returns (stream PersonImageMessage);
}

message TransferStatusMessage {
    string message = 1;
    TransferStatus status = 2;
}
…

2.1  Die vier Wege von gRPC

Es gibt vier Typen von Serveraufrufen in RPC:

  1. Unary RPC
  2. Server Streaming RPC
  3. Client Streaming RPC
  4. Bidirectional Streaming RPC

 

2.1.1        Unary RPC

Einfache Serveraufrufe mit keinem oder einem message-Parameter und keiner oder einer Rückgabe-message.

rpc GetMovie(QueryParams) returns (Movie){};

2.1.2  Server Streaming RPC

Der Client macht einen Serveraufruf mit keinem oder einem message-Parameter und der Server gibt einen offenen Stream zurück. Der Stream kann jetzt verwendet werden für:

rpc GetMovie(QueryParams) returns (stream Data){};

2.1.3  Client Streaming RPC

Wird verwendet zum Upload grosser Dateien zum Server.

rpc Upload(Stream Data) returns (Status){};

Der Client sendet einen Stream zum Server und wartet, bis der Server antwortet.

2.1.4  Bidirectional Streaming RPC

Streaming bedeutet nicht immer grosse Dateien. Das bidirektionale Streaming kann z.B. verwendet werden, um IsAlive Signale zwischen Client und Server auszutauschen, um zu kontrollieren, ob beide noch ‘gesund’ sind.

rpc CheckConnection(Stream Ping) returns (Pong){};

3  gRPC und ASP.NET Core

gRPC ist Teil vom ASP.NET Core 3.1 Framework. Im folgenden wird eine Beispielanwendung erstellt. Den Quellcode kann man hier herunterladen.

3.1 PC Einrichten

Es braucht Visual Studio 2019. Visual Studio Community, Professional und Enterprise funktionieren alle. Einfach .NET Core 3.1 herunterladen (nicht .NET Framework 4.8).

3.2 Server Projekt erstellen

Noser Engineering AG - gRPC Tutorial - new ASP.NET Core web application

Erstelle neue ASP.NET Core web Applikation

3.3  Integrieren von Protocol Buffers (Protobuf)

3.3.1  Verträge erstellen

Beispiel von enums.proto

syntax = "proto3";
option csharp_namespace = "Addressbook.Services";

enum PhoneType {
  PHONETYPE_UNSPECIFIED = 0;
  PHONETYPE_MOBILE = 1;
  PHONETYPE_HOME = 2;
  PHONETYPE_WORK = 3;
}

enum Gender {
  GENDER_UNSPECIFIED = 0;
  GENDER_MALE = 1;
  GENDER_FEMALE = 2;
}

enum ReadingStatus{
  READINGSTATUS_UNSPECIFIED = 0;
  READINGSTATUS_SUCCESS = 1;
  READINGSTATUS_FAILURE = 2;
  READINGSTATUS_INVALID = 3;
}

Beispiel von addressbook.proto

syntax = "proto3";
import "Protos/enums.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
option csharp_namespace = "Addressbook.Services";

service AddressBookService {
    rpc AddPersons (AddressBookMessage) returns (StatusMessage);
    rpc GetAddressBook (google.protobuf.Empty) returns (AddressBookMessage);
}

message StatusMessage {
    string message = 1;
    ReadingStatus status = 2;
}

message Person {
  string name = 1;
  int32 age = 2;
  string email = 3;
  Gender gender = 4;
  message PhoneNumber {
    string number = 1;
    PhoneType phoneType = 2;
  }
  repeated PhoneNumber phone_numbers = 5;
  google.protobuf.Timestamp last_updated = 6;
}

message AddressBook {
  repeated Person people = 1;
}

message AddressBookMessage {
  AddressBook addressBook = 1;
  StatusMessage statusMessage = 2;
}
Noser Engineering AG - gRPC Tutorial - Aktion 'Protobuf compiler' einstellen

Aktion ‹Protobuf compiler› einstellen

Wenn man jetzt kompiliert werden zwei *.cs-Dateien generiert, die sich in ‘obj\Debug\netcoreapp3.1‘ befinden. Die Dateien sind im Solution Explorer nur sichtbar, wenn man ‘Show all files’ aktiviert.

3.3.2  Service implementieren

namespace Addressbook.Services
{
    public class AddressbookService : AddressBookService.AddressBookServiceBase
    {
        private readonly ILogger<AddressbookService> _logger;

        public AddressbookService(ILogger<AddressbookService> logger)
        {
            _logger = logger;
        }

        public override Task<StatusMessage> AddPersons(AddressBookMessage request, ServerCallContext context)
        {
            StatusMessage result = new StatusMessage { Status = ReadingStatus.Failure };
            try
            {
                foreach (var p in request.AddressBook.Persons)
                {
                    // Todo: create person EF and add to repo
                }
                // Todo: save all changes to repo
                result.Status = ReadingStatus.Success;
            }
            catch (Exception ex)
            {
                result.Message = "Message thrown during processing.";
                _logger.LogError($"Message thrown during processing ({ex}).");
            }
            return Task.FromResult(result);
        }

        public override Task<AddressBookMessage> GetAddressBook(Empty request, ServerCallContext context)
        {
            AddressBookMessage result = new AddressBookMessage();
            result.StatusMessage = new StatusMessage() { Status = ReadingStatus.Success };
            result.AddressBook = new AddressBook();
            // Todo: read from repo
            result.AddressBook.Persons.Add(new Person()
            {
                Name = "Erik Stroeken", 
                Gender = Gender.Male, 
                Age = 50, 
                Email = "[email protected]",
                PhoneNumbers =
                {
                    new Person.Types.PhoneNumber {Number = "00 41 76 444 00 87", PhoneType = PhoneType.Mobile},
                    new Person.Types.PhoneNumber {Number = "00 41 44 444 83 16", PhoneType = PhoneType.Home},
                    new Person.Types.PhoneNumber {Number = "00 41 41 444 66 45", PhoneType = PhoneType.Work}
                },
                LastUpdated = Timestamp.FromDateTime(DateTime.UtcNow)
            });
            return Task.FromResult(result);
        }
    }
}

 

3.3.3        Den Service verdrahten

Startup.cs

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddGrpc(opt => { opt.EnableDetailedErrors = true; });
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGrpcService<AddressBookService>();
        endpoints.MapControllers();
    });
}
Noser Engineering AG - gRPC Tutorial - Kestrel einstellen

Kestrel einstellen

Hier ist die Log-Information wenn der Server gestartet wird:

Noser Engineering AG - gRPC Tutorial - Output vom gRPC Server

Output vom gRPC Server

3.4  Erstellen des gRPC Clients

Noser Engineering AG - gRPC Tutorial - Add new Worker Project

Add new Worker Service Project

Noser Engineering AG - gRPC Tutorial - Service Referenz hinzufügen

Service Referenz hinzufügen

Noser Engineering AG - gRPC Tutorial - Nur client Code generieren

Nur client Code generieren

Noser Engineering AG - gRPC Tutorial - Nur messages generieren

Nur messages generieren

Der Client importiert die proto-Dateien als Referenzen in das Projekt. Die import-Anweisung in der Protodatei ist relativ und bezieht sich jetzt auf das Client-Projekt, in dem die Datei enums.proto nicht vorhanden ist.

Hack

syntax = "proto3";
import "enums.proto";
<ItemGroup>
    <Protobuf Include="Protos\addressBook.proto" ProtoRoot="Protos\" />
    <Protobuf Include="Protos\enums.proto" ProtoRoot="Protos\" />
</ItemGroup>

3.5  Implementiere den Client

Füge die folgende Sektion zu der appsettings.json-Datei hinzu:

{
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
        }
    },
    "Service": {
        "CustomerId": 1,
        "DelayInterval": 3000,
        "ServiceUrl" :  "https://localhost:5001"
    }
}

Hier ist der Code vom Worker:

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly IConfiguration _config;
    private AddressBookService.AddressBookServiceClient _client = null;

    public Worker(ILogger<Worker> logger, IConfiguration config)
    {
        _logger = logger;
        _config = config;
    }

    protected AddressBookService.AddressBookServiceClient Client
    {
        get
        {
            if (_client == null)
            {
                ChannelBase channel = GrpcChannel.ForAddress(_config["Service:ServiceUrl"]);
                _client = new AddressBookService.AddressBookServiceClient(channel);
            }
            return _client;
        }
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
       AddressBook addressBook = new AddressBook();
       addressBook.Persons.Add(
           new Person()
           {
               Name = "Jan Muster",
               Gender = Gender.Male,
               Age = 50,
               Email = "[email protected]",
               PhoneNumbers =
               {
                   new Person.Types.PhoneNumber {Number = "00 41 76 444 00 87", 
                                                 PhoneType = PhoneType.Mobile},
                   new Person.Types.PhoneNumber {Number = "00 41 44 555 83 16", 
                                                 PhoneType = PhoneType.Home},
                   new Person.Types.PhoneNumber {Number = "00 41 41 666 66 45", 
                                                 PhoneType = PhoneType.Work}
               },
               LastUpdated = Timestamp.FromDateTime(DateTime.UtcNow)
           });
       AddressBookMessage msg = new AddressBookMessage
       {
           AddressBook = addressBook
       };
       var status = Client.AddPersons(msg);
       while (!stoppingToken.IsCancellationRequested)
       {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                var result = await Client.GetAddressBookAsync(new Empty());
                await Task.Delay(_config.GetValue("Service:DelayInterval"), stoppingToken);
            }    
      }
}

 

Starte erst den Server und dann den Client. Die Log-Traces sehen ungefähr so aus (rechts ist Client):

Noser Engineering AG - gRPC Tutorial - Output vom Server und Client

Output vom Server und Client

4.  Fazit

Dieser Blogbeitrag gibt eine Übersicht über die Vor- und Nachteile von gRPC und eine Einführung mit einer einfachen Implementierung. In der nächsten Folge wird Server und Client Streaming erklärt mit einem einfachem Beispiel, in welchem ein Bild hoch- und runtergeladen wird.

Nächster Post →

Kommentare

Eine Antwort zu “gRPC Tutorial Teil 1: gRPC in ASP.NET Core”

  1. […] und läuft ausschliesslich HTTP/2. Eine detaillierte Einleitung ist beschrieben im ersten Teil von diesem […]

Schreiben Sie einen Kommentar

Ihre E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Newsletter - aktuelle Angebote, exklusive Tipps und spannende Neuigkeiten

 Jetzt anmelden
NACH OBEN
Zur Webcast Übersicht