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

相关推荐
3Cloudream5 小时前
互联网大厂Java面试深度解析:从基础到微服务云原生的全场景模拟
java·spring boot·redis·elasticsearch·微服务·kafka·电商架构
天上掉下来个程小白5 小时前
微服务-27.配置管理-什么是配置管理
运维·微服务·架构
叫我阿柒啊5 小时前
Java全栈开发工程师的面试实战:从基础到微服务
java·数据库·spring boot·微服务·node.js·vue3·全栈开发
lisw058 小时前
医学冥思:生物技术的下一次革命
人工智能·微服务·学习方法·冥想
桃酥4038 小时前
聊一聊 单体分布式 和 微服务分布式
分布式·微服务·架构
孤狼程序员13 小时前
【Spring Cloud 微服务】5.架构的智慧枢纽:深度剖析 Nacos 注册中心
spring cloud·微服务·架构
老顾聊技术13 小时前
网关如何聚合各个微服务的接口文档?
微服务·api
阿里云云原生13 小时前
使用 MSE 流量防护轻松面对运行态流量不确定风险的最佳实践
微服务
亦安✘14 小时前
服务器从0到1微服务所需的环境的安装
运维·服务器·spring cloud·微服务