在 C# 中使用 Consul 客户端库实现服务发现,主要通过查询 Consul 服务器获取指定服务的健康实例列表。以下是详细的实现步骤和代码示例:
实现步骤
-
安装 Consul 客户端库(已安装可跳过):
dotnet add package Consul
-
核心逻辑:
- 创建 Consul 客户端连接
- 调用 Consul API 查询指定服务的健康实例
- 处理返回结果,提取可用服务地址
完整代码实现
cs
using Consul;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
/// <summary>
/// Consul 服务发现工具类
/// </summary>
public class ConsulServiceDiscoverer
{
private readonly ConsulClient _consulClient;
/// <summary>
/// 初始化服务发现器
/// </summary>
/// <param name="consulServerAddresses">Consul 服务器地址列表(支持集群)</param>
public ConsulServiceDiscoverer(params string[] consulServerAddresses)
{
if (consulServerAddresses == null || !consulServerAddresses.Any())
{
// 默认连接本地 Consul 服务
consulServerAddresses = new[] { "http://localhost:8500" };
}
_consulClient = new ConsulClient(config =>
{
config.Address = new Uri(consulServerAddresses.First());
// 如需配置多个 Consul 服务器(高可用),可添加以下代码
// config.SecondaryAddresses = consulServerAddresses.Skip(1).Select(uri => new Uri(uri)).ToList();
});
}
/// <summary>
/// 发现指定服务的所有健康实例
/// </summary>
/// <param name="serviceName">服务名称(必须与注册时一致)</param>
/// <param name="tag">筛选标签(null 表示不筛选)</param>
/// <returns>健康服务实例列表(地址+端口)</returns>
public async Task<List<ServiceInstance>> DiscoverHealthyServicesAsync(string serviceName, string tag = null)
{
try
{
// 调用 Consul API 查询健康服务实例
var queryResult = await _consulClient.Health.Service(
service: serviceName,
tag: tag,
passingOnly: true, // 只返回通过健康检查的实例
queryOptions: new QueryOptions { WaitTime = TimeSpan.FromSeconds(10) }
);
if (queryResult.Response == null || !queryResult.Response.Any())
{
throw new KeyNotFoundException($"未找到服务 {serviceName} 的健康实例");
}
// 转换为自定义服务实例对象
return queryResult.Response
.Select(s => new ServiceInstance
{
ServiceId = s.Service.ID,
ServiceName = s.Service.Service,
Address = s.Service.Address,
Port = s.Service.Port,
Tags = s.Service.Tags?.ToList() ?? new List<string>(),
FullAddress = $"{s.Service.Address}:{s.Service.Port}"
})
.ToList();
}
catch (Exception ex)
{
Console.WriteLine($"服务发现失败:{ex.Message}");
throw;
}
}
/// <summary>
/// 从服务实例列表中随机选择一个(简单负载均衡)
/// </summary>
public ServiceInstance SelectRandomInstance(List<ServiceInstance> instances)
{
if (instances == null || !instances.Any())
return null;
var random = new Random();
return instances[random.Next(instances.Count)];
}
}
/// <summary>
/// 服务实例模型
/// </summary>
public class ServiceInstance
{
public string ServiceId { get; set; } // 服务唯一ID
public string ServiceName { get; set; } // 服务名称
public string Address { get; set; } // 服务地址(IP或域名)
public int Port { get; set; } // 服务端口
public List<string> Tags { get; set; } // 服务标签(如版本、环境)
public string FullAddress { get; set; } // 完整地址(地址:端口)
}
使用示例
// 1. 创建服务发现器(可指定多个 Consul 服务器地址)
var discoverer = new ConsulServiceDiscoverer(
"http://consul-server1:8500",
"http://consul-server2:8500"
);
// 2. 发现指定服务的健康实例(例如:发现支付服务)
var paymentServices = await discoverer.DiscoverHealthyServicesAsync("PaymentService");
// 3. 从实例列表中选择一个进行调用(这里使用随机选择实现简单负载均衡)
var targetService = discoverer.SelectRandomInstance(paymentServices);
if (targetService != null)
{
Console.WriteLine($"选中的服务实例:{targetService.FullAddress}");
// 4. 调用服务(示例:使用 HttpClient 调用)
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync($"http://{targetService.FullAddress}/api/pay");
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine($"服务响应:{result}");
}
}
关键说明
-
健康实例筛选 :
passingOnly: true
参数确保只返回通过健康检查的服务实例,避免调用故障节点。 -
高可用配置:通过传入多个 Consul 服务器地址,可实现客户端的高可用(当一个服务器不可用时自动切换到其他服务器)。
-
负载均衡:示例中使用了简单的随机选择策略,实际项目中可根据需求实现轮询、权重、哈希等更复杂的负载均衡算法。
-
服务标签 :可通过
tag
参数筛选特定标签的服务实例(例如:只发现version=v2
的服务)。 -
异常处理:代码包含了异常捕获逻辑,可根据实际需求扩展(如重试机制、降级策略等)。
通过这种方式,C# 应用可以动态发现并调用 Consul 中注册的服务,适配服务的动态扩缩容和故障转移场景。
相关代码