Einstieg in gRPC mit ASP.NET Core

Ein Gamechanger in der Datenkommunikationstechnologie?
6
Oct

Einstieg in gRPC mit ASP.NET Core

Ein Gamechanger in der Datenkommunikationstechnologie? Der Grundgedanke des klassischen RPC (Remote Procedure Call – Aufruf einer fernen Prozedur) existiert bereits seit 1976. Google hat ein eigenes Protokoll, gRPC, entworfen, das für große Daten optimal ist und ökosystemübergreifend für viele aktuelle Sprachen zur Verfügung steht. Das perfekte Setting, um die Datenkommunikationstechnologie umzukrempeln.

Der Quasistandard bei den Datenkommunikationstechnologien ist zur heutigen Zeit HTTP. Dazu kommen Architekturstile und APIs wie REST, GraphQL und WebSockets zum Einsatz. Jede dieser einzelnen Möglichkeiten hat ihre eigenen Stärken und Schwächen. Einen Gewinner für alle Anforderungen gibt es hierbei nicht. Eher zählt die Devise, auf eine polyglotte Lösung zu setzen. Jetzt haben wir mit gRPC einen weiteren Mitspieler dazu gewonnen. Ist dieser jetzt der totale Gamechanger der Datenkommunikationstechnologie? Und wie können wir diesen in unseren .NET-Projekten nutzen? Tauchen wir gemeinsam ab, in dieses spannende neue Thema.

 

Einführung in gRPC

Das gRPC [1] ist ein Protokoll, um Funktionen in verteilten Computersystemen aufzurufen. Es wurde von Google entworfen und 2016 veröffentlicht. gRPC basiert auf dem HTTP/2-Standard und setzt auf Protocol Buffers. Google hat es der Cloud Native Computing Foundation gespendet [2]. Es steht ökosystemübergreifend für C, C++, C#, Dart, Go, Java, Kotlin, Node.js, Objective-C, PHP, Python und Ruby zur Verfügung. Die Datenkommunikation findet hier komprimiert im binären Datenformat statt, das gerade für richtig große Daten optimal ist. Eine weitere, besondere Stärke ist eine Echtzeitkommunikation über Streaming. Ein gRPC-Dienst unterstützt alle Streamingkombinationen: Unär (kein Streaming – einfache Client-Server-Kommunikation), Streaming vom Server zum Client, Streaming vom Client zum Server und bidirektionales Streaming (ein Streaming in beide Richtungen zur gleichen Zeit).

Der hauptsächliche Anwendungszweck von gRPC besteht eher in der Server-to-Server-Kommunikation. Es kann ebenfalls für native Desktop-, Mobile-, oder IoT-Clients ideal sein, die leistungsschwache Hardware haben und eine große Menge Daten beanspruchen. Eher suboptimal wären hierbei, anders als erwartet, HTTP-Clients. Obwohl gRPC über HTTP/2 bereitgestellt wird, gibt es kein natives Browser-API, um mit gRPC kommunizieren zu können. Es gibt zwar die Möglichkeit, mit zusätzlichen Tools und Libraries wie gRPC-Web eine gRPC-Kommunikation über einen HTTP-Client aufzubauen, in der Praxis erweist sich das allerdings als nicht gerade komfortabel. Eine Herausforderung ist hier auf jeden Fall das binäre Datenformat, womit man auf die Generierung von Client-Proxy-Code angewiesen ist. Das Integrieren der Codegenerierungstools beansprucht nochmal einiges an Zeit und ist nicht Native-WebDev-like. Ein weiterer, bitterer Nachgeschmack ist ebenfalls eine technische Einschränkung: Das gleichzeitige Streamen der Daten in beide Richtungen wird nicht unterstützt. Eine Ausnahme betrifft die unattraktive Toolunterstützung: Blazor-WebAssembly-Apps. Microsoft hat hierbei eine fertig integrierte Lösung in Visual Studio bereitgestellt. Es empfiehlt sich allerdings, auch hier nur gRPC einzusetzen, wenn wirklich große Daten permanent ausgetauscht werden müssen. Wieso sollte man einen Lamborghini fahren, wenn die Geschwindigkeitsgrenze bei dreißig liegt. Denn eines hat die Praxis dem Autor gezeigt: Auf Codegeneratoren angewiesen zu sein, kann die Produktivität stark beeinflussen. Nach jedem Build wird der Proxy-Code neu generiert und wenn das Tooling mal gerade nicht so will, muss man regelmäßig Visual Studio neustarten, damit alles wieder ordnungsgemäß funktioniert. Nicht gerade berauschend bei der täglichen Arbeit. Das Bereitstellen des eigenen gRPC API an externe Konsumenten ist ebenfalls nicht gerade überwältigend. Es gibt im Grunde genommen keinen Playground und auch keine einfache Art, diese Schnittstellen zu dokumentieren. Die bereits erwähnte Abhängigkeit von der Codegenerierung lässt einen die Developer Experience von gRPC dann mit der Schulnote befriedigend bis ungenügend bewerten. In Gänze bedeutet es, dass man das seinen Kunden nicht gerade antun möchte und wirft auch kein gutes Bild auf das eigene Unternehmen.

