为内存映射文件模块单独弄设计的,用于MMF上实现多进程见消息传输;测试中发现命名管道的效率很差,性能还不如用Socket Loopback,决定试试MMF。
测试了下性能还不错,在我机器上:测试每个包4K 每秒可以执行近100w写入+读取操作,也就是每秒8G的数据操作,可以满足觉大多数业务需求。
唯一不足在于全局锁,无法利用多核;不过可以使用多个MMF块绕过,无锁编程还是太复杂了。
192 }
测试代码:
}
附:
API 代码,.Net自带MMF库有些问题:
public sealed class Win32API { [DllImport("Kernel32", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpAttributes, FileMapProtection flProtect, Int32 dwMaxSizeHi, Int32 dwMaxSizeLow, string lpName); public static IntPtr CreateFileMapping(System.IO.FileStream File, FileMapProtection flProtect, Int64 ddMaxSize, string lpName) { int Hi = (Int32)(ddMaxSize / Int32.MaxValue); int Lo = (Int32)(ddMaxSize % Int32.MaxValue); return CreateFileMapping(File.SafeFileHandle.DangerousGetHandle(), IntPtr.Zero, flProtect, Hi, Lo, lpName); } public static IntPtr CreateFileMapping(SafeFileHandle handle, FileMapProtection flProtect, Int64 ddMaxSize, string lpName) { int Hi = (Int32)(ddMaxSize / Int32.MaxValue); int Lo = (Int32)(ddMaxSize % Int32.MaxValue); return CreateFileMapping(handle.DangerousGetHandle(), IntPtr.Zero, flProtect, Hi, Lo, lpName); } [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr OpenFileMapping(FileMapAccess DesiredAccess, bool bInheritHandle, string lpName); public static unsafe IntPtr Memcpy(byte* dest, byte* src, int len) { return memcpy(new IntPtr(dest), new IntPtr(src), len); } [DllImport("msvcrt.dll", SetLastError = false)] public static extern IntPtr memcpy(IntPtr dest, IntPtr src, int len); [Flags] public enum FileMapProtection : uint { PageReadonly = 0x02, PageReadWrite = 0x04, PageWriteCopy = 0x08, PageExecuteRead = 0x20, PageExecuteReadWrite = 0x40, SectionCommit = 0x8000000, SectionImage = 0x1000000, SectionNoCache = 0x10000000, SectionReserve = 0x4000000, } [DllImport("Kernel32", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr MapViewOfFile(IntPtr hFileMapping, FileMapAccess dwDesiredAccess, Int32 dwFileOffsetHigh, Int32 dwFileOffsetLow, Int32 dwNumberOfBytesToMap); public static IntPtr MapViewOfFile(IntPtr hFileMapping, FileMapAccess dwDesiredAccess, Int64 ddFileOffset, Int32 dwNumberOfBytesToMap) { int Hi = (Int32)(ddFileOffset / Int32.MaxValue); int Lo = (Int32)(ddFileOffset % Int32.MaxValue); return MapViewOfFile(hFileMapping, dwDesiredAccess, Hi, Lo, dwNumberOfBytesToMap); } [Flags] public enum FileMapAccess : uint { FileMapCopy = 0x0001, FileMapWrite = 0x0002, FileMapRead = 0x0004, FileMapAllAccess = 0x001f, fileMapExecute = 0x0020, } [DllImport("kernel32.dll")] public static extern bool FlushViewOfFile(IntPtr lpBaseAddress, Int32 dwNumberOfBytesToFlush); [DllImport("kernel32")] public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress); [DllImport("kernel32", SetLastError = true)] public static extern bool CloseHandle(IntPtr hFile); [DllImport("kernel32.dll")] public static extern void GetSystemInfo([MarshalAs(UnmanagedType.Struct)] ref SYSTEM_INFO lpSystemInfo); [StructLayout(LayoutKind.Sequential)] public struct SYSTEM_INFO { public _PROCESSOR_INFO_UNION uProcessorInfo; public uint dwPageSize; public IntPtr lpMinimumApplicationAddress; public IntPtr lpMaximumApplicationAddress; public IntPtr dwActiveProcessorMask; public uint dwNumberOfProcessors; public uint dwProcessorType; public uint dwAllocationGranularity; public ushort dwProcessorLevel; public ushort dwProcessorRevision; } [StructLayout(LayoutKind.Explicit)] public struct _PROCESSOR_INFO_UNION { [FieldOffset(0)] public uint dwOemId; [FieldOffset(0)] public ushort wProcessorArchitecture; [FieldOffset(2)] public ushort wReserved; } }
public unsafe class MMFMessageQueue { private volatile void* _shm; private volatile int* _lck; private volatile int* _head; private volatile int* _tail; private LoopMemoryStream _ms; private int _size; private int _realSize; private string _mmfName; public enum QueueResult { EMPTY, SUCCESS, FULL, } public int Count { get; set; } public MMFMessageQueue(string mmfName,int size) { _mmfName = mmfName; _size = size; _realSize = _size - sizeof(int*) * 3; _shm = GetOrCreateMMFView(); _lck = (int*)_shm; _ms = new LoopMemoryStream((byte*)_shm + sizeof(int*), _size - sizeof(int*)); //_ms.ClearData();//打开的同时清理 _head = _ms.Head; _tail = _ms.Tail; } private void* GetOrCreateMMFView() { IntPtr mmf = Win32API.OpenFileMapping(Win32API.FileMapAccess.FileMapAllAccess, false, _mmfName); if (mmf == IntPtr.Zero) { mmf = Win32API.CreateFileMapping(new SafeFileHandle(new IntPtr(-1), true), Win32API.FileMapProtection.PageReadWrite, _size, _mmfName); if (mmf == IntPtr.Zero) throw new Win32Exception(); } IntPtr mvf = Win32API.MapViewOfFile(mmf, Win32API.FileMapAccess.FileMapWrite | Win32API.FileMapAccess.FileMapRead, 0, _size); if (mvf == IntPtr.Zero) throw new Win32Exception(); return mvf.ToPointer(); } //SpinWait 每20次会有一次系统时间片切换 //清理数据(挂的时候数据一致性是问题,全部删掉) //然后相当于获取锁往下执行 //测试发现Count=10w时,wait时间为5s private void TryEnterLock() { SpinWait wait = new SpinWait(); int head = *_head; int tail = *_tail; int count = 0; while (Interlocked.CompareExchange(ref *_lck, 1, 0) != 0) { wait.SpinOnce(); count++; if (head != *_head || tail != *_tail) { head = *_head; tail = *_tail; count = 0; } if (count > 100000) { Console.WriteLine("ClearData"); _ms.ClearData(); break; } } } private void ExitLock() { *_lck = 0; } public QueueResult TryAppend(byte[] data) { return TryAppend(data, 0, data.Length); } public QueueResult TryAppend(byte[] data, int offsize, int length) { int realsize = 4 + length; if (realsize > _realSize) throw new OverflowException(); TryEnterLock(); if (_ms.AvailLen < realsize) { ExitLock(); return QueueResult.FULL; } else { _ms.Write(data, 0, length); ExitLock(); return QueueResult.SUCCESS; } } public QueueResult TryDequeue(out byte[] result) { result = null; TryEnterLock(); if (_ms.IsEmpty) { ExitLock(); return QueueResult.EMPTY; } else { result = _ms.Read(); ExitLock(); return QueueResult.SUCCESS; } } }
/// <summary> /// 每个通道两个MMF队列,一个发送,一个接收 /// </summary> class RpcMMFMessageQueue { private const int NotityTimes = 1; private MMFMessageQueue _repQueue; private MMFMessageQueue _rspQueue; private Thread _thread; private EventWaitHandle _reqWait; private EventWaitHandle _rspWait; private RpcMMFMode _mode; private int _count; public Action<RpcMMFMessageQueue, byte[]> ReceiveData; public RpcMMFMessageQueue(string mmfName, RpcMMFMode mode) { _mode = mode; string reqName = string.Format("rpc_mmf_{0}_req", mmfName); string rspName = string.Format("rpc_mmf_{0}q_rsp", mmfName); _repQueue = new MMFMessageQueue(reqName, RpcMMFConfiguration.Current.MMFSize); _rspQueue = new MMFMessageQueue(rspName, RpcMMFConfiguration.Current.MMFSize); _reqWait = RpcMMFHelper.GetOrCreateWaitHandle(reqName+"_wait"); _rspWait = RpcMMFHelper.GetOrCreateWaitHandle(rspName+"_wait"); _thread = new Thread(DequeueProc); _thread.IsBackground = true; _thread.Start(); } public void Enqueue(byte[] data) { Enqueue(data, 0, data.Length); } public void Enqueue(byte[] data, int offsize, int length) { try { var queue = _mode == RpcMMFMode.RpcClient ? _repQueue : _rspQueue; if (queue.TryAppend(data, offsize, length) == MMFMessageQueue.QueueResult.FULL) { throw new RpcMMFException("MMF Queue Full"); } } catch (OverflowException) { throw new RpcMMFException("MMF Queue Full"); } if (Interlocked.Increment(ref _count) == NotityTimes) { _count = 0; if (_mode == RpcMMFMode.RpcClient) _reqWait.Set(); else _rspWait.Set(); } } private void DequeueProc() { while (true) { byte[] data; var queue = _mode == RpcMMFMode.RpcServer ? _repQueue : _rspQueue; if (queue.TryDequeue(out data) == MMFMessageQueue.QueueResult.EMPTY) { if (_mode == RpcMMFMode.RpcServer) _reqWait.WaitOne(1); else _rspWait.WaitOne(1); } else { if (ReceiveData != null) ReceiveData(this, data); } } } }
class RpcMMFHelper { public static EventWaitHandle GetOrCreateWaitHandle(string name) { EventWaitHandle set = TryCreateWaitHandle(name); if (set == null) set = TryOpenWaitHandle(name); if (set == null) throw new Exception(string.Format("can't open or create eventWaitHandle:{0}", name)); return set; } public unsafe static byte[] GetRpcPacket<T>(T header, RpcBodyBuffer bodyBuffer) { MemoryStream hms = ProtoBufSerializer.Serialize<T>(header); short headLen = (short)hms.Length; int bodyLen = bodyBuffer == null ? 0 : bodyBuffer.GetSize(); int totalLen = 2 + headLen + bodyLen; byte[] buffer = new byte[totalLen]; fixed (byte* p = buffer) { *(short*)p = headLen; hms.Read(buffer, 2, headLen); if (bodyLen > 0) { byte[] body = bodyBuffer.GetByteArray(); fixed (byte* src = body) { Win32API.Memcpy(p + 2 + headLen, src, bodyLen); } } } return buffer; } private static EventWaitHandle TryOpenWaitHandle(string name) { EventWaitHandle set = null; try { set = (EventWaitHandle)EventWaitHandle.OpenExisting(name); } catch (WaitHandleCannotBeOpenedException ex) { } return set; } private static EventWaitHandle TryCreateWaitHandle(string name) { EventWaitHandle set = null; try { set = new EventWaitHandle(false, EventResetMode.AutoReset, name); } catch (WaitHandleCannotBeOpenedException ex) { } return set; } }
Rpc请求封包:
/// <summary> /// 2byte head Length + header+ body /// </summary> unsafe class RpcMMFRequestPacket { private RpcRequest _request; private int _sequence; public RpcRequest Request { get { return _request; } } public int Sequence { get { return _sequence; } } public RpcMMFRequestPacket(byte[] data) { if (data.Length <= 2) throw new ArgumentException(); short headLen; fixed (byte* p = data) { headLen = *(short*)p; } MemoryStream hms = new MemoryStream(data, 2, headLen); var header = ProtoBufSerializer.Deserialize<RpcMMFRequestHeader>(hms); var request = new RpcRequest(); request.ContextUri = header.ContextUri; request.FromComputer = header.FromComputer; request.FromService = header.FromService; request.Method = header.Method; request.Service = header.Service; _sequence = header.Sequence; int bodyLen = data.Length - 2 - headLen; if (bodyLen > 0) { MemoryStream bms = new MemoryStream(data, 2 + headLen, bodyLen); request.SetBodyStream(bms, bodyLen); } else request.BodyBuffer = RpcBodyBuffer.EmptyBody; _request = request; } public static void WriteRequest(RpcMMFMessageQueue queue, IRpcMMFSendingPacket packet) { RpcMMFRequestHeader header = packet.RequestHeader; byte[] buffer = RpcMMFHelper.GetRpcPacket<RpcMMFRequestHeader>(header, packet.BodyBuffer); queue.Enqueue(buffer); } }
RRC 请求头描述信息:
[ProtoContract] public class RpcMMFRequestHeader { [ProtoMember(1, IsRequired = true)] public int Sequence; [ProtoMember(2)] public string ContextUri; [ProtoMember(3)] public string FromComputer; [ProtoMember(4)] public string FromService; [ProtoMember(5)] public string Service; [ProtoMember(6)] public string Method; public unsafe void Serialize(Stream stream) { byte[] b = new byte[4]; fixed (byte* p = b) *(int*)p = Sequence; stream.Write(b, 0, 4); WriteStr(stream, ContextUri); WriteStr(stream, FromComputer); WriteStr(stream, FromService); WriteStr(stream, Service); WriteStr(stream, Method); } private static unsafe void WriteStr(Stream stream, string str) { byte len = 0; if (!string.IsNullOrEmpty(str)) len = (byte)str.Length; if (len > 0) { byte[] data = UTF8Encoding.UTF8.GetBytes(str); if (data.Length > byte.MaxValue) throw new NotSupportedException(); len = (byte)data.Length; stream.WriteByte(len); stream.Write(data, 0, len); } else stream.WriteByte(0); } private static unsafe string ReadStr(Stream stream) { byte len = (byte)stream.ReadByte(); if (len == 0) return null; byte[] data = new byte[len]; stream.Read(data, 0, len); return UTF8Encoding.UTF8.GetString(data); } public MemoryStream Serialize() { MemoryStream ms = new MemoryStream(); Serialize(ms); return ms; } public static unsafe RpcMMFRequestHeader Deserialize(Stream stream) { RpcMMFRequestHeader header = new RpcMMFRequestHeader(); byte[] b = new byte[4]; stream.Read(b, 0, 4); fixed (byte* p = b) header.Sequence = *(int*)p; header.ContextUri = ReadStr(stream); header.FromComputer = ReadStr(stream); header.FromService = ReadStr(stream); header.Service = ReadStr(stream); header.Method = ReadStr(stream); return header; } }
RPC 应答封包:
/// <summary> /// 2byte head Length + header+ body /// </summary> unsafe class RpcMMFResponsePacket { private RpcResponse _response; private int _sequence; public int Sequence { get { return _sequence; } } public RpcResponse Response { get { return _response; } } public RpcMMFResponsePacket(byte[] data) { if (data.Length <= 2) throw new ArgumentException(); short headLen; fixed (byte* p = data) { headLen = *(short*)p; } MemoryStream hms = new MemoryStream(data, 2, headLen); var header = ProtoBufSerializer.Deserialize<RpcMMFResponseHeader>(hms); int bodyLen = data.Length - 2 - headLen; RpcBodyBuffer body = null; if (bodyLen > 0) { MemoryStream bs = new MemoryStream(data, 2 + headLen, bodyLen, false); body = new RpcBodyBuffer(bs, bodyLen); } else body = RpcBodyBuffer.EmptyBody; _sequence = header.Sequence; _response = new RpcResponse((RpcErrorCode)header.ResponseCode, body); } public static void WriteResponse(RpcMMFMessageQueue queue, IRpcMMFSendingPacket packet) { RpcMMFResponseHeader header = packet.ResponseHeader; byte[] buffer = RpcMMFHelper.GetRpcPacket<RpcMMFResponseHeader>(header, packet.BodyBuffer); queue.Enqueue(buffer); } }
应答头:
[ProtoContract] public class RpcMMFResponseHeader { [ProtoMember(1, IsRequired = true)] public int Sequence; [ProtoMember(2, IsRequired = true)] public int ResponseCode; [ProtoMember(3, IsRequired = true)] public int BodyLength; }
事务管理:
static class RpcMMFTransactionManager { private static Thread _thread; private static ConcurrentDictionary<int, RpcMMFClientTransaction> _dict; private static Stopwatch _watch; private static object _sync = new object(); public static void Initialize() { if (_dict == null) lock (_sync) { _watch = new Stopwatch(); _watch.Start(); _dict = new ConcurrentDictionary<int, RpcMMFClientTransaction>(16, 64 * 1024); _thread = new Thread(MonitorProc); _thread.IsBackground = true; _thread.Start(); } } public static void BeginTransaction(RpcMMFClientTransaction tx) { tx.Tickets = _watch.ElapsedMilliseconds; if (!_dict.TryAdd(tx.Sequence, tx)) { tx.SendFailed(RpcErrorCode.SendFailed, null); _tracing.ErrorFmt("sequence key same,{0}", ObjectHelper.DumpObject(tx.RequestHeader)); } } public static void EndTransaction(int seq, RpcResponse response) { RpcMMFClientTransaction tx; if (_dict.TryRemove(seq, out tx)) { tx.Callback(response); } else { _tracing.ErrorFmt("out of band sequence:{0},{1}", seq, ObjectHelper.DumpObject(response)); } } private static void MonitorProc() { while (true) { try { long currentTickets = _watch.ElapsedMilliseconds; foreach (var kv in _dict) { if (kv.Value.Tickets + (long)kv.Value.Timeout < currentTickets) { RpcResponse rsp = new RpcResponse(RpcErrorCode.TransactionTimeout, null); EndTransaction(kv.Key, rsp); _tracing.Error("transation timeout"); } } } catch (ThreadAbortException) { Thread.ResetAbort(); } catch (Exception ex) { _tracing.ErrorFmt(ex, "MonitorProc Error"); } Thread.Sleep(1000); } } }
主要代码都贴上了,不是完整的Rpc,大家集成大自己Rpc 框架试试。