解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
|
# IMessage 消æ¯å¸®åŠ©æ‰‹å†Œ
> âš ï¸ **æ—§æ ˆæ–‡æ¡£ï¼ˆv12 已退役)**:`IFrameMessage` 与消æ¯å¸§æ–¹æ³•(`TryParseHeader`/`ReadFrame`/`Build`/`BuildHeader` ç‰ï¼‰å·²ä»Ž `IMessage`/`Message` æ•´åˆ ã€‚çŽ°è¡Œå¥‘çº¦ï¼ˆ`IMessage` 消æ¯å¥‘约 + `IMessageCodec` å议契约)è§ã€Šæ¶ˆæ¯åè®®æ ˆã€‹ï¼Œæœ¬æ–‡ç« èŠ‚ä»…ä½œåŽ†å²å‚考。
本文档基于æºç `NewLife.Core/Messaging/IMessage.cs`,说明 `IMessage` 接å£åŠå…¶åŸºç±» `Message`ã€`DefaultMessage` 的设计ã€ç”¨æ³•与注æ„事项。
> 关键è¯ï¼šè¯·æ±‚-å“应模å¼ã€Dispose 释放链ã€IOwnerPacket æ± åŒ–å†…å˜ã€RPC 消æ¯ç”Ÿå‘½å‘¨æœŸã€‚
> **契约指引(v12 ä¸é—´æ€ï¼Œå·²è¢«åŽç»æ”¶å°¾å–代)**:`IMessage : IFrameMessage` 曾为å•一消æ¯å¥‘约——帧读写(`TryParseHeader` 定界 / `ReadFrame` 整帧读体 / `BindBody` æµå¼ä½“绑定 / `SetBody` å‘é€ä½“绑定 / `Build` 整帧构建 / `BuildHeader` 头 + æµå¼ä½“ / `Body` 读å–器)在帧契约 `IFrameMessage`,请求-å“应è¯ä¹‰ï¼ˆ`Reply/Error/OneWay` 三个独立布尔 + `Payload` + `CreateReply`)由消æ¯å¥‘çº¦åœ¨å…¶ä¸Šè¿½åŠ ï¼›`TryParse` 为帧长解æžä¾¿æ·å…¥å£ï¼ˆä¾› `GetFrameLength` 委托绑定)。v12 ç ´åæ€§å˜æ›´ï¼šæ—§å `Read/ToPacket/ToHeaderPacket` å·²åˆ é™¤ï¼Œä¸‹æ¸¸æ”¹ç”¨ `ReadFrame/Build/BuildHeader` 并釿–°ç¼–译;`WebSocketMessage` 仅实现帧契约。构建采用转移è¯ä¹‰ï¼š`Build` æˆåŠŸåŽæ¶ˆæ¯ä¸å†æŒæœ‰ä½“(`SetBody(null)` 表示所有æƒè½¬ç§»ã€ä¸å½’还);æµå¼ä½“ä¸å¯æ•´å¸§æž„建,请改用 `BuildHeader` + æµå¼å‘é€ã€‚该ä¸é—´æ€åŽç»äº¦åˆ 除 `IFrameMessage`,现行契约è§ã€Šæ¶ˆæ¯åè®®æ ˆã€‹ï¼ˆ`IMessage` 消æ¯å¥‘约 + `IMessageCodec` å议契约)。
---
## 1. è®¾è®¡ç›®æ ‡
- **ç»Ÿä¸€æ¶ˆæ¯æŠ½è±¡**:为 RPCã€ç½‘络通信æä¾›è¯·æ±‚-å“应消æ¯çš„æ ‡å‡†æŽ¥å£ã€‚
- **资æºå®‰å…¨é‡Šæ”¾**:`IMessage : IDisposable`ï¼Œé‡Šæ”¾æ¶ˆæ¯æ—¶è‡ªåŠ¨å½’è¿˜å†…éƒ¨ `Payload` çš„æ± åŒ–å†…å˜ã€‚
- **çµæ´»å¯æ‰©å±•**:通过 `Message` 基类与 `DefaultMessage` 实现类,支æŒè‡ªå®šä¹‰åè®®æ ¼å¼ã€‚
---
## 2. 接å£å®šä¹‰
```csharp
public interface IMessage : IFrameMessage // 帧契约:TryParseHeader/ReadFrame/Body/SetBody/Build/BuildHeader ç‰
{
Boolean Reply { get; set; } // 是å¦å“应消æ¯
Boolean Error { get; set; } // æ˜¯å¦æœ‰é”™
Boolean OneWay { get; set; } // å•å‘请求
IPacket? Payload { get; set; } // 负载数æ®ï¼ˆæ¶ˆæ¯ä½“奿Ÿ„视图,æµå¼æ¨¡å¼ä¸º null)
IMessage CreateReply(); // æ ¹æ®è¯·æ±‚创建é…对å“应
}
```
### 2.1 æ ¸å¿ƒå±žæ€§
| 属性 | 说明 |
|------|------|
| `Reply` | `true` 表示å“应消æ¯ï¼Œ`false` è¡¨ç¤ºè¯·æ±‚æ¶ˆæ¯ |
| `Error` | `true` 表示处ç†è¿‡ç¨‹ä¸å‘生错误 |
| `OneWay` | `true` 表示å•å‘请求,ä¸éœ€è¦ç‰å¾…å“应 |
| `Payload` | 消æ¯è´Ÿè½½æ•°æ®ï¼Œç±»åž‹ä¸º `IPacket?`,å¯ä»¥æ˜¯ `ArrayPacket`ã€`OwnerPacket` ç‰ä»»æ„实现 |
### 2.2 æ ¸å¿ƒæ–¹æ³•
- **`CreateReply()`**ï¼šæ ¹æ®è¯·æ±‚消æ¯åˆ›å»ºé…对的å“应消æ¯ï¼Œç»§æ‰¿åºåˆ—å·ç‰å…³é”®å±žæ€§ã€‚仅请求消æ¯å¯è°ƒç”¨ã€‚
- **`ReadFrame(IPacket pk)`**ï¼ˆå¸§å¥‘çº¦ï¼‰ï¼šä»Žå®Œæ•´å¸§è§£æžæ¶ˆæ¯å¤´å’Œè´Ÿè½½ã€‚帧头å¯è·¨èŠ‚ç‚¹ï¼ˆé¦–æ®µä¸è¶³æ—¶è‡ªåŠ¨æ‹¼å…¥æ ˆç¼“å†²æ‹¼è¯»ï¼Œè´Ÿè½½å¯ç»§ç»ä¸ºé“¾å¼èŠ‚ç‚¹ï¼‰ã€‚
- **`Build()`**ï¼ˆå¸§å¥‘çº¦ï¼‰ï¼šå°†æ¶ˆæ¯æž„建为完整数æ®åŒ…,用于网络å‘é€ï¼›æž„建采用转移è¯ä¹‰ã€‚
---
## 3. 基类 Message
`Message` æä¾› `IMessage` 的基础实现:
```csharp
public class Message : IMessage
{
public Boolean Reply { get; set; }
public Boolean Error { get; set; }
public Boolean OneWay { get; set; }
public IPacket? Payload { get; set; } // 消æ¯ä½“奿Ÿ„视图
public LimitedReader? Body => _body; // 消æ¯ä½“读å–视图(帧契约)
public void Dispose() { ... }
protected virtual void Dispose(Boolean disposing) { ... }
public virtual IMessage CreateReply() { ... }
public virtual Boolean ReadFrame(IPacket pk) { ... }
public virtual IPacket? Build() { ... } // 转移è¯ä¹‰ï¼šæž„å»ºåŽæ¶ˆæ¯ä¸å†æŒæœ‰ä½“
public virtual void Reset() { ... }
}
```
### 3.1 释放机制
`Message.Dispose(disposing)` çš„æ ¸å¿ƒé€»è¾‘ï¼š
```csharp
protected virtual void Dispose(Boolean disposing)
{
if (disposing)
{
Payload.TryDispose(); // 安全释放 Payload
Payload = null;
}
}
```
`TryDispose` 是 NewLife 的通用扩展方法,检查对象是å¦å®žçް `IDisposable`,若是则调用 `Dispose()`。
---
## 4. DefaultMessage(SRMP æ ‡å‡†æ¶ˆæ¯ï¼‰
### 4.1 åè®®æ ¼å¼
```
1 Flag + 1 Sequence + 2 Length + N Payload
```
| å—æ®µ | å—节数 | 说明 |
|------|--------|------|
| Flag | 1 | 高 2 ä½ä¸ºæ¶ˆæ¯æ¨¡å¼ï¼ˆ00 请求/01 å•å‘/10 å“应/11 å“应+错误),低 6 ä½ä¸ºæ•°æ®ç±»åž‹ |
| Sequence | 1 | åºåˆ—å·ï¼Œç”¨äºŽè¯·æ±‚-å“应é…对 |
| Length | 2 | å°ç«¯å—节åºï¼Œè´Ÿè½½æ•°æ®é•¿åº¦ï¼ˆä¸å«å¤´éƒ¨ 4 å—节) |
| Payload | N | è´Ÿè½½æ•°æ® |
超大包支æŒï¼šå½“ Length 为 `0xFFFF` 时,åŽç» 4 å—节为实际长度。
### 4.2 示例
```
请求 Open: 01-01-04-00-"Open"
å“应 OK: 81-01-02-00-"OK"
```
---
## 5. 与 IOwnerPacket çš„è”åŠ¨é‡Šæ”¾è®¾è®¡ï¼ˆæ ¸å¿ƒäº®ç‚¹ï¼‰
### 5.1 问题背景
在 RPC / 网络通信ä¸ï¼Œåº•层需è¦é«˜æ•ˆæŽ¥æ”¶æ•°æ®ï¼š
1. 使用 `OwnerPacket` 从 `ArrayPool` 租用缓冲区,é¿å…é¢‘ç¹ GC。
2. å议解æžåŽï¼Œè´Ÿè½½æ•°æ®é€šè¿‡ `Slice` 切片共享底层缓冲区(零拷è´ï¼‰ã€‚
3. 负载被包装到 `IMessage.Payload` ä¸ï¼Œä¼ 递给上层业务代ç 。
**æ ¸å¿ƒé—®é¢˜**ï¼šè°æ¥é‡Šæ”¾æ± 化内å˜ï¼Ÿ
### 5.2 设计方案
```
底层网络接收
→ new OwnerPacket(bufferSize) // 从 ArrayPool 租用
→ socket.ReceiveAsync(...) // å¡«å……æ•°æ®
→ DefaultMessage.Read(ownerPacket) // è§£æžåè®®
→ Slice(4, len) // 切出共享切片给 Payload(引用计数)
→ 返回 IMessage 给上层
→ 上层使用完毕
→ msg.Dispose() // è‡ªåŠ¨å½’è¿˜æ± åŒ–å†…å˜
```
**释放链路**:
```
IMessage.Dispose()
→ Message.Dispose(disposing: true)
→ Payload.TryDispose()
→ OwnerPacket.Dispose()
→ ArrayPool<Byte>.Shared.Return(buffer)
→ Next.TryDispose() // 递归释放链å¼èŠ‚ç‚¹
```
### 5.3 设计巧妙之处
1. **逿˜Žé‡Šæ”¾**:上层代ç åªéœ€ `using var msg = ...`ï¼Œæ— éœ€çŸ¥é“ `Payload` 的具体实现类型。`TryDispose` 扩展方法安全处ç†äº†æ‰€æœ‰æƒ…况:
- `ArrayPacket`ï¼ˆå€¼ç±»åž‹ï¼Œéž `IDisposable`ï¼‰ï¼šè·³è¿‡ï¼Œæ— æ“作。
- `OwnerPacket`(引用类型,`IDisposable`):调用 `Dispose()`,归还 `ArrayPool` 缓冲区。
- `null`:安全跳过。
2. **引用计数共享**:`DefaultMessage.Read` 用 `Slice(offset, count)` 把负载切为共享切片交给 `Payload`ï¼ˆæ¯æ®µé€’å¢žè®¡æ•°ï¼‰ã€‚æ‰€æœ‰å¥æŸ„å„è‡ªé‡Šæ”¾ã€æœ€åŽä¸€ä¸ªå½’还;原始包ä¸è¢«ä¿®æ”¹ï¼Œå¯ç»§ç»ä½¿ç”¨å¹¶åŒæ ·éœ€è¦é‡Šæ”¾ã€‚
3. **链å¼é€’归释放**:`OwnerPacket.Dispose` 会自动释放 `Next` 链节点。å³ä½¿å议解æžäº§ç”Ÿäº†å¤šæ®µé“¾å¼è´Ÿè½½ï¼ˆå¦‚跨包拼接),一次 `Dispose` å³å¯å…¨éƒ¨å½’还。
4. **接å£åˆ†å±‚精妙**:
- `IPacket` ä¸è¦æ±‚ `IDisposable`——值类型实现(`ArrayPacket`ã€`MemoryPacket`ã€`ReadOnlyPacket`ï¼‰ä¿æŒè½»é‡ã€‚
- `IOwnerPacket : IPacket, IDisposable`â€”â€”ä»…æ± åŒ–å®žçŽ°éœ€è¦é‡Šæ”¾ã€‚
- `IMessage : IDisposable`——上层统一释放入å£ã€‚
5. **ä¸Žå¯¹è±¡æ± å¤ç”¨é…åˆ**:`Message.Reset()` å¯å°†æ¶ˆæ¯çŠ¶æ€æ¸…零并归还 `Payload` 引用(交付å‰é¡»å…ˆæ‘˜é™¤å·²è½¬ç§»çš„负载,如 `Payload=null`),适用于消æ¯å¯¹è±¡æ± 场景。
### 5.4 使用示例
```csharp
// ===== 场景 1ï¼šæ ‡å‡† RPC æŽ¥æ”¶å¤„ç† =====
var raw = new OwnerPacket(4096);
var count = await socket.ReceiveAsync(raw.GetMemory());
raw.Resize(count);
using var msg = new DefaultMessage();
msg.Read(raw);
// Payload æŒæœ‰å…±äº«åˆ‡ç‰‡ï¼ˆå¼•用计数),raw å¯ç»§ç»ä½¿ç”¨ã€åŒæ ·éœ€è¦é‡Šæ”¾
var response = ProcessRequest(msg);
// using å—退出åŽè‡ªåŠ¨å½’è¿˜æ± åŒ–å†…å˜
// ===== 场景 2:手动管ç†ç”Ÿå‘½å‘¨æœŸ =====
var msg = new DefaultMessage();
msg.Read(rawPacket);
try
{
// 使用消æ¯...
var data = msg.Payload.ToStr();
}
finally
{
msg.Dispose(); // 归还 Payload çš„æ± åŒ–å†…å˜
}
// ===== 场景 3:返回 IMessage 给上层 =====
public IMessage Receive()
{
var raw = new OwnerPacket(bufferSize);
var count = socket.Receive(raw.GetSpan());
raw.Resize(count);
var msg = new DefaultMessage();
msg.Read(raw);
return msg; // 所有æƒè½¬ç§»ç»™è°ƒç”¨æ–¹ï¼Œè°ƒç”¨æ–¹è´Ÿè´£ Dispose
}
```
---
## 6. 线程安全性
- `Message` åŠå…¶åç±»**䏿˜¯çº¿ç¨‹å®‰å…¨çš„**。
- 消æ¯å®žä¾‹åº”在å•一线程/任务ä¸ä½¿ç”¨ï¼Œä¸åº”跨线程共享。
- æ± åŒ–å¤ç”¨æ—¶ï¼Œéœ€ç¡®ä¿å–出åŽç‹¬å ä½¿ç”¨ï¼Œç”¨å®ŒåŽ `Reset()` å†å½’还。
---
## 7. 最佳实践
| 场景 | 建议 |
|------|------|
| æŽ¥æ”¶æ¶ˆæ¯ | 使用 `using var msg = ...` ç¡®ä¿è‡ªåŠ¨é‡Šæ”¾ |
| 返回消æ¯ç»™ä¸Šå±‚ | æ–‡æ¡£è¯´æ˜Žè°ƒç”¨æ–¹éœ€è¦ `Dispose` |
| 消æ¯å¯¹è±¡æ± | 使用 `Reset()` é‡ç½®çжæ€ï¼Œ`Dispose()` 释放 Payload |
| 多次切片 | å„切片共享引用计数,用毕å„自 `Dispose`(最åŽä¸€ä¸ªå½’è¿˜æ± ï¼‰ |
| é•¿æœŸæŒæœ‰è´Ÿè½½ | å…ˆ `Clone()` å¤åˆ¶æ•°æ®ï¼Œé¿å…æŒæœ‰æ± åŒ–å†…å˜ |
---
## 8. 兼容性说明
- æœ¬ç»„ä»¶å¤šç›®æ ‡æ¡†æž¶ï¼ˆä»Ž `net45` 到更高版本)。
- `IMessage : IDisposable` åœ¨æ‰€æœ‰ç›®æ ‡æ¡†æž¶ä¸Šå¯ç”¨ã€‚
- `TryDispose` æ‰©å±•æ–¹æ³•æ— æ¡†æž¶é™åˆ¶ã€‚
---
## 9. å˜æ›´è®°å½•
- åˆå§‹ç‰ˆæœ¬ï¼šåŸºäºŽ `IMessage.cs` å’Œ `DefaultMessage.cs` 现状编写。
- é‡ç‚¹è¯´æ˜Ž `IMessage.Dispose` 与 `IOwnerPacket` çš„è”动释放设计。
|