Zugegeben, die letzten Passagen klingen nicht gerade motivierend. Es gibt allerdings wieder Anwendungsfälle, in denen gRPC eine prachtvolle Lösung ist. Haben Sie bereits eine native Desktop-App in WinForms oder WPF? Wurde hierbei WCF als Client-Server-Kommunikation gewählt und soll das Backend auf .NET 5 oder höher umgestellt werden? Oder sollen im Backend unterschiedliche Services miteinander kommunizieren? Oder haben Sie leistungsschwache Clients, die in Echtzeit große Daten austauschen sollen? Dann ist gRPC auf jeden Fall die ideale Technologie.

 

Design by Contract

Der Grundstein von gRPC beginnt mit einem Vertrag: Design by Contract. Hierbei gibt es zwei unterschiedliche Möglichkeiten, einen Vertrag aufzusetzen: Contract first und Code first. Bei Contract first handelt es sich um den offiziellen Weg, wie er uns in der .NET-Welt zur Verfügung steht. Die Services und ihre Message-Datentypen werden in einer eigenen Standardsprache (IDL, Interface Definition Language) beschrieben. Codegeneratortools können daraus dann Proxycode erzeugen und der Protocol Buffer Serializer kann die Messages damit bestens binär serialisieren und deserialisieren.

In Abbildung 1 sehen wir sogenannte *.proto-Dateien. Das sind normale Textdateien, die mit IDL den Dienst und die Datenstrukturen deklarieren. Daraus wird dann der notwendige Code generiert. Wir konzentrieren uns hauptsächlich auf die Contract-first-Variante.

Abb. 1: Der Grundstein von gRPC beginnt mit Contract first

 

Die zweite Möglichkeit besteht nicht aus einem offiziellen Weg von Microsoft, sondern aus einer aus der Community getriebenen Methode: Code first. Hier wird, wie aus WCF bekannt ist, über Attribute bei Interfaces (für Services) und Datenklassen (DTOs) deklariert, wie etwas für gRPC bereitgestellt werden soll. In Abbildung 2 sehen wir, dass daraus der notwendige Code erst zur Laufzeit über die Metaprogrammierung bereitgestellt wird. Das Besondere an dieser Lösung ist, dass wir unser bisheriges Know-how zu WCF wiederverwenden können und uns nicht erst in die neue Deklarationssprache IDL mit Protocol Buffer (Protobuf) einarbeiten müssen [3].

 

Abb. 2: Eine besondere Alternative ist der Code-first-Ansatz

 

Ein neues ASP.NET-Core-Projekt mit gRPC erzeugen

Um die Anatomie hinter gRPC besser zu verstehen, wollen wir interaktiv einige Beispiele implementieren. Microsoft stellt bereits eine gRPC-Projektvorlage für ASP.NET Core zur Verfügung, die über ein neues Projekt in Visual Studio 2019 oder über die .NET CLI ausgewählt werden kann. In der Eingabeaufforderung wird mit der .NET CLI das neue Projekt mit folgendem Befehl erzeugt:

 

