22

I have created a GRPC Server in C# using the example given at Link. Now I want to figure out as how should I be hosting this server so that I achieve following:

  • Should I make this Server a Console application or a a Windows Service. If I make it a windows Service then updating the service will be cumbersome (which is a big negative) and if I make it a console app then updating will simply need shutting down exe. But that comes with the price of closing the same by mistake. Is there any other better way?
  • With IIS this issue won't b there as I can simply remove the site from LB and stop the website to perform the update but since GRPC won't be a part of IIS, I am not sure what's the way to get this working.

Any references for the better architecture are welcomed.

GawdePrasad
  • 623
  • 1
  • 7
  • 15
  • 2
    Given the nature of the system it sounds like it should be _reliable_ and somewhat _fault-tolerant_. That's a trait one does not normally associate with _console apps_. A big negative. Windows Services on the other hand are _recoverable_. A big positive. –  Feb 03 '16 at 12:25

4 Answers4

23

We can use Microsoft.Extensions.Hosting pacakge to host a .net core console application by using the HostBuilder API to start building gRPC host and setting it up.

In order to run the gRPC service, we first need to start/stop Grpc.Core.Server in a hosted service. A hosted service is basically a piece of code that is run by the host when the host itself is started and the same for when it is stopped. The following code implement a GrpcHostedService to override IHostedService interface:

using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Microsoft.Extensions.Hosting;

namespace Grpc.Host
{
    public class GrpcHostedService: IHostedService
    {
        private Server _server;

        public GrpcHostedService(Server server)
        {
            _server = server;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _server.Start();
            return Task.CompletedTask;
        }

        public async Task StopAsync(CancellationToken cancellationToken) => await _server.ShutdownAsync();
    }
}

In the Program.cs, use HostBuilder API to start building our grpc host and setting it up:

public class Program
{
    public static async Task Main(string[] args)
    {
        var hostBuilder = new HostBuilder()
             // Add configuration, logging, ...
            .ConfigureServices((hostContext, services) =>
            {
                // Better to use Dependency Injection for GreeterImpl
                Server server = new Server
                {
                    Services = {Greeter.BindService(new GreeterImpl())},
                    Ports = {new ServerPort("localhost", 5000, ServerCredentials.Insecure)}
                };
                services.AddSingleton<Server>(server);
                services.AddSingleton<IHostedService, GrpcHostedService>();
            });

        await hostBuilder.RunConsoleAsync();
    }
}

By doing this, the generic host will automatically run StartAsync on our hosted service, which in turn will call StartAsync on the Server instance, essentially start the gRPC server.

When we shut down the host with Control-C, the generic host will automatically call StopAsync on our hosted service, which again will call StopAsync on the Server instance which will do some clean up.

For other configuration in HostBuilder, you can see this blog.

Feiyu Zhou
  • 4,344
  • 32
  • 36
  • Hi, thank you for the answer. Can you please elaborate how to do your commented suggestion of "// Better to use Dependency Injection for GreeterImpl"? – Jens Caasen May 25 '20 at 11:44
  • DI can reduce tight coupling and it's better to use unified design pattern in one project. – Feiyu Zhou May 26 '20 at 02:45
  • It might just be me, but I cannot for the life of me figure out how to Inject the greeter service into services. This is done in ConfigureServices, so the DI container is not built... right? – Rouse02 Aug 04 '20 at 04:35
4

I'm going to add one more option.

With dot net core, you can run this as a Linux Daemon now.

frostymarvelous
  • 2,786
  • 32
  • 43
3

Currently gRPC doesn't support integration with ASP.Net/IIS. You would need to host the server in a console or as a Windows service.

Likely you would want this to be a Windows service to make it easier to keep the server running across reboots or crashes. If you want to easily turn your console application into a Windows service I would recommend using the excellent TopShelf Nuget.

Updating the service can be done as you would a console app.

  • Stop the Windows service. net stop <service-name}>
  • Copy the updated assemblies.
  • Start the Windowsservice net start <service-name>
KevM
  • 2,496
  • 1
  • 22
  • 25
  • 1
    Great news! Coming in aspnetcore 3.0 gRPC has first party support and they Sherlocked TopShelf with Worker Services. – KevM May 14 '19 at 16:11
3

My company (Shortbar) is building the application server for a hotel management system called HOLMS on gRPC. Our setup is as follows:

  • HOLMS.Application is a .NET class library (assembly) that does the actual work of the server
  • HOLMS.Application.ConsoleRunner is a C# console application that hosts HOLMS.Application. The console runner is used by (1) developers for convenience (mentioned in the question) as well as (2) production scenarios running inside a Docker container, where the container runtime (e.g. Amazon ECS) implements job control/scaling. It follows "12 factor app" guidelines, including running itself as a single, standalone, stateless process, fast startup/shutdown, and environment-variable config injection. The system logs to stdout which gets drained however stdout is drained in the prod environment (e.g. Sumo, logstash, etc). This is how our SaaS multi-tenant solution will go into production.
  • HOLMS.Application.ServiceRunner packages HOLMS.Application into a Windows service, for more traditional, on-premise situations where a customer's IT group will run the service themselves. This package uses the Windows registry for configuration and relies on Windows service job control for startup/shutdown/restarts. It logs to the Windows Event Log.

The ConsoleRunner and ServiceRunner each are only about 200 lines of code; for the most part, they just wrap the Application package, and call into it.

Hope this helps.

David Albrecht
  • 143
  • 1
  • 1
  • 5