在 C# 中使用 Consul 客户端库实现服务发现

在 C# 中使用 Consul 客户端库实现服务发现,主要通过查询 Consul 服务器获取指定服务的健康实例列表。以下是详细的实现步骤和代码示例:

实现步骤

  1. 安装 Consul 客户端库(已安装可跳过):

    复制代码
    dotnet add package Consul
  2. 核心逻辑

    • 创建 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}");
    }
}

关键说明

  1. 健康实例筛选passingOnly: true 参数确保只返回通过健康检查的服务实例,避免调用故障节点。

  2. 高可用配置:通过传入多个 Consul 服务器地址,可实现客户端的高可用(当一个服务器不可用时自动切换到其他服务器)。

  3. 负载均衡:示例中使用了简单的随机选择策略,实际项目中可根据需求实现轮询、权重、哈希等更复杂的负载均衡算法。

  4. 服务标签 :可通过 tag 参数筛选特定标签的服务实例(例如:只发现 version=v2 的服务)。

  5. 异常处理:代码包含了异常捕获逻辑,可根据实际需求扩展(如重试机制、降级策略等)。

通过这种方式,C# 应用可以动态发现并调用 Consul 中注册的服务,适配服务的动态扩缩容和故障转移场景。

相关代码

consul.demo资源-CSDN下载

相关推荐
_codemonster12 小时前
30分钟快速搭建 Spring Cloud Alibaba 微服务实战(一)
微服务·架构·毕业设计·课程设计
Dongwoo Jeong14 小时前
微服务架构(MSA)是如何诞生的?
微服务·云原生·架构
半旧夜夏14 小时前
【保姆级】微服务组件环境搭建(Docker Compose版)
java·linux·spring cloud·微服务·云原生·容器
西凉的悲伤1 天前
Spring Boot 、Spring Cloud 微服务架构认证授权方案
spring boot·spring cloud·微服务·架构·认证授权
苏渡苇1 天前
Seata 番外篇:使用 docker-compose 部署 Seata Server(TC)及 K8S 部署 Seata 高可用
spring boot·docker·微服务·容器·kubernetes·seata·springcloud
RingWu1 天前
高并发三板斧-异步
分布式·微服务·架构
雨辰AI2 天前
SpringBoot3 整合达梦 DM9 超详细入门实战|从零搭建可直接上线
数据库·微服务·架构·政务
Regentsoft丽晶软件3 天前
传统单体架构拖垮分销效率:2026品牌分销系统微服务化升级的价值拆解
微服务·云原生·架构
逻极3 天前
Go 从入门到精通:并发编程与云原生实践
微服务·云原生·go·并发
苏渡苇4 天前
强强联合:OpenFeign 整合 Sentinel
spring boot·spring cloud·微服务·sentinel·openfeign