dotnet new grpc –name HelloGrpc

 

Anschließend wurde ein neuer HelloGrpc-Ordner erzeugt, die darin enthaltene HelloGrpc.csproj-Datei öffnen wir gemeinsam mit Visual Studio 2019. Im Prinzip handelt es sich um ein gewohntes neues ASP.NET-Core-Projekt. Entdecken wir zunächst einmal die Unterschiede. Der gravierendste hierbei ist, dass bereits das Grpc.AspNetCore-NuGet-Paket vorinstalliert ist. Wie man es bereits von den Projektvorlagen gewohnt ist, kann man dieses Paket erst einmal manuell auf die neueste Version updaten. In der Startup.cs-Datei werden bei der ConfigureServices-Methode alle notwendigen Klassen beim Dependency-Injection-Provider registriert und instanziiert:

 

services.AddGrpc();

 

Mit einer Middlewareroute für die Routing-Pipeline wird unser gRPC Service zur Laufzeit für andere Clients zugänglich gemacht. Es ist bis jetzt nur eine Route registriert, und zwar für einen Beispieldienst:

 

endpoints.MapGrpcService<GreeterService>();

 

Dann bleiben noch die beiden Verzeichnisse Protos und Services übrig. Im Protos-Ordner befindet sich unsere Protocol-Buffer-Datei für Contract first, die wir jetzt genauer erforschen.

 

Protocol Buffers

Protocol Buffers [4] ist ein Datenformat zur Serialisierung mit der Schnittstellenbeschreibungssprache IDL. Auch diese wurde von Google entwickelt und bereits 2008 veröffentlicht. Hauptsächliche Entwurfskriterien der Protocol Buffers sind Einfachheit und Performance. Daher ist es im Gegensatz zu XML oder JSON als Binärformat konzipiert, das auf ein textuelles Format setzt. Die .NET-Implementierung (protobuf-net) gibt es allerdings erst seit 2014. Einen tatsächlichen Mehrwert des binären Formats gewinnt man allerdings erst bei richtig großen Datenmengen. Ein Vergleich von JSON und Protobuf hat gezeigt, dass bei einem gewöhnlichen Datenaustausch kein wirklicher Unterschied festzustellen ist.

 

Abb. 3: Die greet.proto-Datei beschreibt in IDL, wie Protobuf binär (de)serialisieren soll

 

In Abbildung 3 sehen wir den Inhalt der greet.proto-Datei. Zu Beginn wird auf die aktuelle Protobuf-Version [5] hingewiesen, was ausschlaggebend dafür ist, welche Sprachfeatures wir nutzen können. Die package-Deklaration entspricht einem Namespace unter C#, wodurch eine saubere Strukturierung der unterschiedlichen Dienste erfolgen kann. Die Daten zur Kommunikation werden mit Messages behandelt. Diese sind notwendig für Anfragen sowie für die zu liefernden Antworten. Im Prinzip handelt es sich dabei um Datentransferobjekte (DTOs).

In beiden Messages wird eine Zuweisung mit dem Wert 1 festgelegt. Dabei handelt es sich nicht um den tatsächlichen Integer-Wert 1, sondern um eine Indexierung vom jeweiligen Feld für die binäre Serialisierung. So soll unsere Datenkommunikation später stark optimiert werden können, indem nicht der tatsächliche Name im Binärformat serialisiert wird, sondern nur der Indexwert. Würden wir jetzt ein weiteres Feld hinzufügen, müssen wir natürlich mit Wert 2 aufsteigend weiter machen. Jeder Wert muss hierzu innerhalb einer Message einzigartig sein. Die explizite Deklaration soll hierbei eine kontrollierte Abwärtskompatibilität gewährleisten.

