Fabio 没有注册 ASP.NET Core gRPC

问题描述 投票:0回答:0

我有一个问题,Fabio 没有注册 gRPC 服务,即使它在 Consul 服务上变成绿色。

这是重现示例所需的所有代码,代码会很多,但大部分代码都可以复制粘贴,并且只是选项和依赖注入 (DI) 方法。

还有我的

.csproj
领事包裹

<PackageReference Include="Consul" Version="1.6.10.7" />

只需创建一个新的 ASP.NET Core gRPC 服务,并确保您调用

AddFabio
&
AddConsul
作为
Services
.

从配置绑定的选项

public sealed class ConsulOptions
{
    public bool Enabled { get; set; }
    public bool IsGrpc { get; set; }
    public string Url { get; set; } = string.Empty;
    public ServiceRegistration Service { get; set; } = new();
    public HealthCheckRegistration HealthCheck { get; set; } = new();

    public sealed class ServiceRegistration
    {
        public string Name { get; set; } = string.Empty;
        public string Url { get; set; } = string.Empty;
    }

    public sealed class HealthCheckRegistration
    {
        public string Endpoint { get; set; } = string.Empty;
        public TimeSpan? Interval { get; set; }
        public TimeSpan? DeregisterInterval { get; set; }
    }
}

public sealed class FabioOptions
{
    public bool Enabled { get; set; }
    public string Url { get; set; } = string.Empty;
}

领事注册服务

public class ConsulRegistrationService : IHostedService
{
    private readonly IConsulClient _client;
    private readonly ILogger<ConsulRegistrationService> _logger;
    private readonly ConsulOptions _options;
    private readonly IServiceDiscoveryRegistration _serviceDiscoveryRegistration;
    private readonly string _serviceId;
    private readonly string _serviceName;
    private readonly Uri _serviceUrl;

    public ConsulRegistrationService(IConsulClient client,
        IServiceDiscoveryRegistration serviceDiscoveryRegistration,
        ILogger<ConsulRegistrationService> logger,
        IOptions<ConsulOptions> options)
    {
        if (string.IsNullOrWhiteSpace(options.Value.Service.Name))
            throw new ArgumentException("Service name is required.", nameof(options.Value.Service.Name));

        if (string.IsNullOrWhiteSpace(options.Value.Service.Url))
            throw new ArgumentException("Service url is required.", nameof(options.Value.Service.Url));

        _client = client;
        _serviceDiscoveryRegistration = serviceDiscoveryRegistration;
        _logger = logger;
        _options = options.Value;
        _serviceUrl = new Uri(options.Value.Service.Url);
        _serviceName = options.Value.Service.Name;
        _serviceId = $"{_serviceName}-{Guid.NewGuid():N}";
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation($"Registering a service: '{_serviceId}' in Consul...");
        var result = await _client.Agent.ServiceRegister(new AgentServiceRegistration
        {
            ID = _serviceId,
            Name = _serviceName,
            Address = _serviceUrl.Host,
            Port = _serviceUrl.Port,
            Tags = _serviceDiscoveryRegistration.Tags.ToArray(),
            Check = AgentServiceCheck,
        }, cancellationToken);
        
        if (result.StatusCode == HttpStatusCode.OK)
        {
            _logger.LogInformation($"Registered a service: '{_serviceId}' in Consul.");
            return;
        }

        _logger.LogError(
            $"There was an error: {result.StatusCode} when registering a service: '{_serviceId}' in Consul.");
    }
    
    private AgentServiceCheck AgentServiceCheck => _options.IsGrpc
        ? new AgentServiceCheck
        {
            GRPC = _serviceUrl.ToString(),
            GRPCUseTLS = false,
            Name = _serviceId,
            Interval = _options.HealthCheck.Interval,
            DeregisterCriticalServiceAfter = _options.HealthCheck.DeregisterInterval,
            Timeout = TimeSpan.FromSeconds(10)
        }
        : new AgentServiceCheck
        {
            HTTP = $"{_serviceUrl}{_options.HealthCheck.Endpoint}",
            Interval = _options.HealthCheck.Interval,
            DeregisterCriticalServiceAfter = _options.HealthCheck.DeregisterInterval,
            Timeout = TimeSpan.FromSeconds(10)
        };
        // Also a stop which we do not care
}

