[英]Keep Dotnet Core Grpc Server running as a console application?
I am trying to keep a Grpc server running as a console daemon. 我试图让一台Grpc服务器作为控制台守护程序运行。 This gRPC server is a microservice that runs in a docker container.
这个gRPC服务器是一个在docker容器中运行的微服务。
All of the examples I can find make use of the following: 我能找到的所有例子都使用以下内容:
Console.ReadKey();
This does indeed block the main thread and keeps it running but does not work in docker with the following error: 这确实会阻塞主线程并使其保持运行但在docker中不起作用,并出现以下错误:
"Cannot read keys when either application does not have a console or when console input has been redirected. Try Console.Read."
Now I could probably try to find a workaround for docker specifically, but this still doesn't feel right. 现在我可能会尝试专门为docker找到解决方法,但这仍然感觉不对。 Does anyone know of a good "production ready" way to keep the service running?
有没有人知道一个良好的“生产就绪”方式来保持服务运行?
You can now use Microsoft.Extensions.Hosting
pacakge which is a hosting and startup infrastructures for both asp.net core and console application. 您现在可以使用
Microsoft.Extensions.Hosting
pacakge,它是asp.net核心和控制台应用程序的托管和启动基础结构。
Like asp.net core, you can use the HostBuilder API to start building gRPC host and setting it up. 与asp.net核心一样,您可以使用HostBuilder API开始构建gRPC主机并进行设置。 The following code is to get a console application that keeps running until it is stopped (for example using Control-C):
以下代码是为了让控制器应用程序一直运行直到它被停止(例如使用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. 要运行gRPC服务,您需要在托管服务中启动/停止
Grpc.Core.Server
。 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.
这在IHostedService接口中表示。 That's to say, implement a GrpcHostedService to override the interface:
也就是说,实现一个GrpcHostedService来覆盖接口:
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. 我们得到一个通过依赖注入注入的
GrpcHostedService
实例,并在启动主机时对其运行GrpcHostedService
。 When the host is stopped we run StopAsync so that we can gracefully shut everything down including Grpc server. 当主机停止时,我们运行StopAsync,以便我们可以优雅地关闭所有内容,包括Grpc服务器。
Then go back to the Program.cs
and make some changes: 然后返回
Program.cs
并进行一些更改:
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. 通过这样做,通用主机将在我们的托管服务上自动运行StartAsync,而后者将在
Server
实例上调用StartAsync,实质上是启动gRPC服务器。
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. 当我们使用Control-C关闭主机时,通用主机将自动在我们的托管服务上调用StopAsync,这将再次调用
Server
实例上的StopAsync,它将进行一些清理。
For other configuration in HostBuilder, you can see this blog . 对于HostBuilder中的其他配置,您可以看到此博客 。
Use ManualResetEvent
to block the main thread until you receive a shutdown event. 使用
ManualResetEvent
阻止主线程,直到收到关闭事件。
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();
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.