Anschließend wird hier das Service-Modell beschrieben. Der offizielle Styleguide [6] gibt auch vor, dass „Service“ im Namen enthalten sein sollte. Eine Service-Funktion beginnt mit der rpc-Deklaration gefolgt vom Funktionsnamen. Dann werden ebenfalls eine Parameter- und eine Rückgabe-Message festgelegt.

 

Die Codegenerierung von Protobuf zu .NET

In Visual Studio 2019 ist protobuf-net bereits ein fester Bestandteil, wodurch beim Abspeichern des Projekts oder notfalls bei einem Rebuild alle notwendigen Service-Kontexte und DTO-Klassen generiert werden. In Abbildung 4 sehen wir die versteckten Verzeichnisse unseres Projekts und den Ort, an dem die generierten Daten liegen. Es hat sich allerdings in der Praxis gezeigt, dass ein wiederholtes Neustarten von Visual Studio 2019 notwendig ist, wenn die Codegenerierung versagt.

 

Abb. 4: Die von Visual Studio 2019 generierten Daten für unseren gRPC Service

 

Den GreeterService erkunden

Der Beispielservice GreeterService ist im Services-Ordner enthalten. In Listing 1 wird die Implementierung gezeigt. Hier können wir schön erkennen, dass vom generierten Service-Kontextcode eine Basisklasse festgelegt wurde. Wir erhalten hier ebenfalls, wie man es vom ASP.NET-Core-Ökosystem gewohnt ist, Unterstützung durch Constructor Injection. Die Implementierung der Kommunikation erfolgt mit async/await. Bei dieser Implementierung handelt es sich um eine einfache Request-Response-Kommunikation

 

Listing 1: Der GreeterService mit einer Unary-Kommunikation
.namespace HelloGrpc
{
  public class GreeterService : Greeter.GreeterBase
  {
    private readonly ILogger _logger;
    public GreeterService(ILogger logger)
    {
      _logger = logger;
    }

    public override Task SayHello(HelloRequest request, ServerCallContext context)
    {
      return Task.FromResult(new HelloReply
      {
        Message = "Hello " + request.Name
      });
    }
  }
}

Kein Support für IIS Express

Ein ungewohnter Aspekt dieses Projekttemplates für ASP.NET Core ist, dass kein IIS-Express-Profil vorkonfiguriert ist. Das liegt daran, dass gRPC auf HTTP/2 läuft, das nicht von IIS Express unterstützt wird. Aus diesem Grund kann hier der Einsatz nur direkt über den leichtgewichtigen Cross-Platform-Webserver Kestrel erfolgen. Hier darf man auf keinen Fall vergessen, dass bei der eigenen Produktivumgebung der Support für HTTP/2 noch aktiviert werden muss. Der aktuelle Stand ist sogar, dass gRPC noch nicht über Azure mit iisnode unterstützt wird [7]. Eine Alternative wäre gRPC-Web, das nicht gerade berauschend ist, wie eingangs erwähnt wurde.

 

Wir bauen einen gRPC-Client

Zu unserem Projekt erzeugen wir einfach eine neue Konsolenanwendung innerhalb der Solution. Der Name wird auf HelloGrpc.Client festgelegt. Die nächsten Schritte dürften allen WSDL-, Web-Services- und WCF-Services-Entwicklern und -Entwicklerinnen bekannt vorkommen. Nach einem Rechtsklick auf das neue Konsolenprojekt wählen wir im Kontextmenü Add und dann Service reference … Anschließend öffnet sich ein Tab mit einer Übersicht über alle bereits hinzugefügten Services. Beim Klick auf den Add-Button von Service References öffnet sich ein Wizard (Abb. 5). Hier wählen wir gRPC Service aus und beim nächsten Dialog wird, wie in Abbildung 6 zu sehen ist, die Protobuf-Datei ausgewählt. Anschließend wird uns der notwendige Client-Proxy-Code generiert.

In Listing 2 steht der Code für einen einfachen Request-Response-Zugriff (Unary). Über die Solution-Eigenschaften müssen beide Projekte als Startprojekte festgelegt werden. Anschließend können wir die Anwendung ausführen und erhalten die Textausgabe „Hello World“ auf der Konsole.

 

