# EasyCore.Consul
**Repository Path**: wzhy-0521/easy-core.-consul
## Basic Information
- **Project Name**: EasyCore.Consul
- **Description**: EasyCore.Consul
- **Primary Language**: Unknown
- **License**: MulanPSL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 1
- **Created**: 2025-03-23
- **Last Updated**: 2026-07-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: EasyCore
## README
# 🧭 EasyCore.Consul
> **EasyCore.Consul** 是面向 .NET 8 的生产级 [HashiCorp Consul](https://www.consul.io/) 集成库。提供服务注册与健康检查、KV 存储、分布式锁、健康实例发现与负载均衡,以及服务间 HTTP 调用能力。






---
## 🌍 Language
- 🇨🇳 **中文(当前文档)**
- 🇺🇸 English: [README.en.md](https://github.com/RockyWang0521/EasyCore.Consul/blob/master/README.en.md)
---
## 📚 目录
### 🗺️ 第一部分:总览与架构
- [1. 🎯 项目定位](#1--项目定位)
- [2. 🏗️ 架构与模块关系](#2-️-架构与模块关系)
- [3. 📦 NuGet / 项目清单](#3--nuget--项目清单)
- [4. 📊 能力对比](#4--能力对比)
### 🚀 第二部分:快速上手
- [5. 💻 环境要求](#5--环境要求)
- [6. 📥 安装](#6--安装)
- [7. ⚡ 三分钟快速开始](#7--三分钟快速开始)
- [8. ⚙️ 配置项完整说明](#8-️-配置项完整说明)
### 🧩 第三部分:KV · 锁 · 服务调用
- [9. 🗄️ KV 缓存](#9-️-kv-缓存)
- [10. 🔒 分布式锁](#10--分布式锁)
- [11. 🔗 服务发现与调用](#11--服务发现与调用)
### 🏭 第四部分:Demo 与生产
- [12. 🧪 Demo 项目](#12--demo-项目)
- [13. 🔄 从旧版迁移](#13--从旧版迁移)
- [14. ✅ 生产清单](#14--生产清单)
- [15. ❓ FAQ](#15--faq)
- [16. 📄 License](#16--license)
---
## 1. 🎯 项目定位
EasyCore.Consul 解决「在 ASP.NET Core 里安全、可运维地接入 Consul」的问题:
| 痛点 | EasyCore.Consul 做法 |
|---|---|
| 手写注册/注销易漏 | `IHostedService` 异步注册,停机优雅注销 |
| Catalog 可能打到不健康实例 | Health API + `PassingOnly` |
| HttpClient 改 DefaultHeaders 不安全 | 按请求设置 Authorization |
| 分布式锁无续约 | Session TTL + `RenewPeriodic` |
| 配置错误启动后才发现 | `ValidateOnStart` 选项校验 |
| 扩展点耦合 | 注册 / KV / 锁 / 调用按需 `Add*` |
### 1.1 ✨ 设计原则
| 原则 | 说明 |
|---|---|
| **低摩擦接入** | 几个扩展方法即可跑通注册与健康检查 |
| **按需组合** | Cache / Locking / Server 独立注册 |
| **失败可感知** | 注册失败抛错;调用返回 `Succeed` / `Message` |
| **生产默认** | 健康实例发现、稳定 ServiceId、ACL Token 支持 |
| **向后兼容** | 保留 `EasyCoreConsul*` / `ServiceIP` 等别名 |
### 1.2 📁 解决方案目录
```text
EasyCore.Consul/
├── src/EasyCore.Consul/ # 核心库
│ ├── Configuration/ # Options + 校验
│ ├── Registration/ # HostedService 注册
│ ├── Cache/ # KV
│ ├── Locking/ # 分布式锁
│ ├── Discovery/ # 健康发现 + 负载均衡
│ ├── Invocation/ # 服务间 HTTP 调用
│ └── DependencyInjection/ # Add* / Use*
├── demo/
│ ├── Web.Consul/ # :5057 — KV / 锁 / 调用
│ ├── Web.Consul.Server/ # :5058 — 下游 API
│ └── Web.Consul.Ocelot/ # Ocelot + Consul
├── tests/EasyCore.Consul.Tests/
└── docs/svg/ # README 架构图
```
---
## 2. 🏗️ 架构与模块关系
### 2.1 🖼️ 组件关系图

### 2.2 🔁 服务生命周期

### 2.3 📜 数据流(文字版)
```text
[ASP.NET Core Host]
│
▼
AddEasyCoreConsul ──► ConsulOptions (ValidateOnStart)
│
├─ HostedService ──► Agent.ServiceRegister / Deregister
│
├─ IConsulCache ────► KV Put / Get / Delete
├─ IConsulLocking ──► Session + Acquire / Renew / Release
└─ IConsulServer ───► Health.Service → LB → HttpClient
│
▼
Downstream Service
```
---
## 3. 📦 NuGet / 项目清单
| 包名 / 项目 | 职责 | 是否必须 |
|---|---|---|
| `EasyCore.Consul` | 注册、KV、锁、发现、调用 | ✅ |
| `demo/Web.Consul` | 客户端示例 | 示例 |
| `demo/Web.Consul.Server` | 下游服务示例 | 示例 |
| `demo/Web.Consul.Ocelot` | 网关 + Consul 发现 | 示例 |
| `tests/EasyCore.Consul.Tests` | 单元测试 | 开发 |
---
## 4. 📊 能力对比
| 能力 | 说明 | 接口 |
|---|---|---|
| 服务注册 | 启动注册 / 停机注销 / HTTP 健康检查 | `AddEasyCoreConsul` |
| KV 存储 | 字符串与强类型 JSON | `IConsulCache` |
| 分布式锁 | TTL 续约、`await using` 租约 | `IConsulLocking` |
| 服务发现 | 仅健康实例(默认) | `IConsulServiceDiscovery` |
| 负载均衡 | RoundRobin(默认)/ Random | `LoadBalance` |
| 服务调用 | GET/POST/PUT/DELETE + Bearer | `IConsulServer` |
| 网关 | Ocelot Provider(Demo) | `Web.Consul.Ocelot` |
### 4.1 🌳 选型决策树
```text
需要本进程注册到 Consul?
├── 是 → AddEasyCoreConsul + Register=true + HealthCheck.Http
└── 否 → AddEasyCoreConsul + Register=false(仅消费)
还需要?
├── 配置 / 缓存 → AddEasyCoreConsulCache
├── 互斥临界区 → AddEasyCoreConsulLocking
└── 调其他服务 → AddEasyCoreConsulServer
```
---
## 5. 💻 环境要求
| 项 | 要求 |
|---|---|
| .NET | 8.0+ |
| 宿主 | ASP.NET Core(Web / API) |
| Consul | Agent 可达(默认 `http://127.0.0.1:8500`) |
| 依赖 | `Consul` NuGet(由本包引入) |
---
## 6. 📥 安装
```bash
dotnet add package EasyCore.Consul
```
本地源码引用:
```xml
```
---
## 7. ⚡ 三分钟快速开始
### 7️⃣.1️⃣ 配置 `appsettings.json`
```json
{
"Consul": {
"ConsulAddress": "http://127.0.0.1:8500",
"Token": null,
"Register": true,
"ServiceName": "my-service",
"ServiceAddress": "127.0.0.1",
"ServicePort": 5057,
"LoadBalance": "RoundRobin",
"PassingOnly": true,
"HealthCheck": {
"Http": "http://127.0.0.1:5057/healthCheck",
"Interval": "00:00:10",
"Timeout": "00:00:05",
"DeregisterCriticalServiceAfter": "00:01:00"
}
}
}
```
### 7️⃣.2️⃣ 注册服务
```csharp
using EasyCore.Consul;
var builder = WebApplication.CreateBuilder(args);
builder.AddEasyCoreConsul()
.AddEasyCoreConsulCache()
.AddEasyCoreConsulLocking()
.AddEasyCoreConsulServer();
builder.Services.AddControllers();
var app = builder.Build();
app.UseEasyCoreConsul(); // 映射 /healthCheck;注册由 HostedService 完成
app.MapControllers();
app.Run();
```
打开 Consul UI:`http://127.0.0.1:8500`,即可看到已注册服务。
---
## 8. ⚙️ 配置项完整说明
| 配置 | 说明 |
|---|---|
| `ConsulAddress` | Consul HTTP API 地址 |
| `Token` | 可选 ACL Token |
| `Datacenter` | 可选数据中心 |
| `Register` | 是否自注册(默认 `true`) |
| `ServiceName` | 逻辑服务名 |
| `ServiceId` | 实例 ID;空则 `{ServiceName}-{MachineName}-{Port}` |
| `ServiceAddress` / `ServiceIP` | 对外宣告地址 |
| `ServicePort` | 对外宣告端口 |
| `Tags` / `Meta` | 服务标签与元数据 |
| `HealthCheck.Http` / `ServiceHealthCheck` | HTTP 健康检查 URL |
| `HealthCheck.Interval` | 检查间隔(默认 10s) |
| `HealthCheck.Timeout` | 超时(默认 5s) |
| `HealthCheck.DeregisterCriticalServiceAfter` | 临界后自动注销 |
| `LoadBalance` | `RoundRobin` / `Random` |
| `PassingOnly` | 仅发现通过健康检查的实例(默认 `true`) |
---
## 9. 🗄️ KV 缓存
```csharp
public class DemoController(IConsulCache cache) : ControllerBase
{
[HttpPost("{key}")]
public Task Put(string key, [FromBody] MyDto dto, CancellationToken ct)
=> cache.PutAsync(key, dto, ct);
[HttpGet("{key}")]
public Task Get(string key, CancellationToken ct)
=> cache.GetAsync(key, ct);
[HttpGet("raw/{key}")]
public Task GetRaw(string key, CancellationToken ct)
=> cache.GetStringAsync(key, ct);
}
```
| API | 说明 |
|---|---|
| `PutAsync` / `KVPut` | 写入字符串或对象(JSON) |
| `GetStringAsync` | 原文字符串,不做 JSON 反序列化 |
| `GetAsync` / `KVGet` | 强类型读取;`T=string` 时同样按原文返回 |
| `DeleteAsync` / `KVDelete` | 删除键 |
---
## 10. 🔒 分布式锁
```csharp
// 推荐:自动获取 / 续约 / 释放
await locking.ExecuteLockedAsync("orders:pay", ttlSeconds: 30, async ct =>
{
await DoWorkAsync(ct);
}, cancellationToken);
// 或手动租约
await using var lease = await locking.TryAcquireAsync(
"orders:pay", TimeSpan.FromSeconds(30), ct);
if (lease is null) return Conflict();
```
| API | 说明 |
|---|---|
| `TryAcquireAsync` | 返回 `IConsulLock`(含 Session 续约),失败返回 `null` |
| `ExecuteLockedAsync` | `Func`,正确 await |
| `AcquireLock` / `ReleaseLock` | 兼容旧版(无自动续约) |
> ⚠️ 持锁期间请保证 TTL 合理;租约对象会续约 Session,请务必 `await using` 释放。
---
## 11. 🔗 服务发现与调用
```csharp
var result = await consulServer.ServiceGetAsync(
RequestScheme.Http,
serviceName: "order-service",
apiPath: "/api/orders/1",
cancellationToken: ct);
if (!result.Succeed) return Problem(result.Message);
return Ok(result.Values);
```
| 方法 | 说明 |
|---|---|
| `ServiceGetAsync` | GET |
| `ServicePostAsync` | POST(可带 body) |
| `ServicePutAsync` | PUT |
| `ServiceDeleteAsync` | DELETE |
发现链路:`Health.Service` → 负载均衡 → `IHttpClientFactory` 发请求。默认只选 **passing** 实例。
---
## 12. 🧪 Demo 项目

| 项目 | 端口 | 角色 | 命令 |
|---|---|---|---|
| [`Web.Consul`](demo/Web.Consul) | 5057 | KV / 锁 / 服务调用 | `dotnet run --project demo/Web.Consul` |
| [`Web.Consul.Server`](demo/Web.Consul.Server) | 5058 | 下游 REST API | `dotnet run --project demo/Web.Consul.Server` |
| [`Web.Consul.Ocelot`](demo/Web.Consul.Ocelot) | — | Ocelot 网关 | `dotnet run --project demo/Web.Consul.Ocelot` |
```bash
# 1. 启动 Consul Agent(HTTP :8500)
# 2. 启动下游
dotnet run --project demo/Web.Consul.Server
# 3. 启动客户端
dotnet run --project demo/Web.Consul
# 4. 打开 http://127.0.0.1:8500 查看注册结果
```
---
## 13. 🔄 从旧版迁移
**8.1.0** 相对早期封装为破坏性增强(推荐升级):
| 旧版 | 8.1 |
|---|---|
| `UseEasyCoreConsul` 内 `.Wait()` | `IHostedService` 异步注册 |
| Catalog 发现 | Health API + `PassingOnly` |
| `ExecuteLocked(Action)` | 优先 `ExecuteLockedAsync(Func<..., Task>)` |
| `WriteResult` 直接暴露 | 返回 `bool`(语义更清晰) |
| `ServiceHealthCheck` 扁平字段 | `HealthCheck` 对象(旧字段仍可用) |
| `EasyCoreConsul*` | 保留;推荐 `AddEasyCoreConsul*` |
---
## 14. ✅ 生产清单
- [ ] Consul 地址使用内网 DNS / VIP,配置 ACL `Token`
- [ ] `ServiceAddress` 使用可被其他节点访问的 IP(勿用仅本机可达地址)
- [ ] `HealthCheck.Timeout` 保持合理(秒级),避免过长超时
- [ ] 多实例设置稳定 `ServiceId` 或接受默认 `{Name}-{Machine}-{Port}`
- [ ] 服务调用侧开启 `PassingOnly=true`
- [ ] 锁的 TTL 与业务临界区时长匹配,始终释放租约
- [ ] 连接失败时关注启动日志(注册失败会使 Host 启动失败)
- [ ] CI 执行 `dotnet test`(本仓库已提供 workflow)
---
## 15. ❓ FAQ
**Q: 启动报 Options 校验失败?**
A: `Register=true` 时必须配置 `ServiceName`、`ServiceAddress`、`ServicePort` 与合法 `ConsulAddress`。
**Q: Consul UI 能看到服务但调用失败?**
A: 检查健康检查是否 passing;确认 `ServiceAddress` 对调用方可达;确认下游已启动。
**Q: 只想用 KV,不想注册自己?**
A: `Register: false`,再 `AddEasyCoreConsulCache()`。
**Q: 锁拿不到?**
A: 可能被其他持有者占用,或 Session/Acquire 失败。查看日志;使用 `TryAcquireAsync` 处理 `null`。
**Q: Ocelot Demo 与本库关系?**
A: Ocelot 通过 `Ocelot.Provider.Consul` 自行发现;本库负责业务侧注册与调用。可同时使用。
---
## 16. 📄 License
MIT OR Apache-2.0 — 详见 [LICENSE](LICENSE)。
---
## 🤝 贡献
1. Fork 并创建特性分支
2. 在 `tests/EasyCore.Consul.Tests` 补充测试
3. 执行 `dotnet test` 与 `dotnet build EasyCore.Consul.sln`
4. 提交 Pull Request
欢迎 Issue / PR 🚀