解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
|
# æœåŠ¡è§£æžå™¨ IServiceResolver
## 概述
`IServiceResolver` 是 NewLife 体系ä¸çš„**æœåŠ¡å‘现抽象接å£**,定义在 `NewLife.Core` æ ¸å¿ƒåº“ä¸ã€‚它å…许业务代ç 通过æœåŠ¡å获å–通信客户端(`IApiClient`ï¼‰ï¼Œæ— éœ€å…³å¿ƒæœåŠ¡åœ°å€çš„æ¥æºå’Œç®¡ç†æ–¹å¼ã€‚
æ ¸å¿ƒä»·å€¼ï¼š**类库项目仅引用 NewLife.Core å³å¯é€šè¿‡ DI 获å–该接å£ï¼Œç”±ä¸Šå±‚应用æä¾›å…·ä½“实现**。
## 三层架构
```
┌─────────────────────────────────────────────────────â”
│ Stardust (星尘) │
│ AppClient : IRegistry : IServiceResolver │
│ · 从注册ä¸å¿ƒåЍæ€å‘现æœåŠ¡åœ°å€ â”‚
│ · æ”¯æŒæƒé‡ã€å¤šåœ°å€ã€è‡ªåŠ¨æ›´æ–° │
│ · æœåŠ¡å˜æ›´æ—¶è‡ªåŠ¨é€šçŸ¥å¹¶æ›´æ–°å®¢æˆ·ç«¯åœ°å€ â”‚
├─────────────────────────────────────────────────────┤
│ NewLife.Remoting │
│ RemotingServiceResolver : ConfigServiceResolver │
│ · æ‰©å±•æ”¯æŒ tcp/udp/ws/wss 长连接åè®® │
│ · tcp/udp → ApiClient (SRMP 长连接) │
│ · ws/wss → WsClient (WebSocket 长连接) │
├─────────────────────────────────────────────────────┤
│ NewLife.Core │
│ IServiceResolver (接å£) │
│ ConfigServiceResolver (默认实现) │
│ · 从é…置文件/é…ç½®ä¸å¿ƒè¯»å–åœ°å€ â”‚
│ · ä»…æ”¯æŒ http/https åè®® │
│ · 轮询负载å‡è¡¡ (RoundRobin) │
└─────────────────────────────────────────────────────┘
```
## 接å£å®šä¹‰
```csharp
public interface IServiceResolver
{
/// <summary>为指定æœåŠ¡èŽ·å–æ‰˜ç®¡å®¢æˆ·ç«¯ï¼Œå†…置负载å‡è¡¡ã€æ•…障转移ã€è‡ªåŠ¨æ›´æ–°</summary>
Task<IApiClient> GetClientAsync(String serviceName, String? tag = null);
/// <summary>è§£æžæœåŠ¡åœ°å€åˆ—表,供调用方自建客户端</summary>
Task<String[]> ResolveAddressesAsync(String serviceName, String? tag = null);
}
```
### 两个方法的选择
| 方法 | 适用场景 |
|------|---------|
| `GetClientAsync` | **推è**。返回托管客户端,内置 RoundRobin/Failover/Raceã€æ•…éšœå±è”½ã€è‡ªåŠ¨æ›´æ–°åœ°å€åˆ—表 |
| `ResolveAddressesAsync` | 需è¦**自定义åè®®**(如 gRPCã€è‡ªç ” RPC)或需è¦è‡ªè¡ŒæŽ§åˆ¶è´Ÿè½½å‡è¡¡ç–略时使用 |
## ConfigServiceResolver 默认实现
### 地å€è§£æžä¼˜å…ˆçº§
1. **`Servers` 属性** — 直接指定地å€ï¼Œä¼˜å…ˆçº§æœ€é«˜
2. **DI ä¸çš„ `IConfigProvider`** — 从é…ç½®ä¸å¿ƒè¯»å–
3. **本地 `appsettings.json`** — 兜底方案
### æž„é€ å‡½æ•°
```csharp
// æ–¹å¼ä¸€ï¼šé€šè¿‡ DI 容器(推è,å¯è‡ªåŠ¨è§£æž IConfigProviderã€ITracerã€ILog)
var resolver = new ConfigServiceResolver(serviceProvider);
// æ–¹å¼äºŒï¼šç›´æŽ¥æŒ‡å®šé…ç½®æä¾›è€…(适用于ä¸ä½¿ç”¨ DI 的场景)
var resolver = new ConfigServiceResolver(configProvider);
```
### 基本用法
```csharp
// 1. 通过 DI 注册
services.AddSingleton<IServiceResolver>(sp => new ConfigServiceResolver(sp)
{
Servers = "http://api.example.com:8080"
});
// 2. 在业务代ç ä¸ä½¿ç”¨
public class OrderService
{
private readonly IServiceResolver _resolver;
public OrderService(IServiceResolver resolver) => _resolver = resolver;
public async Task<Order?> GetOrderAsync(Int32 id)
{
var client = await _resolver.GetClientAsync("OrderApi");
return await client.InvokeAsync<Order>("Get", new { id });
}
}
```
### é…置文件方å¼
在 `appsettings.json` 䏿Œ‰æœåŠ¡åé…置地å€ï¼š
```json
{
"OrderApi": "http://192.168.1.100:8080,http://192.168.1.101:8080",
"UserApi": "https://user-api.example.com"
}
```
多地å€ç”¨é€—å·æˆ–分å·åˆ†éš”,自动使用轮询负载å‡è¡¡ï¼ˆRoundRobin)。
### é…ç½®åŠ¨æ€æ›´æ–°
å½“åœ°å€æ¥è‡ª `IConfigProvider`ï¼ˆéž `Servers` 属性)时,`ConfigServiceResolver` 会自动将客户端绑定到é…ç½®æä¾›è€…。é…ç½®ä¸æœåŠ¡å对应的地å€å‘ç”Ÿå˜æ›´æ—¶ï¼Œå®¢æˆ·ç«¯æœåŠ¡åˆ—è¡¨éšä¹‹è‡ªåŠ¨æ›´æ–°ï¼Œæ— éœ€é‡å¯åº”用。
```csharp
// ✅ åœ°å€æ¥è‡ª IConfigProvider:支æŒåŠ¨æ€æ›´æ–°
var resolver = new ConfigServiceResolver(configProvider);
var client = await resolver.GetClientAsync("OrderApi");
// 修改 configProvider["OrderApi"] åŽï¼Œclient 会自动使用新地å€
// âš ï¸ Servers å±žæ€§ï¼šé™æ€æŒ‡å®šï¼Œä¸ç›‘å¬é…ç½®å˜æ›´
var resolver2 = new ConfigServiceResolver(serviceProvider)
{
Servers = "http://192.168.1.100:8080" // 固定地å€ï¼Œä¸ä¼šéšé…置更新
};
```
> **注æ„**:`Servers` 属性优先级最高,设置åŽä¸å†ç»‘定 `IConfigProvider`ã€‚å¦‚éœ€åŠ¨æ€æ›´æ–°ï¼Œåº”将地å€é…置在 `IConfigProvider` ä¸ï¼Œè€Œéžç›´æŽ¥èµ‹å€¤ `Servers`。
### ç‰¹æ€§æ ‡ç¾ï¼ˆTag)
åŒä¸€æœåŠ¡å¯é€šè¿‡ `tag` 区分ä¸åŒçš„环境或分组,ä¸åŒ tag 创建独立的客户端实例:
```csharp
var devClient = await resolver.GetClientAsync("OrderApi", "dev");
var prodClient = await resolver.GetClientAsync("OrderApi", "prod");
// devClient å’Œ prodClient 是ä¸åŒçš„实例
```
### 自建客户端(ResolveAddressesAsync)
当需è¦è‡ªå®šä¹‰å议(gRPCã€è‡ªç ” RPC ç‰ï¼‰æˆ–自行控制负载å‡è¡¡æ—¶ï¼Œç”¨ `ResolveAddressesAsync` 获å–地å€åˆ—表:
```csharp
// 获å–地å€åˆ—表,自行创建 gRPC 客户端
var addresses = await resolver.ResolveAddressesAsync("PaymentService");
foreach (var addr in addresses)
{
// addr → "http://192.168.1.100:8080"
var channel = GrpcChannel.ForAddress(addr);
}
// è‡ªè¡Œéšæœºé€‰ä¸€ä¸ªåœ°å€
var addr = addresses[Random.Shared.Next(addresses.Length)];
```
## RemotingServiceResolver 多å议支æŒ
NewLife.Remoting æä¾›çš„æ‰©å±•实现,在 `ConfigServiceResolver` åŸºç¡€ä¸Šå¢žåŠ é•¿è¿žæŽ¥å议支æŒï¼š
| åè®® | 客户端类型 | è¿žæŽ¥æ–¹å¼ |
|------|-----------|---------|
| http/https | ApiHttpClient | HTTP çŸè¿žæŽ¥ |
| tcp/udp | ApiClient | SRMP 长连接 |
| ws/wss | WsClient | WebSocket 长连接 |
å议按首个地å€è‡ªåŠ¨è¯†åˆ«ï¼ŒåŒä¸€æœåŠ¡çš„å¤šä¸ªåœ°å€åè®®åº”ä¿æŒä¸€è‡´ã€‚
```csharp
// 引用 NewLife.Remoting åŽä½¿ç”¨
var resolver = new RemotingServiceResolver(configProvider);
var client = await resolver.GetClientAsync("RpcService"); // tcp://... → ApiClient
```
## Stardust 完整实现
在星尘(Stardust)ä¸ï¼Œ`AppClient` 实现了 `IRegistry`(继承自 `IServiceResolver`),æä¾›å®Œæ•´çš„æœåŠ¡å‘现能力:
- 从星尘注册ä¸å¿ƒèŽ·å–æœåŠ¡åœ°å€ï¼ˆæ”¯æŒå¤šå®žä¾‹ã€æƒé‡ï¼‰
- æœåŠ¡æä¾›æ–¹å˜æ›´æ—¶**自动更新**客户端地å€åˆ—表
- æ”¯æŒæœåŠ¡æ³¨å†Œã€å–消注册
- æœ¬åœ°ç¼“å˜æœåŠ¡åœ°å€ï¼Œæ³¨å†Œä¸å¿ƒä¸å¯ç”¨æ—¶é™çº§ä½¿ç”¨ç¼“å˜
```csharp
// Stardust ä¸çš„使用方å¼
var star = new StarFactory(server, appId, secret);
var client = await star.Service.GetClientAsync("OrderApi");
```
## 扩展自定义实现
继承 `ConfigServiceResolver` å¹¶é‡å†™ `BuildClient` 方法å³å¯æ”¯æŒè‡ªå®šä¹‰å议:
```csharp
public class MyServiceResolver : ConfigServiceResolver
{
public MyServiceResolver(IServiceProvider sp) : base(sp) { }
protected override IApiClient BuildClient(String name, String address)
{
if (address.StartsWith("grpc://"))
return new MyGrpcClient(address);
return base.BuildClient(name, address);
}
}
```
## 设计è¦ç‚¹
| è¦ç‚¹ | 说明 |
|------|------|
| **实例缓å˜** | åŒåæœåŠ¡ï¼ˆå« tag)å¤ç”¨åŒä¸€ IApiClient,é¿å…é‡å¤åˆ›å»ºè¿žæŽ¥ |
| **é…ç½®åŠ¨æ€æ›´æ–°** | åœ°å€æ¥è‡ª IConfigProvider 时自动绑定,é…ç½®å˜æ›´å³åˆ»ç”Ÿæ•ˆï¼›Servers å±žæ€§ä¸ºé™æ€ï¼Œä¸å‚与绑定 |
| **è´Ÿè½½å‡è¡¡** | 默认使用 RoundRobin 轮询模å¼ï¼Œå¤šåœ°å€è‡ªåŠ¨åˆ†å‘请求 |
| **å¯é‡Šæ”¾** | 继承 DisposeBase,Dispose 时释放所有缓å˜çš„客户端 |
| **线程安全** | 使用 ConcurrentDictionary ä¿è¯å¹¶å‘安全 |
| **åè®®æ ¡éªŒ** | ConfigServiceResolver ä»…å…许 http/https,åç±»å¯æ‰©å±• |
| **æ¸è¿›å¼æž¶æž„** | 最简å•的场景用é…ç½®æ–‡ä»¶ï¼Œå¤æ‚场景用注册ä¸å¿ƒï¼Œä¸šåŠ¡ä»£ç æ— 需修改 |
## 相关类型
- `IApiClient` — 应用接å£å®¢æˆ·ç«¯æŽ¥å£ï¼ˆInvoke/InvokeAsync)
- `ApiHttpClient` — HTTP 客户端实现,支æŒè´Ÿè½½å‡è¡¡å’Œæ•…障转移
- `IConfigProvider` — é…ç½®æä¾›è€…接å£
- `IRegistry` — æœåŠ¡æ³¨å†Œå®¢æˆ·ç«¯æŽ¥å£ï¼ˆStardust ä¸å®šä¹‰ï¼Œç»§æ‰¿ IServiceResolver)
|