Abb. 5: gRPC-Client generieren lassen

 

Abb. 6: Protobuf-Datei für Clientcodegenerierung auswählen

 

Listing 2: Der gRPC-Client für eine Unary-Kommunikation zum GreeterService
class Program
{
  static async Task Main(string[] args)
  {
    var channel = GrpcChannel.ForAddress("https://localhost:5001");
    var greeter = new GreeterClient(channel);

    var result = await greeter.SayHelloAsync(new HelloRequest { Name = "World" });
    Console.WriteLine(result.Message);
    Console.ReadKey();
  }
}

Streaming-Service für Server-to-Client-Kommunikation implementieren

Eine besondere Stärke von gRPC ist eine Echtzeitkommunikation in unterschiedliche Streamingrichtungen. In diesem Beispiel soll der Server dem Client dauerhaft Informationen senden, während der Client einfache Antworten asynchron liefert. Ein idealer Algorithmus zur Veranschaulichung ist die Fibonacci-Folge. Diese beginnt mit einer 1, die mit einer weiteren 1 addiert wird. Daraus erhalten wir das Ergebnis 2. Dann folgt die weitere Berechnung mit 2 plus das letzte Ergebnis 1, wir erhalten 3 usw., bis wir zu einem festgelegten Ende gelangen. Listing 3 zeigt die dazugehörige Fibonacci-Implementierung, die wir für unserem gRPC Service benötigen.

Passend zur Implementierung fügen wir mit Visual Studio im Protos-Ordner eine neue Protobuf-Datei mit dem Namen fibonacci.proto hinzu. Hier werden der Service sowie die eingehende und ausgehende Message deklariert. Der wesentliche Aspekt im Vergleich zur greeting.proto-Datei ist, dass zusätzlich der Rückgabetyp mit einem stream-Keyword gekennzeichnet wird (Listing 4). Ein sehr wichtiger Schritt ist jetzt das Aktivieren des Protobuf-Compilers für unsere neue Datei. Das muss jeweils explizit pro Datei in den Dateieigenschaften erfolgen. Die Datei benötigt in Visual Studio die folgenden Eigenschaften: Build Action auf Protobuf compiler und gRPC Stub Classes auf Server only.

Listing 3: Die Fibonacci-Implementierung
public class Fibonacci
{
  public IEnumerable GetNumbers(int from, int to)
  {
    var previous = 0;
    var current = from;

    while (current <= to)
    {
      (previous, current) = (current, previous + current);
      yield return current;
    }
  }
}

 

Listing 4: Die fibonacci.proto-Datei für einen Server-to-Client Streaming
syntax = "proto3";

option csharp_namespace = "HelloGrpc.Services";

service Fibonacci {
  rpc GetNumbers(NumberRequest) returns (stream NumberResponse);
}

message NumberRequest {
  int32 from = 1;
  int32 to = 2;
}

message NumberResponse {
  int32 result = 1;
}

 

Im Services-Verzeichnis wird jetzt eine FibonacciService-Klasse angelegt. Sie soll von der generierten Server-Basisklasse Fibonacci.FibonacciBase ableiten. Über Constructor Injection soll unsere Fibonacci-Instanz injiziert werden. Die Basisklasse selbst bietet uns jetzt das Überschreiben einer GetNumbers-Methode an. In Listing 5 ist die fertige FibonacciService-Implementierung zu sehen. Die Stream-Kommunikation zum Client findet dann über den responseStream-Parameter statt.

 

Listing 5: Die Fibonacci-Service-Implementierung
public class FibonacciService : Fibonacci.FibonacciBase
{
  private readonly HelloGrpc.Fibonacci _fibonacci;

  public FibonacciService(HelloGrpc.Fibonacci fibonacci)
  {
    _fibonacci = fibonacci;
  }

  public override async Task GetNumbers(NumberRequest request, IServerStreamWriter responseStream, ServerCallContext context)
  {
    foreach (var current in _fibonacci.GetNumbers(request.From, request.To))
    {
      await responseStream.WriteAsync(new NumberResponse { Result = current });
      await Task.Delay(300);
    }
  }
}

 

