Keep Dotnet Core Grpc Server running as a console application?

后端 未结 2 1436
深忆病人
深忆病人 2021-02-13 14:11

I am trying to keep a Grpc server running as a console daemon. This gRPC server is a microservice that runs in a docker container.

All of the examples I can find make us

相关标签:
2条回答
  • 2021-02-13 14:50

    You can now use Microsoft.Extensions.Hosting pacakge which is a hosting and startup infrastructures for both asp.net core and console application.

    Like asp.net core, you can use the HostBuilder API to start building gRPC host and setting it up. The following code is to get a console application that keeps running until it is stopped (for example using Control-C):

    using System;
    using System.Threading.Tasks;
    using Microsoft.Extensions.Hosting;
    
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var hostBuilder = new HostBuilder();
    
             // register your configuration and services.
            ....
    
            await hostBuilder.RunConsoleAsync();
        }
    }
    

    In order to run the gRPC service, you 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. This is represented in the IHostedService interface. That's to say, implement a GrpcHostedService to override the 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();
        }
    }
    

    It's simple really. We get an GrpcHostedService instance injected through dependency injection and run StartAsync on it when host is started. When the host is stopped we run StopAsync so that we can gracefully shut everything down including Grpc server.

    Then go back to the Program.cs and make some changes:

    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.

    0 讨论(0)
  • 2021-02-13 14:58

    Use ManualResetEvent to block the main thread until you receive a shutdown event.

    For example in a trivial situation:

    class Program
    {
      public static ManualResetEvent Shutdown = new ManualResetEvent(false);
    
      static void Main(string[] args)
      {
        Task.Run(() =>
        {
          Console.WriteLine("Waiting in other thread...");
          Thread.Sleep(2000);
          Shutdown.Set();
        });
    
        Console.WriteLine("Waiting for signal");
        Shutdown.WaitOne();
    
        Console.WriteLine("Resolved");
      }
    }
    

    For example, in your case, I imagine something like:

    using System;
    using System.Net.Sockets;
    using System.Threading;
    using System.Threading.Tasks;
    using Grpc.Core;
    using Helloworld;
    
    namespace GreeterServer
    {
      class GreeterImpl : Greeter.GreeterBase
      {
        // Server side handler of the SayHello RPC
        public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
        {
          Program.Shutdown.Set(); // <--- Signals the main thread to continue 
          return Task.FromResult(new HelloReply {Message = "Hello " + request.Name});
        }
      }
    
      class Program
      {
        const int Port = 50051;
    
        public static ManualResetEvent Shutdown = new ManualResetEvent(false);
    
        public static void Main(string[] args)
        {
          Server server = new Server
          {
            Services = {Greeter.BindService(new GreeterImpl())},
            Ports = {new ServerPort("localhost", Port, ServerCredentials.Insecure)}
          };
          server.Start();
    
          Shutdown.WaitOne(); // <--- Waits for ever or signal received
    
          server.ShutdownAsync().Wait();
        }
      }
    }
    
    0 讨论(0)
提交回复
热议问题