在 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下载

相关推荐
米丘3 天前
微前端之 Web Components 完全指南
微服务·html
霸道流氓气质5 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
霸道流氓气质6 天前
Spring Boot 微服务性能优化完全指南
spring boot·微服务·性能优化
地瓜伯伯6 天前
从MESI缓存一致性协议讲透synchronized的底层
java·spring boot·spring·spring cloud·微服务·springcloud
Devin~Y6 天前
大厂 Java 面试实录:从音视频内容社区到 AI RAG 的全链路技术设计
java·spring boot·redis·spring cloud·微服务·kafka·音视频
递归尽头是星辰6 天前
AI 访问数据仓库:从直连到微服务化
数据仓库·人工智能·微服务·dataagent·ai数据治理
就改了6 天前
Windows 环境 SkyWalking 完整实操教程
windows·微服务·skywalking
至乐活着7 天前
Docker Compose多服务编排实战:从零搭建Node.js+MySQL+Redis全栈应用
docker·微服务·devops·容器编排·compose
就改了7 天前
微服务异步场景链路断裂完整解决方案
微服务·云原生·架构
山东点狮信息科技有限公司7 天前
点狮OA-企业级 OA 办公自动化系统架构设计与实践
spring cloud·微服务·性能优化·架构·系统架构