appsettings.json

"consul": {
    "enabled": true,
    "isGrpc": true,
    "url": "http://localhost:8500",
    "service": {
      "name": "event-grpc",
      "url": "host.docker.internal:4081" // This might need to be updated i have the grpc service listening on port 4081
    },
    "healthCheck": {
      "endpoint": "",
      "interval": "00.00:00:10",
      "deregisterInterval": "00.00:00:10"
    }
  },
  "fabio": {
    "enabled": true,
    "url": "http://localhost:9999"
  },
internal class DefaultServiceDiscoveryRegistration : IServiceDiscoveryRegistration
{
    public IEnumerable<string> Tags { get; } = Enumerable.Empty<string>();
}

public interface IServiceDiscoveryRegistration
{
    IEnumerable<string> Tags { get; }
}

DI 用于添加领事

private static IServiceCollection AddConsul(this IServiceCollection services, IConfiguration configuration)
    {
        var section = configuration.GetSection("consul");
        var options = section.BindOptions<ConsulOptions>();
        services.Configure<ConsulOptions>(section);
        if (!options.Enabled) return services;

        if (string.IsNullOrWhiteSpace(options.Url))
            throw new ArgumentException("Consul URL cannot be empty.", nameof(options.Url));

        services.AddHostedService<ConsulRegistrationService>();
        
     
        services.AddSingleton<IServiceDiscoveryRegistration,DefaultServiceDiscoveryRegistration>();
        services.AddSingleton<IConsulClient>(new ConsulClient(consulConfig =>
        {
            consulConfig.Address = new Uri(options.Url);
        }));

        return services;
    }

这里是 fabio 的配置

internal sealed class FabioServiceDiscoveryRegistration : IServiceDiscoveryRegistration
{
    public FabioServiceDiscoveryRegistration(IOptions<ConsulOptions> options)
    {
        var serviceName = options.Value.Service.Name;
        bool isGrpc = options.Value.IsGrpc;

        if (isGrpc)
        {
            Tags = new []{$"urlprefix-/{serviceName} proto=grpc strip=/{serviceName}"};
        }
        else
        {
            Tags = new[] { $"urlprefix-/{serviceName}" };    
        }
         
    }

    public IEnumerable<string> Tags { get; }
}

法比奥的DI

private static IServiceCollection AddFabio(this IServiceCollection services, IConfiguration configuration)
    {
        var section = configuration.GetSection("fabio");
        var options = section.BindOptions<FabioOptions>();
        services.Configure<FabioOptions>(section);

        if (!options.Enabled) return services;

        if (string.IsNullOrWhiteSpace(options.Url))
            throw new ArgumentException("Fabio URL cannot be empty.", nameof(options.Url));

        services.AddSingleton<IServiceDiscoveryRegistration, FabioServiceDiscoveryRegistration>();

        return services;
    }

最后是 docker-compose

version: '3.7'

services:
  consul:
    image: consul
    container_name: consul
    restart: unless-stopped
    networks:
      - odin
    ports:
      - 8500:8500
    volumes:
      - consul:/consul/data
  
  fabio:
    image: fabiolb/fabio
    container_name: fabio
    restart: unless-stopped
    environment:
      - FABIO_REGISTRY_CONSUL_ADDR=consul:8500
    networks:
      - odin
    ports:
      - 9998:9998
      - 9999:9999
  

在本地运行您的服务后,您将在 consul 上获得以下图像

但是 fabio 上没有任何内容,但我从 Web Api 的相同配置开始,然后我可以看到它已正确注册。

c# docker consul grpc-c# fabio
© www.soinside.com 2019 - 2024. All rights reserved.