【问题标题】:How should a GRPC Service be hosted?应该如何托管 GRPC 服务?
【发布时间】:2016-05-12 14:14:32
【问题描述】:

我使用Link 中给出的示例在C# 中创建了一个GRPC 服务器。现在我想弄清楚我应该如何托管这个服务器,以便实现以下目标:

  • 我应该将此服务器设置为控制台应用程序还是 Windows 服务。如果我将其设为 Windows 服务,那么更新服务将会很麻烦(这是一个很大的负面影响),如果我将其设为控制台应用程序,那么更新将只需要关闭 exe。但这伴随着错误地关闭相同的价格。还有其他更好的方法吗?
  • 使用 IIS 不会出现此问题,因为我可以简单地从 LB 中删除该网站并停止该网站以执行更新,但由于 GRPC 不会成为 IIS 的一部分,我不确定如何获取这工作。

欢迎任何关于更好架构的参考。

【问题讨论】:

  • 鉴于系统的性质,它听起来应该是可靠并且有点容错。这是一个通常不会与控制台应用程序相关联的特征。一个很大的负面。另一方面,Windows 服务是可恢复的。一个很大的积极因素。

标签: c# grpc


【解决方案1】:

目前 gRPC 不支持与 ASP.Net/IIS 集成。您需要将服务器托管在控制台中或作为 Windows 服务。

您可能希望这是一项 Windows 服务,以便更轻松地保持服务器在重新启动或崩溃时运行。如果您想轻松地将控制台应用程序转变为 Windows 服务,我建议您使用出色的 TopShelf Nuget。

可以像更新控制台应用一样更新服务。

  • 停止 Windows 服务。 net stop <service-name}>
  • 复制更新的程序集。
  • 启动Windows服务net start <service-name>

【讨论】:

  • 好消息!进入 aspnetcore 3.0 gRPC 有第一方支持,他们用 Worker 服务 Sherlock TopShelf。
【解决方案2】:

我的公司(Shortbar)正在 gRPC 上为名为 HOLMS 的酒店管理系统构建应用程序服务器。我们的设置如下:

  • HOLMS.Application 是一个执行服务器实际工作的 .NET 类库(程序集)
  • HOLMS.Application.ConsoleRunner 是托管 HOLMS.Application 的 C# 控制台应用程序。 (1) 开发人员为了方便(在问题中提到)以及 (2) 在 Docker 容器内运行的生产场景使用控制台运行程序,其中容器运行时(例如 Amazon ECS)实现作业控制/缩放。它遵循“12 因素应用程序”指南,包括将自身作为单个、独立、无状态的进程运行、快速启动/关闭和环境变量配置注入。系统记录到标准输出,标准输出被耗尽,但标准输出在 prod 环境中被耗尽(例如 Sumo、logstash 等)。这就是我们的 SaaS 多租户解决方案投入生产的方式。
  • HOLMS.Application.ServiceRunner 将 HOLMS.Application 打包到 Windows 服务中,适用于客户的 IT 团队将自行运行服务的更传统的内部部署情况。此软件包使用 Windows 注册表进行配置,并依赖 Windows 服务作业控制进行启动/关闭/重启。它记录到 Windows 事件日志中。

ConsoleRunner 和 ServiceRunner 各只有大约 200 行代码;在大多数情况下,他们只是包装 Application 包,然后调用它。

希望这会有所帮助。

【讨论】:

    【解决方案3】:

    我要再添加一个选项。

    使用 dot net core,您现在可以将其作为 Linux 守护程序运行。

    【讨论】:

      【解决方案4】:

      我们可以使用Microsoft.Extensions.Hosting pacakge 来托管 .net 核心控制台应用程序,方法是使用 HostBuilder API 开始构建 gRPC 主机并进行设置。

      为了运行 gRPC 服务,我们首先需要在托管服务中启动/停止Grpc.Core.Server。托管服务基本上是在主机本身启动时由主机运行的一段代码,在它停止时也是如此。以下代码实现了一个GrpcHostedService 来覆盖IHostedService 接口:

      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();
          }
      }
      

      Program.cs 中,使用 HostBuilder API 开始构建我们的 grpc 主机并进行设置:

      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();
          }
      }
      

      通过这样做,通用主机将自动在我们的托管服务上运行 StartAsync,这反过来又会在 Server 实例上调用 StartAsync,实质上是启动 gRPC 服务器。

      当我们使用 Control-C 关闭主机时,通用主机将自动在我们的托管服务上调用 StopAsync,该服务将再次在 Server 实例上调用 StopAsync,这将进行一些清理。

      HostBuilder中的其他配置可以看这个blog

      【讨论】:

      • 您好,谢谢您的回答。您能否详细说明您对“// Better to use Dependency Injection for GreeterImpl”的评论建议?
      • DI可以减少紧耦合,最好在一个项目中使用统一设计模式。
      • 可能只是我,但我无法终生弄清楚如何将欢迎服务注入服务。这是在 ConfigureServices 中完成的,所以没有构建 DI 容器……对吗?
      猜你喜欢
      • 2021-12-18
      • 2021-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-25
      • 2022-10-15
      • 1970-01-01
      相关资源
      最近更新 更多