我有一个问题,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 的相同配置开始,然后我可以看到它已正确注册。