高併發網絡服務中,頻繁 malloc/free 或 new/delete 緩衝區會導致:
本文實現**池化緩衝區**,支援**引用計數/歸還池**、**ArraySegment 零拷貝切片**、**多池負載均衡**。
public class DataBuffer : IDisposable {
public byte[] Data; // 底層字節數組
private int _length; // 數組容量
private int _position; // 讀寫游標
internal int _count; // 有效數據長度
internal BufferPool Pool; // 所屬池(歸還用)
public DataBuffer(int length) { // 分配新緩衝區
_length = length;
Data = new byte[length];
_position = 0;
_count = length;
}
public DataBuffer(byte[] data) { // 包裝現有數組
Data = data;
_length = data.Length;
_count = data.Length;
}
// 寫入:返回實際寫入字節數,自動處理邊界
public int Write(byte[] src, int offset = 0) {
int avail = _length - _position;
int toWrite = Math.Min(src.Length - offset, avail);
if (toWrite > 0) {
Buffer.BlockCopy(src, offset, Data, _position, toWrite);
_position += toWrite;
_count = Math.Max(_count, _position);
}
return toWrite;
}
// 讀取:返回 ArraySegment,零拷貝,推進游標
public ArraySegment<byte> Read(int count) {
int avail = _count - _position;
int toRead = Math.Min(count, avail);
var seg = new ArraySegment<byte>(Data, _position, toRead);
_position += toRead;
return seg;
}
// 重置游標,不清零數據
public void Seek(int pos = 0) => _position = pos;
// 獲取整個有效區段
public ArraySegment<byte> GetSegment() => new ArraySegment<byte>(Data, 0, _count);
// 歸還池
public void Dispose() {
if (Pool != null) {
_position = 0;
_count = 0;
Pool.Push(this);
Pool = null;
}
}
}
public class BufferPool : IDisposable {
private static List<BufferPool> _pools = new List<BufferPool>();
private static int _nextPoolIndex = 0;
private readonly Queue<DataBuffer> _buffers = new Queue<DataBuffer>();
private readonly int _bufferLength;
private bool _disposed;
// 靜態工廠:創建多個池,分散鎖競爭
public static void Setup(int poolCount, int buffersPerPool, int bufferLength = 2048) {
lock (_pools) {
for (int i = 0; i < poolCount; i++)
_pools.Add(new BufferPool(buffersPerPool, bufferLength));
}
}
public static void Clean() {
lock (_pools) {
foreach (var p in _pools) p.Dispose();
_pools.Clear();
}
}
// 輪詢獲取池,簡單負載均衡
public static BufferPool GetPool() {
lock (_pools) {
if (_pools.Count == 0) return null;
var pool = _pools[_nextPoolIndex];
_nextPoolIndex = (_nextPoolIndex + 1) % _pools.Count;
return pool;
}
}
private BufferPool(int count, int bufferLength) {
_bufferLength = bufferLength;
for (int i = 0; i < count; i++)
_buffers.Enqueue(new DataBuffer(bufferLength) { Pool = this });
}
// 取緩衝區:池空則自動分配新的(避免阻塞)
public DataBuffer Pop() {
lock (_buffers) {
if (_buffers.Count > 0) return _buffers.Dequeue();
}
return new DataBuffer(_bufferLength) { Pool = this }; // 擴容
}
// 歸還緩衝區
public void Push(DataBuffer buf) {
lock (_buffers) {
if (!_disposed) _buffers.Enqueue(buf);
}
}
public void Dispose() {
lock (this) {
if (_disposed) return;
_disposed = true;
lock (_buffers) {
while (_buffers.Count > 0) {
var b = _buffers.Dequeue();
b.Pool = null; // 斷開關聯,防止再次歸還
}
}
}
}
}
// 1. 接收端:從池取緩衝區,直接 Socket.Receive 寫入
var pool = BufferPool.GetPool();
var buf = pool.Pop();
int received = socket.Receive(buf.Data, 0, buf.Data.Length, SocketFlags.None);
buf._count = received;
buf._position = 0;
// 2. 解碼器:Read() 返回 ArraySegment,無拷貝傳給協議層
while (buf._position < buf._count) {
var segment = buf.Read(messageHeaderSize);
// 解析頭部,獲取 body 長度
var bodySeg = buf.Read(bodyLen);
// 直接將 segment 遞交給業務處理器(引用計數或複製決策)
ProcessMessage(bodySeg);
}
// 3. 業務處理完,Dispose() 歸還池
buf.Dispose();
// 4. 發送端:同理 Pop -> Write -> Socket.Send(seg) -> Dispose
var sendBuf = pool.Pop();
sendBuf.Write(headerBytes);
sendBuf.Write(bodyBytes);
socket.Send(sendBuf.GetSegment());
sendBuf.Dispose();
| 決策 | 理由 |
|---|---|
| ArraySegment<byte> 而非 byte[] | 精確描述「緩衝區內的一段有效數據」,支援零拷貝切片、多段組裝(Scatter/Gather IO) |
| IDisposable + Pool 回引用 | using (var buf = pool.Pop()) 模式,確保異常時也能歸還,防洩漏 |
| 多池 + 輪詢 | 單一隊列在高併發下鎖競爭劇烈,分池將鎖粒度降低 N 倍 |
| Pop 自動擴容 | 池耗盡時分配新緩衝區,避免業務阻塞;歸還時自動回收,池大小動態適應峰值 |
| 內部字段 _position/_count | 封裝讀寫狀態,外部不可隨意修改,保證緩衝區一致性 |
WriteInt32、ReadString 等類型化讀寫,支援大小端轉換、可變長整數。_refCount,允許同一緩衝區在多層處理器間共享,最後一釋放歸還池。ArrayPool<byte>.Shared (CoreCLR) 替代自建。sendfile/splice、Windows TransmitFile,配合緩衝區指針直達網卡。DataBuffer + BufferPool 構成了高性能網絡層的**內存管理基石**。核心價值:**池化消除分配開銷、ArraySegment 實現零拷貝切片、IDisposable 保證歸還安全、多池消除鎖競爭**。在 .NET Core 2.1+ 中,推薦直接使用 System.Buffers.ArrayPool<byte>.Shared 替代自建池,並配合 Memory<byte>/ReadOnlySequence<byte> 實現更現代的管道處理(System.IO.Pipelines)。