host grpc services in .net core 3.0 worker service – code

Create Worker Service

Worker Service is a new project template in .net core 3.0, and BackgroundService worker will be created for long running services.

Here is the sample code:

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(1000, stoppingToken);
        }
    }
}

Also in .net core 3.0, Generic Host is recommended for both HTTP and non-HTTP workloads. BackgroundService will be hosted like this:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices(services =>
        {
            services.AddHostedService<Worker>();
        });

Later, gRPC service will also use Generic Host to host.

Host gRPC service in the worker BackgroundService

In .net core 3.0, there is also a new project template for gRPC service, which will create a new asp.net core project base one the project SDK: Microsoft.NET.Sdk.Web. For Work Service, the project SDK was Microsoft.NET.Sdk.Worker. It seems Microsoft.NET.Sdk.Web contain the Microsoft.NET.Sdk.Worker, and if this is a project that contain gRPC, project SDK need to be changed.

Here is a simple code for csproj file:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Grpc" Version="1.21.0" />
    <PackageReference Include="Grpc.AspNetCore.Server" Version="0.1.20-pre1" />
    <PackageReference Include="Google.Protobuf" Version="3.7.0" />
    <PackageReference Include="Grpc.Tools" Version="1.21.0" PrivateAssets="All" />
  </ItemGroup>
</Project>

gRPC service need to be hosted in the kestrel web host server, which can be created in the BackgroudService.

public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;

        public Worker(ILogger<Worker> logger)
        {
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            await Host.CreateDefaultBuilder()
                .ConfigureWebHostDefaults(builder =>
                {
                    builder
                        .ConfigureKestrel(options =>
                        {
                            options.ListenAnyIP(0, listenOptions =>
                            {
                                listenOptions.Protocols = HttpProtocols.Http2;
                            });
                        })
                        .UseKestrel()
                        .UseStartup<GrpcServerStartup>();
                })
                .Build()
                .StartAsync(stoppingToken);
        }
    }

    public class GrpcServerStartup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddGrpc();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGrpcService<GreeterService>();
            });
        }
    }

There are some useful code for special case:

Get the Kestrel server random port

After the Web Host server started, use this code to get the server address:

var address = host.Services.GetService<IServer>()
    .Features
    .Get<IServerAddressesFeature>()
    .Addresses
    .FirstOrDefault();

Config the gRPC service as Singleton

services.AddSingleton<GreeterService>();

Code base: https://github.com/celery94/GrpcHostServer