One of the alternatives recommended by Microsoft
for organizations looking for a migration path away from WCF
on .NET Framework is gRPC: a low-overhead, high-performance, cross-platform RPC framework.
The upcoming .NET Core 3.0 has first-class support for gRPC; out of the box, you can create a new project with
dotnet new grpc.
This creates an ASP.NET Core 3.0 project with the usual
Program.cs (remember, ASP.NET Core apps are console apps)
Startup.cs, plus an example gRPC service called Greet.
There are two new things in this project: a
greet.proto file in the
Protos folder, and a
Services folder. Let’s take a look at them.
This is the Protobuf Interface Definition Language (IDL), a custom language for specifying data structures and service contracts. The first line tells whatever tooling you’re using that you’re using v3 of the language; the default is v2. The second line provides a name for the generated package; in .NET Core this will be used as the namespace for the generated code.
Then we have the actual definitions: a
service and two
service is like the
[ServiceContract] interface in a WCF project; it’s a declaration of the methods (or endpoints) that the
service will have. Each method is declared using the
rpc token, then the name of the method, the parameter type, and the return type.
gRPC methods can only have a single parameter, which must be a known
message type. You can’t just use
message is like the
[DataContract] types in WCF; it declares a data transport object (DTO). Each field has a type, a name,
and an index, which starts at
1. As you add new fields, you increment the index.
proto file is used to generate all the gRPC plumbing code, including data objects and their Protobuf serialization
implementations, and base classes for your services. It may seem strange to declare these things in a separate text file if
you’re used to working in WCF using C# or VB, but there are code generators for almost every language and platform currently
in use. Once you’ve got your
Go, Rust and more.
And trust me: you don’t want to see the code that it generates. Let’s just say it prioritizes performance over beauty.
This is where you write your implementation code. The
Greeter.GreeterBase base class is generated by Protobuf, which is
integrated with MSBuild so you don’t need to install anything beyond the
NuGet packages. That base class defines virtual methods for all the declared
rpc methods in the
proto file, with request
and response types that have also been automatically generated. (If you’re following along and wondering where the code for
all these classes is, it’s in the
obj folder in your project.)
ServerCallContext is the gRPC equivalent of
HttpContext in an ASP.NET Core MVC application. There you can access
authentication information, headers, and other metadata about the call. As with a WCF contract, you simply handle the request
and return a response object.
The ASP.NET Core
Startup class is responsible for the application setup, similar to
Global.asax in an ASP.NET application.
Here we register the gRPC services with the Dependency Injection provider (line 5), and add specific services with the
UseEndpoints call (lines 12-15). We can now run this application from the command line on Windows or Linux, or in IIS
on Windows, and connect to it from any client application that has used the same
.proto file to generate client access
You can drop a
.proto file in a .NET Core 3.0 client application with the
Grpc.Tools NuGet packages
and it will generate a client class for accessing the server, much like generating client classes using “Add Service Reference”
in a .NET application. Here’s an example of a simple console application using the client class generated from the
UPDATE: These tests were run using the basic HTTP binding for WCF, which is not the most appropriate binding to compare to gRPC. This follow-up post shows a more representative comparison, using NetTCP binding for WCF (and also a modified version of the gRPC solution).
So how does gRPC perform compared to WCF? Here are the results from BenchmarkDotNet comparing a sample WCF service to an equivalent .NET Core 3.0 gRPC service, which was generated using an automated conversion tool currently in development.
The WCF ServiceContract implementation:
Auto-converted code for gRPC:
Auto-generated gRPC wrapper code:
Note that in this case, because the original
GetDataStream method returned an
IEnumerable, the conversion tool
created a method with a
stream response, which lets gRPC sends responses asynchronously. The wrapper code takes care
of running this from the original
IEnumerable-returning method from the WCF code.
Benchmarking these two implementations with the auto-generated client code for each server gives the following results:
|Method||Mean||Error||StdDev||Gen 0||Gen 1||Gen 2||Allocated|
|gRPC||0.971 ms||18.39 us||26.37 us||3.9063||-||-||1.48 KB|
|WCF||1.406 ms||0.0278 ms||0.0390 ms||3.9063||-||-||19.38 KB|
So it looks like gRPC is about 33% faster than WCF, and allocates nearly 95% less memory, which is a pretty good win for an auto-converted project.
Again, please refer to this post for a more accurate comparison using WCF NetTCP binding.
Here’s a video showing the conversion tool (working title “Uplift” may change before release) in use in VS2019, and the benchmarks running: