Consul 服务发现与注册
大约 13 分钟约 3883 字
Consul 服务发现与注册
简介
Consul 是 HashiCorp 公司开源的分布式服务发现与配置管理系统。它提供服务注册、服务发现、健康检查、Key/Value 存储、多数据中心支持等功能。在微服务架构中,Consul 是解决"服务在哪里"和"服务是否健康"这两个核心问题的关键组件。ASP.NET Core 通过 Consul API 实现服务的自动注册与发现。
特点
Consul 架构
| 组件 | 说明 |
|---|---|
| Agent | 运行在每个节点上的守护进程,分为Server和Client两种模式 |
| Server | 参与Raft选举、存储数据、响应查询,建议3-5个节点组成集群 |
| Client | 转发请求到Server,无状态,轻量级 |
| LAN Gossip | 同一局域网内的Agent间通信 |
| WAN Gossip | 不同数据中心的Server间通信 |
Docker 安装 Consul
单节点开发环境
# 拉取镜像
docker pull consul:1.15
# 启动单节点(开发模式)
docker run -d --name consul-dev \
-p 8500:8500 \
-p 8600:8600/udp \
consul:1.15 agent -dev -client=0.0.0.0
# 访问管理界面
# http://localhost:8500生产集群部署
# 创建Docker网络
docker network create consul-net
# 启动 Server 节点1
docker run -d --name consul-server-1 \
--network consul-net \
-p 8500:8500 \
consul:1.15 agent -server -bootstrap-expect=3 \
-node=server-1 -client=0.0.0.0 \
-retry-join=server-2 -retry-join=server-3
# 启动 Server 节点2
docker run -d --name consul-server-2 \
--network consul-net \
consul:1.15 agent -server \
-node=server-2 -client=0.0.0.0 \
-retry-join=server-1 -retry-join=server-3
# 启动 Server 节点3
docker run -d --name consul-server-3 \
--network consul-net \
consul:1.15 agent -server \
-node=server-3 -client=0.0.0.0 \
-retry-join=server-1 -retry-join=server-2
# 启动 Client 节点
docker run -d --name consul-client-1 \
--network consul-net \
consul:1.15 agent -node=client-1 \
-retry-join=server-1Docker Compose 方式
# docker-compose.yml
version: '3.8'
services:
consul-server-1:
image: consul:1.15
command: agent -server -bootstrap-expect=3 -node=server-1 -client=0.0.0.0 -retry-join=consul-server-2 -retry-join=consul-server-3
ports:
- "8500:8500"
- "8600:8600/udp"
networks:
- consul-net
consul-server-2:
image: consul:1.15
command: agent -server -node=server-2 -client=0.0.0.0 -retry-join=consul-server-1 -retry-join=consul-server-3
networks:
- consul-net
consul-server-3:
image: consul:1.15
command: agent -server -node=server-3 -client=0.0.0.0 -retry-join=consul-server-1 -retry-join=consul-server-2
networks:
- consul-net
networks:
consul-net:
driver: bridgeNuGet 包
# Consul 官方客户端
dotnet add package Consul
# 健康检查集成(可选)
dotnet add packageAspNetCore.HealthChecks.UI.Client服务注册
基本配置
// appsettings.json
{
"Consul": {
"Address": "http://localhost:8500",
"ServiceName": "order-service",
"ServiceId": "order-service-001",
"ServiceHost": "192.168.1.100",
"ServicePort": 5001,
"HealthCheckPath": "/health",
"HealthCheckInterval": "10s",
"HealthCheckTimeout": "5s",
"Tags": [ "v1.0", "order", "microservice" ]
}
}配置模型
/// <summary>
/// Consul 配置选项
/// </summary>
public class ConsulOptions
{
/// <summary>Consul 服务器地址</summary>
public string Address { get; set; } = "http://localhost:8500";
/// <summary>服务名称(用于服务发现)</summary>
public string ServiceName { get; set; }
/// <summary>服务唯一ID(同一服务多实例时需不同)</summary>
public string ServiceId { get; set; }
/// <summary>服务所在主机地址</summary>
public string ServiceHost { get; set; }
/// <summary>服务端口</summary>
public int ServicePort { get; set; }
/// <summary>健康检查路径</summary>
public string HealthCheckPath { get; set; } = "/health";
/// <summary>健康检查间隔</summary>
public string HealthCheckInterval { get; set; } = "10s";
/// <summary>健康检查超时</summary>
public string HealthCheckTimeout { get; set; } = "5s";
/// <summary>服务标签</summary>
public string[] Tags { get; set; } = Array.Empty<string>();
/// <summary>是否启用</summary>
public bool Enabled { get; set; } = true;
}服务注册与注销实现
/// <summary>
/// Consul 服务注册 — 负责服务启动时注册、停止时注销
/// </summary>
public class ConsulRegistrationService : IHostedService
{
private readonly ConsulOptions _options;
private readonly ILogger<ConsulRegistrationService> _logger;
private readonly IConsulClient _consulClient;
public ConsulRegistrationService(
IOptions<ConsulOptions> options,
ILogger<ConsulRegistrationService> logger,
IConsulClient consulClient)
{
_options = options.Value;
_logger = logger;
_consulClient = consulClient;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
if (!_options.Enabled) return;
var registration = new AgentServiceRegistration
{
ID = _options.ServiceId,
Name = _options.ServiceName,
Address = _options.ServiceHost,
Port = _options.ServicePort,
Tags = _options.Tags,
// 健康检查配置
Check = new AgentServiceCheck
{
HTTP = $"http://{_options.ServiceHost}:{_options.ServicePort}{_options.HealthCheckPath}",
Interval = TimeSpan.Parse(_options.HealthCheckInterval.TrimEnd('s') + "seconds"),
Timeout = TimeSpan.Parse(_options.HealthCheckTimeout.TrimEnd('s') + "seconds"),
DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1), // 不健康1分钟后自动注销
TLSSkipVerify = true
}
};
_logger.LogInformation(
"注册服务到 Consul: {ServiceId} -> {ServiceHost}:{ServicePort}",
_options.ServiceId, _options.ServiceHost, _options.ServicePort);
await _consulClient.Agent.ServiceRegister(registration, cancellationToken);
}
public async Task StopAsync(CancellationToken cancellationToken)
{
if (!_options.Enabled) return;
_logger.LogInformation("从 Consul 注销服务: {ServiceId}", _options.ServiceId);
await _consulClient.Agent.ServiceDeregister(_options.ServiceId, cancellationToken);
}
}注册到DI容器
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// 绑定配置
builder.Services.Configure<ConsulOptions>(builder.Configuration.GetSection("Consul"));
// 注册 Consul 客户端
builder.Services.AddSingleton<IConsulClient>(sp =>
{
var options = sp.GetRequiredService<IOptions<ConsulOptions>>().Value;
return new ConsulClient(config =>
{
config.Address = new Uri(options.Address);
});
});
// 注册后台服务(自动注册与注销)
builder.Services.AddHostedService<ConsulRegistrationService>();
var app = builder.Build();
// 健康检查端点
app.MapGet("/health", () => Results.Ok(new { Status = "Healthy", Timestamp = DateTime.UtcNow }));
app.Run();健康检查
多种健康检查方式
/// <summary>
/// 注册时配置多种健康检查
/// </summary>
var registration = new AgentServiceRegistration
{
ID = "order-service-001",
Name = "order-service",
Address = "192.168.1.100",
Port = 5001,
Checks = new AgentServiceCheck[]
{
// HTTP 健康检查
new AgentServiceCheck
{
HTTP = "http://192.168.1.100:5001/health",
Interval = TimeSpan.FromSeconds(10),
Timeout = TimeSpan.FromSeconds(5)
},
// TCP 健康检查
new AgentServiceCheck
{
TCP = "192.168.1.100:5001",
Interval = TimeSpan.FromSeconds(10),
Timeout = TimeSpan.FromSeconds(5)
},
// gRPC 健康检查
new AgentServiceCheck
{
GRPC = "192.168.1.100:5002",
Interval = TimeSpan.FromSeconds(10),
GRPCUseTLS = false
}
}
};ASP.NET Core 健康检查集成
// Program.cs — 完整健康检查配置
builder.Services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy(), tags: new[] { "live" })
.AddNpgSql(
connectionString: builder.Configuration.GetConnectionString("Postgres"),
name: "postgresql",
tags: new[] { "db", "ready" })
.AddRabbitMQ(
connectionString: builder.Configuration.GetConnectionString("RabbitMQ"),
name: "rabbitmq",
tags: new[] { "mq", "ready" })
.AddRedis(
connectionString: builder.Configuration.GetConnectionString("Redis"),
name: "redis",
tags: new[] { "cache", "ready" });
var app = builder.Build();
// 存活检查 — 仅检查应用是否运行
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("live"),
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
// 就绪检查 — 检查所有依赖是否就绪
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready"),
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
// 完整健康检查(供 Consul 使用)
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});服务发现
服务发现封装
/// <summary>
/// Consul 服务发现 — 根据服务名称获取可用实例
/// </summary>
public interface IServiceDiscovery
{
/// <summary>获取单个服务实例(随机负载均衡)</summary>
Task<ServiceInstance> GetServiceAsync(string serviceName, string? tag = null);
/// <summary>获取所有健康的服务实例</summary>
Task<List<ServiceInstance>> GetServicesAsync(string serviceName, string? tag = null);
}
/// <summary>
/// 服务实例信息
/// </summary>
public class ServiceInstance
{
public string ServiceId { get; set; } = string.Empty;
public string ServiceName { get; set; } = string.Empty;
public string Host { get; set; } = string.Empty;
public int Port { get; set; }
public string[] Tags { get; set; } = Array.Empty<string>();
}
/// <summary>
/// Consul 服务发现实现
/// </summary>
public class ConsulServiceDiscovery : IServiceDiscovery
{
private readonly IConsulClient _consulClient;
private readonly ILogger<ConsulServiceDiscovery> _logger;
public ConsulServiceDiscovery(
IConsulClient consulClient,
ILogger<ConsulServiceDiscovery> logger)
{
_consulClient = consulClient;
_logger = logger;
}
public async Task<ServiceInstance> GetServiceAsync(string serviceName, string? tag = null)
{
var services = await GetServicesAsync(serviceName, tag);
if (services.Count == 0)
{
throw new InvalidOperationException($"未发现可用服务: {serviceName}");
}
// 简单随机负载均衡
var index = Random.Shared.Next(services.Count);
var instance = services[index];
_logger.LogDebug("选择服务实例: {ServiceId} -> {Host}:{Port}",
instance.ServiceId, instance.Host, instance.Port);
return instance;
}
public async Task<List<ServiceInstance>> GetServicesAsync(string serviceName, string? tag = null)
{
var result = await _consulClient.Health.Service(serviceName, tag, passingOnly: true);
var instances = result.Response
.Select(s => new ServiceInstance
{
ServiceId = s.Service.ID,
ServiceName = s.Service.Service,
Host = s.Service.Address,
Port = s.Service.Port,
Tags = s.Service.Tags ?? Array.Empty<string>()
})
.ToList();
_logger.LogDebug("发现 {ServiceName} 服务 {Count} 个实例",
serviceName, instances.Count);
return instances;
}
}
// 注册
builder.Services.AddSingleton<IServiceDiscovery, ConsulServiceDiscovery>();在业务中使用服务发现
/// <summary>
/// 使用服务发现调用远程服务 — HttpClient + Consul
/// </summary>
public class OrderClientService
{
private readonly IServiceDiscovery _serviceDiscovery;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<OrderClientService> _logger;
public OrderClientService(
IServiceDiscovery serviceDiscovery,
IHttpClientFactory httpClientFactory,
ILogger<OrderClientService> logger)
{
_serviceDiscovery = serviceDiscovery;
_httpClientFactory = httpClientFactory;
_logger = logger;
}
/// <summary>
/// 通过服务发现调用订单服务
/// </summary>
public async Task<OrderDto?> GetOrderAsync(int orderId)
{
// 1. 从 Consul 获取可用实例
var instance = await _serviceDiscovery.GetServiceAsync("order-service");
// 2. 拼接URL并发起请求
var url = $"http://{instance.Host}:{instance.Port}/api/orders/{orderId}";
_logger.LogInformation("调用订单服务: {Url}", url);
var client = _httpClientFactory.CreateClient();
var response = await client.GetAsync(url);
if (!response.IsSuccessStatusCode)
{
_logger.LogWarning("订单服务调用失败: {StatusCode}", response.StatusCode);
return null;
}
return await response.Content.ReadFromJsonAsync<OrderDto>();
}
}基于 Consul 的负载均衡 HttpClient
/// <summary>
/// 自定义 HttpClientHandler — 每次请求通过 Consul 解析服务地址
/// </summary>
public class ConsulHttpMessageHandler : DelegatingHandler
{
private readonly IServiceDiscovery _serviceDiscovery;
private readonly string _serviceName;
public ConsulHttpMessageHandler(IServiceDiscovery serviceDiscovery, string serviceName)
{
_serviceDiscovery = serviceDiscovery;
_serviceName = serviceName;
InnerHandler = new HttpClientHandler();
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// 解析服务实例
var instance = await _serviceDiscovery.GetServiceAsync(_serviceName);
// 替换请求URI
var uriBuilder = new UriBuilder(request.RequestUri!)
{
Host = instance.Host,
Port = instance.Port
};
request.RequestUri = uriBuilder.Uri;
return await base.SendAsync(request, cancellationToken);
}
}
// 注册使用 Consul 负载均衡的 HttpClient
builder.Services.AddHttpClient("order-service")
.ConfigurePrimaryHttpMessageHandler(sp =>
{
var discovery = sp.GetRequiredService<IServiceDiscovery>();
return new ConsulHttpMessageHandler(discovery, "order-service");
})
.SetHandlerLifetime(TimeSpan.FromMinutes(2));Key/Value 配置中心
KV 存储操作封装
/// <summary>
/// Consul KV 配置中心操作
/// </summary>
public class ConsulKVService
{
private readonly IConsulClient _consulClient;
private readonly ILogger<ConsulKVService> _logger;
public ConsulKVService(IConsulClient consulClient, ILogger<ConsulKVService> logger)
{
_consulClient = consulClient;
_logger = logger;
}
/// <summary>获取配置值</summary>
public async Task<string?> GetAsync(string key)
{
var result = await _consulClient.KV.Get(key);
if (result.Response == null) return null;
return Encoding.UTF8.GetString(result.Response.Value);
}
/// <summary>设置配置值</summary>
public async Task SetAsync(string key, string value)
{
var kvPair = new KVPair(key)
{
Value = Encoding.UTF8.GetBytes(value)
};
await _consulClient.KV.Put(kvPair);
_logger.LogInformation("配置已更新: {Key}", key);
}
/// <summary>删除配置</summary>
public async Task DeleteAsync(string key)
{
await _consulClient.KV.Delete(key);
_logger.LogInformation("配置已删除: {Key}", key);
}
/// <summary>获取指定前缀的所有配置</summary>
public async Task<Dictionary<string, string>> GetByPrefixAsync(string prefix)
{
var result = await _consulClient.KV.List(prefix);
if (result.Response == null) return new();
return result.Response
.Where(kv => kv.Value != null)
.ToDictionary(
kv => kv.Key,
kv => Encoding.UTF8.GetString(kv.Value)
);
}
/// <summary>监听配置变更(长轮询)</summary>
public async Task WatchAsync(string key, Action<string> onChanged, CancellationToken cancellationToken)
{
ulong lastIndex = 0;
while (!cancellationToken.IsCancellationRequested)
{
try
{
var result = await _consulClient.KV.Get(key, new QueryOptions
{
WaitIndex = lastIndex,
WaitTime = TimeSpan.FromMinutes(5) // 长轮询等待时间
}, cancellationToken);
if (result.Response != null && result.LastIndex != lastIndex)
{
lastIndex = result.LastIndex;
var value = Encoding.UTF8.GetString(result.Response.Value);
_logger.LogInformation("配置变更: {Key}", key);
onChanged(value);
}
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "监听配置变更失败: {Key}", key);
await Task.Delay(5000, cancellationToken);
}
}
}
}自定义 ConfigurationProvider — 从 Consul 加载配置
/// <summary>
/// Consul 配置提供者 — 将 Consul KV 作为配置源
/// </summary>
public class ConsulConfigurationProvider : ConfigurationProvider
{
private readonly IConsulClient _consulClient;
private readonly string _prefix;
private readonly ILogger _logger;
public ConsulConfigurationProvider(
IConsulClient consulClient,
string prefix,
ILogger logger)
{
_consulClient = consulClient;
_prefix = prefix;
_logger = logger;
}
public override void Load()
{
LoadAsync().GetAwaiter().GetResult();
}
private async Task LoadAsync()
{
try
{
var result = await _consulClient.KV.List(_prefix);
if (result.Response == null) return;
foreach (var kv in result.Response)
{
if (kv.Value == null) continue;
var key = kv.Key.Substring(_prefix.Length).TrimStart('/');
var value = Encoding.UTF8.GetString(kv.Value);
// 将路径分隔符替换为配置分隔符
// "database/connectionString" -> "database:connectionString"
var configKey = key.Replace('/', ':');
Data[configKey] = value;
}
_logger.LogInformation("从 Consul 加载配置成功,共 {Count} 项", Data.Count);
}
catch (Exception ex)
{
_logger.LogError(ex, "从 Consul 加载配置失败");
}
}
}
/// <summary>
/// Consul 配置源
/// </summary>
public class ConsulConfigurationSource : IConfigurationSource
{
private readonly IConsulClient _consulClient;
private readonly string _prefix;
private readonly ILogger _logger;
public ConsulConfigurationSource(IConsulClient consulClient, string prefix, ILogger logger)
{
_consulClient = consulClient;
_prefix = prefix;
_logger = logger;
}
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new ConsulConfigurationProvider(_consulClient, _prefix, _logger);
}
}
// 使用方式
builder.Configuration.Add(new ConsulConfigurationSource(
consulClient,
"myapp/config/",
logger
));多实例动态注册
/// <summary>
/// 支持动态端口的多实例注册 — 适用于在同一台机器上运行多个服务实例
/// </summary>
public static class ConsulExtensions
{
public static IServiceCollection AddConsulRegistration(
this IServiceCollection services,
IConfiguration configuration)
{
var options = new ConsulOptions();
configuration.GetSection("Consul").Bind(options);
// 如果未指定ServiceId,自动生成唯一ID
if (string.IsNullOrEmpty(options.ServiceId))
{
options.ServiceId = $"{options.ServiceName}-{Environment.MachineName}-{options.ServicePort}";
}
services.AddSingleton(Options.Create(options));
services.AddSingleton<IConsulClient>(sp =>
{
return new ConsulClient(config =>
{
config.Address = new Uri(options.Address);
});
});
services.AddSingleton<IServiceDiscovery, ConsulServiceDiscovery>();
services.AddHostedService<ConsulRegistrationService>();
return services;
}
}
// Program.cs — 简洁使用
builder.Services.AddConsulRegistration(builder.Configuration);完整微服务集成示例
// -------------------- 订单服务(OrderService)--------------------
// appsettings.json
{
"Consul": {
"Address": "http://consul:8500",
"ServiceName": "order-service",
"ServiceId": "order-service-1",
"ServiceHost": "192.168.1.100",
"ServicePort": 5001,
"Tags": [ "v1.0", "order" ]
}
}
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddConsulRegistration(builder.Configuration);
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.MapHealthChecks("/health");
app.Run();
// -------------------- 用户服务(UserService)--------------------
// 通过服务发现调用订单服务
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
private readonly IServiceDiscovery _discovery;
private readonly IHttpClientFactory _httpClientFactory;
public UserController(IServiceDiscovery discovery, IHttpClientFactory httpClientFactory)
{
_discovery = discovery;
_httpClientFactory = httpClientFactory;
}
[HttpGet("{userId}/orders")]
public async Task<ActionResult> GetUserOrders(int userId)
{
// 服务发现 — 自动获取可用的 order-service 实例
var instance = await _discovery.GetServiceAsync("order-service");
var client = _httpClientFactory.CreateClient();
var url = $"http://{instance.Host}:{instance.Port}/api/orders?userId={userId}";
var response = await client.GetAsync(url);
var orders = await response.Content.ReadFromJsonAsync<List<OrderDto>>();
return Ok(orders);
}
}Consul 常用 API 操作
/// <summary>
/// Consul 管理 API 封装
/// </summary>
public class ConsulAdminService
{
private readonly IConsulClient _consulClient;
public ConsulAdminService(IConsulClient consulClient)
{
_consulClient = consulClient;
}
// 查看所有注册的服务
public async Task<Dictionary<string, AgentService>> GetAllServicesAsync()
{
var result = await _consulClient.Agent.Services();
return result.Response;
}
// 查看所有健康检查状态
public async Task<HealthStatus> GetServiceHealthAsync(string serviceName)
{
var result = await _consulClient.Health.Service(serviceName, passingOnly: true);
var totalResult = await _consulClient.Health.Service(serviceName, passingOnly: false);
return new HealthStatus
{
ServiceName = serviceName,
TotalInstances = totalResult.Response.Length,
HealthyInstances = result.Response.Length
};
}
// 手动注销服务
public async Task DeregisterServiceAsync(string serviceId)
{
await _consulClient.Agent.ServiceDeregister(serviceId);
}
// 启用维护模式(服务上线前暂时从发现中移除)
public async Task EnableMaintenanceAsync(string serviceId, string reason)
{
await _consulClient.Agent.EnableServiceMaintenance(serviceId, reason);
}
// 禁用维护模式
public async Task DisableMaintenanceAsync(string serviceId)
{
await _consulClient.Agent.DisableServiceMaintenance(serviceId);
}
}
public class HealthStatus
{
public string ServiceName { get; set; } = string.Empty;
public int TotalInstances { get; set; }
public int HealthyInstances { get; set; }
}优点
缺点
总结
Consul 是微服务架构中服务治理的核心组件。通过本文的实践,你可以掌握:Docker 部署 Consul 集群、ASP.NET Core 服务自动注册与注销、健康检查配置、基于 Consul 的服务发现与负载均衡、KV 配置中心的使用。在实际项目中,Consul 通常与 Ocelot API 网关配合使用,实现完整的微服务通信方案。
关键知识点
- 先分清这个主题位于请求链路、后台任务链路还是基础设施链路。
- 服务端主题通常不只关心功能正确,还关心稳定性、性能和可观测性。
- 任何框架能力都要结合配置、生命周期、异常传播和外部依赖一起看。
项目落地视角
- 画清请求进入、业务执行、外部调用、日志记录和错误返回的完整路径。
- 为关键链路补齐超时、重试、熔断、追踪和结构化日志。
- 把配置与敏感信息分离,并明确不同环境的差异来源。
常见误区
- 只会堆中间件或组件,不知道它们在链路中的执行顺序。
- 忽略生命周期和线程池、连接池等运行时资源约束。
- 没有监控和测试就对性能或可靠性下结论。
进阶路线
- 继续向运行时行为、可观测性、发布治理和微服务协同深入。
- 把主题和数据库、缓存、消息队列、认证授权联动起来理解。
- 沉淀团队级模板,包括统一异常处理、配置约定和基础设施封装。
适用场景
- 当你准备把《Consul 服务发现与注册》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合 API 服务、后台任务、实时通信、认证授权和微服务协作场景。
- 当需求开始涉及稳定性、性能、可观测性和发布流程时,这类主题会成为基础设施能力。
落地建议
- 先定义请求链路与失败路径,再决定中间件、过滤器、服务边界和依赖方式。
- 为关键链路补日志、指标、追踪、超时与重试策略。
- 环境配置与敏感信息分离,避免把生产参数写死在代码或镜像里。
排错清单
- 先确认问题发生在路由、模型绑定、中间件、业务层还是基础设施层。
- 检查 DI 生命周期、配置来源、序列化规则和认证上下文。
- 查看线程池、连接池、缓存命中率和外部依赖超时。
复盘问题
- 如果把《Consul 服务发现与注册》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《Consul 服务发现与注册》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《Consul 服务发现与注册》最大的收益和代价分别是什么?
