在微服务架构中,服务实例的地址和端口往往是动态变化的,传统的硬编码服务地址的方式已经无法满足需求,服务发现机制应运而生。Consul是一款开源的服务网格解决方案,提供了服务发现、配置管理、健康检查等核心功能,能够很好地适配C#技术栈的微服务开发场景。

Consul核心概念
在使用C#集成Consul之前,需要先了解几个核心概念:
- 服务注册:服务实例启动时向Consul注册自身的信息,包括服务名称、地址、端口等
- 健康检查:Consul会定期向服务实例发送健康检查请求,判断服务是否可用
- 服务发现:服务消费者通过Consul查询可用的服务实例列表,获取服务地址进行调用
- 服务注销:服务实例关闭时主动向Consul注销自身信息,避免消费者调用到已下线的服务
环境准备
首先需要安装并启动Consul服务端,这里以开发环境为例:
- 从Consul官网下载对应系统的安装包,解压后得到consul可执行文件
- 打开终端执行命令启动开发模式:
consul agent -dev -client=0.0.0.0 - 访问
http://127.0.0.1:8500可以打开Consul的Web管理界面,确认服务启动成功
接下来创建两个C#项目,一个是服务提供者项目,一个是服务消费者项目,都使用.NET 6及以上版本,通过NuGet安装Consul客户端包:
// 在NuGet包管理器中搜索安装 Consul 包,或者使用命令行安装 // Install-Package Consul
服务提供者实现
服务提供者需要完成服务注册和健康检查配置,完整代码如下:
using Consul;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// 配置服务注册信息
var serviceName = "order-service";
var serviceAddress = "127.0.0.1";
var servicePort = 5001;
// 应用启动时注册服务到Consul
app.Lifetime.ApplicationStarted.Register(() =>
{
using var consulClient = new ConsulClient(c =>
{
// Consul服务端地址
c.Address = new Uri("http://127.0.0.1:8500");
});
var registration = new AgentServiceRegistration
{
ID = $"{serviceName}-{serviceAddress}:{servicePort}",
Name = serviceName,
Address = serviceAddress,
Port = servicePort,
// 配置健康检查,每10秒检查一次,超时时间5秒,失败3次后标记为不健康
Check = new AgentServiceCheck
{
HTTP = $"http://{serviceAddress}:{servicePort}/health",
Interval = TimeSpan.FromSeconds(10),
Timeout = TimeSpan.FromSeconds(5),
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(30)
}
};
consulClient.Agent.ServiceRegister(registration).Wait();
Console.WriteLine($"服务 {serviceName} 注册到Consul成功");
});
// 应用停止时注销服务
app.Lifetime.ApplicationStopping.Register(() =>
{
using var consulClient = new ConsulClient(c =>
{
c.Address = new Uri("http://127.0.0.1:8500");
});
consulClient.Agent.ServiceDeregister($"{serviceName}-{serviceAddress}:{servicePort}").Wait();
Console.WriteLine($"服务 {serviceName} 从Consul注销成功");
});
// 健康检查接口
app.MapGet("/health", () => Results.Ok("healthy"));
// 业务接口
app.MapGet("/orders", () =>
{
return new[] { new { OrderId = 1, OrderName = "测试订单1" }, new { OrderId = 2, OrderName = "测试订单2" } };
});
app.Run($"http://{serviceAddress}:{servicePort}");
启动这个服务提供者项目后,打开Consul的Web管理界面,在Services标签页可以看到order-service已经成功注册,并且健康检查状态为通过。
服务消费者实现
服务消费者需要从Consul查询可用的服务实例,然后发起调用,完整代码如下:
using Consul;
using System.Text.Json;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// 查询服务并调用的接口
app.MapGet("/get-orders", async () =>
{
using var consulClient = new ConsulClient(c =>
{
c.Address = new Uri("http://127.0.0.1:8500");
});
// 查询order-service的所有健康实例
var services = await consulClient.Health.Service("order-service", null, true);
if (services.Response.Length == 0)
{
return Results.NotFound("没有可用的order-service实例");
}
// 简单负载均衡,取第一个可用实例
var serviceEntry = services.Response[0];
var serviceAddress = serviceEntry.Service.Address;
var servicePort = serviceEntry.Service.Port;
var requestUrl = $"http://{serviceAddress}:{servicePort}/orders";
// 调用服务接口
using var httpClient = new HttpClient();
var response = await httpClient.GetAsync(requestUrl);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var orders = JsonSerializer.Deserialize<object>(content);
return Results.Ok(orders);
}
return Results.StatusCode((int)response.StatusCode);
});
app.Run("http://127.0.0.1:5002");
启动服务消费者项目,访问http://127.0.0.1:5002/get-orders,可以看到成功返回了服务提供者返回的订单数据,说明服务发现功能已经正常运行。
常见问题与优化
健康检查配置优化
实际生产环境中,健康检查的接口可以结合项目的实际业务状态,比如检查数据库连接、依赖服务状态等,而不是简单的返回成功。同时可以根据服务的实际负载调整检查间隔和超时时间。
负载均衡优化
上面的案例只是简单取了第一个可用实例,实际场景中可以实现轮询、随机等负载均衡策略,比如:
// 随机负载均衡示例 var random = new Random(); var serviceEntry = services.Response[random.Next(services.Response.Length)];
服务注销注意事项
除了应用停止时主动注销服务,还需要配置DeregisterCriticalServiceAfter参数,当服务连续健康检查失败超过指定时间后,Consul会自动注销该服务,避免消费者调用到故障实例。
总结
通过上面的步骤,我们完成了C#项目集成Consul实现服务发现的完整流程,包括服务注册、健康检查、服务查询和调用。Consul的C#客户端使用起来比较简单,只需要几行代码就可以完成核心功能的集成,非常适合在.NET微服务项目中快速落地服务发现能力。实际开发中可以根据项目的规模调整Consul的部署模式,比如生产环境使用集群模式保证高可用。