Zum Schluss muss das Backend alle Bestandteile an der richtigen Stelle registriert haben. Das erfolgt in der Startup.cs-Datei. Bei der ConfigureService-Methode registrieren wir die Fibonacci-Klasse als Singleton beim Dependency Injection Provider mit:

 

services.AddSingleton<Fibonacci>();

 

Der neue Service muss ebenfalls als Route zur Verfügung gestellt werden. Das erfolgt mit einem Eintrag innerhalb der Configure-Methode:

 

endpoints.MapGrpcService<FibonacciService>();

 

Streamingclient für Server-to-Client-Kommunikation implementieren

Der Client benötigt eine neue Client-Proxy-Implementierung unseres neuen Service. Wir machen dazu einen Rechtsklick auf das Konsolenprojekt. Im Kontextmenü wählen wir Add und dann Service reference … Es öffnet sich wieder der Tab mit der Übersicht über alle bereits hinzugefügten Services. Beim Klick auf den +-Button von Service References öffnet sich wieder der Wizard. Hierbei wählen wir gRPC Service aus und beim nächsten Dialog wird die neue fibonacci.proto-Datei ausgewählt. Anschließend wird uns der notwendige Client-Proxy-Code generiert.

In Listing 6 sehen wir dazu dann die passende Implementierung. Einige neue C#-Features kommen hier zum Einsatz. Zum Beispiel sorgt das using hinter der Variablendeklaration für eine flachere Codestruktur. Über das ResponseStream Property können wir asynchron mit ReadAllAsync auf die eingehenden Datenströme reagieren. Mit der await/Forearch-Deklaration funktioniert das auch wunderbar in Kombination mit der Foreach-Schleife. Beim gleichzeitigen Ausführen vom Service und Client können wir leicht verzögert sehen, wie der Service die Daten nacheinander an den Client ausliefert und dieser sie dann zur Ausgabe weitergibt.

 

Listing 6: Die Fibonacci-Client-Implementierung
class Program
{
  static async Task Main(string[] args)
  {
    var channel = GrpcChannel.ForAddress("https://localhost:5001");
    var fibonacciClient = new FibonacciClient(channel);

    using var result = fibonacciClient.GetNumbers(new NumberRequest { From = 1, To = 100000000 });

    await foreach (var response in result.ResponseStream.ReadAllAsync())
    {
      Console.WriteLine("Response: " + response.Result);
    }

    Console.ReadKey();
  }
}

 

Bidirektionales Streaming

Eine technologische Meisterleistung von gRPC ist das bidirektionale Streaming. Damit ist das gleichzeitige Streamen von Server zu Client und umgekehrt gemeint. Das bisherige Beispiel kann zur Demonstration ganz einfach angepasst werden. Bei der fibonacci.proto-Datei erhält der Anfrageparameter zusätzlich das Stream-Schlüsselwort (Listing 7). Beim Speichern und Bauen erzeugt Visual Studio automatisch die neuen Proxydateien im Hintergrund.

 

Listing 7: Die fibonacci.proto-Datei für ein bidirektionales Streaming
syntax = "proto3";

option csharp_namespace = "HelloGrpc.Services";

service Fibonacci {
  rpc GetNumbers(stream NumberRequest) returns (stream NumberResponse);
}

message NumberRequest {
  int32 from = 1;
  int32 to = 2;
}

message NumberResponse {
  int32 result = 1;
}

 

Der FibonacciService erhält eine neue Methodensignatur von der Basisklasse. Der Request-Parameter wird zum RequestStream-Parameter. Diesen nutzen wir wieder mit einer asynchronen Foreach-Schleife, genauso, wie wir sie erst nur beim Client implementiert hatten. Listing 8 zeigt die neue Implementierung.

Listing 8: Die FibonacciService-Implementierung für ein bidirektionales Streaming
public class FibonacciService : Fibonacci.FibonacciBase
{
  private readonly HelloGrpc.Fibonacci _fibonacci;

  public FibonacciService(HelloGrpc.Fibonacci fibonacci)
  {
        _fibonacci = fibonacci;
  }

  public override async Task GetNumbers(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context)
  {
    await foreach (var request in requestStream.ReadAllAsync())
    {
      foreach (var current in _fibonacci.GetNumbers(request.From, request.To))
      {
        await responseStream.WriteAsync(new NumberResponse { Result = current });
        await Task.Delay(300);
      }
    }
  }
}

 

Der Client kann jetzt bei einer dauerhaften Verbindung nacheinander seine Daten senden. Zusätzlich möchten wir aber auch die Daten vom Server in einem eigenen Task beobachten. Die komplette Implementierung dazu ist in Listing 9 zu sehen.

 

Listing 9: Die Fibonacci-Client-Implementierung für ein bidirektionales Streaming
class Program
{
  static async Task Main(string[] args)
  {
    var channel = GrpcChannel.ForAddress("https://localhost:5001");
    var fibonacciClient = new FibonacciClient(channel);

    using var result = fibonacciClient.GetNumbers();
    var task = Task.Run(async () => await WatchResponse(result));

    await result.RequestStream.WriteAsync(new NumberRequest { From = 1, To = 1000000 });
    await result.RequestStream.WriteAsync(new NumberRequest { From = 1, To = 1000000 });
    await result.RequestStream.WriteAsync(new NumberRequest { From = 1, To = 1000000 });
    await task;

    Console.ReadKey();
  }

  static async Task WatchResponse(AsyncDuplexStreamingCall<NumberRequest, NumberResponse> result)
  {
    await foreach (var response in result.ResponseStream.ReadAllAsync())
    {
      Console.WriteLine("Response: " + response.Result);
    }
  }
}

 

Fazit

Der Artikel hat gezeigt, was sich hinter gRPC verbirgt. Zusätzlich haben wir entdeckt, wie einfach gRPC unter .NET Core zum Einsatz kommen kann, aber auch, welche Schwächen wir dabei haben. Abbildung 7 zeigt nochmal alle Vor- und Nachteile. Wir konnten allerdings auf viele weitere Aspekte nicht eingehen, da sie den Rahmen für diesen Artikel sprengen würde. Dazu zählen eine korrekte Fehlerbehandlung, erweiterte Protobuf-Sprachfeatures, gRPC-Web und vieles Weitere. Passend zu diesem Artikel hat der Autor eine kostenlose Livestreamaufzeichnung als Video auf YouTube [8] bereitgestellt, in der auch die nicht besprochenen Aspekte ausführlich gezeigt werden. Viel Spaß beim Ansehen!

Links & Literatur

[1] https://grpc.io/docs/what-is-grpc/faq/

[2] https://www.cncf.io/blog/2017/03/01/cloud-native-computing-foundation-host-grpc-google/

[3] https://www.twitch.tv/videos/731620698

[4] https://developers.google.com/protocol-buffers

[5] https://developers.google.com/protocol-buffers/docs/proto3

[6] https://developers.google.com/protocol-buffers/docs/style

[7] https://devblogs.microsoft.com/aspnet/grpc-web-for-net-now-available/

[8] https://youtu.be/QUdr0qbEKE4?list=PLbWYOayzrhS_WPoubt7PV0h2n1fOimI2j

Ihr aktueller Zugang zur .NET- und Microsoft-Welt.
Der BASTA! Newsletter:

Behind the Tracks

.NET Framework & C#
Visual Studio, TFS, C# & mehr

Agile & DevOps
Best Practices & mehr

Web Development
Alle Wege führen ins Web

Data Access & Storage
Alles rund um´s Thema Data

HTML5 & JavaScript
Leichtegewichtig entwickeln

User Interface
Alles rund um UI- und UX-Aspekte

Microservices & APIs
Services, die sich über APIs via REST und JavaScript nutzen lassen

Security
Tools und Methoden für sicherere Applikationen

Cloud & Azure
Cloud-basierte & Native Apps