更多代码细节,球球各位观众老爷给鄙人的开源项目点个Star,持续更新中~ [项目开源地址]
使用值类型Struct降低GC
public struct MessageBlock
{
public BaseConnection sender;
public IMessage message;
}
namespace Common.Summer
{
///
/// 线程安全
///
/// 泛型类型
public class BaseManager<T> where T : new()
{
private static T? _instance;
private static object _lock = new();
public static T Instance
{
get
{
if (_instance != null) return _instance;
lock (_lock)
{
_instance ??= new T();
}
return _instance;
}
}
}
}
Channel是核心数据结构,线程安全的数据分发,ConcurrentDictionary,线程安全的字典用于处理消息回调,Channel作为消息队列的数据结构载体,CancellationTokenSource用于处理多线程Task的异常终止,workCount是此时工作的线程数量,使用原子操作来保证线程安全
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Channels;
using Google.Protobuf;
using Serilog;
namespace Common.Summer.Network
{
public struct MessageBlock
{
public BaseConnection Sender;
public IMessage Message;
}
public class MessageRouter : BaseManager<MessageRouter>
{
private static readonly MethodInfo FireMethod = typeof(MessageRouter).GetMethod(
nameof(Fire), BindingFlags.NonPublic | BindingFlags.Instance); //反射获取Fire方法
private static readonly ConcurrentDictionary<Type, Action<MessageRouter, BaseConnection, IMessage>>
FireDelegates = new();
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> MessagePropertiesCache = new();
private readonly Channel<MessageBlock> _messageChannel = Channel.CreateUnbounded<MessageBlock>();
private readonly ConcurrentDictionary<string, Delegate> _messageHandlers = new();
private CancellationTokenSource _cts;
private int _activeWorkers;
public bool Running { get; private set; }
public delegate void MessageHandler<in T>(BaseConnection sender, T msg) where T : IMessage;
public void Subscribe<T>(MessageHandler<T> handler) where T : IMessage
{
var typeKey = typeof(T).FullName;
_messageHandlers.AddOrUpdate(
typeKey, _ => handler,
(_, existing) => Delegate.Combine(existing, handler));
Log.Verbose("Subscribed to {MessageType} - Current handlers: {HandlerCount}",
typeof(T).Name,
((MessageHandler<T>)_messageHandlers[typeKey])?.GetInvocationList().Length ?? 0);
}
public void Unsubscribe<T>(MessageHandler<T> handler) where T : IMessage
{
var typeKey = typeof(T).FullName;
_messageHandlers.AddOrUpdate(
typeKey, _ => null,
(_, existing) => Delegate.Remove(existing, handler));
if (_messageHandlers.TryGetValue(typeKey, out var current) && current == null)
{
_messageHandlers.TryRemove(typeKey, out _);
}
}
private void Fire<T>(BaseConnection sender, T message) where T : IMessage
{
if (!_messageHandlers.TryGetValue(typeof(T).FullName, out var handler)) return;
try
{
((MessageHandler<T>)handler)?.Invoke(sender, message);
}
catch (Exception ex)
{
Log.Error(ex, "Error processing {MessageType} message", typeof(T).Name);
}
}
public void PostMessage(BaseConnection sender, IMessage message)
{
try
{
if (!_messageChannel.Writer.TryWrite(new MessageBlock { Sender = sender, Message = message }))
{
Log.Warning("Message queue full, message dropped");
}
}
catch (ChannelClosedException ex)
{
Log.Error(ex, "Failed to post message to closed channel");
}
}
public void Start(int concurrencyLevel = 8)
{
if (Running) return;
Running = true;
concurrencyLevel = Math.Clamp(concurrencyLevel, 1, Environment.ProcessorCount * 2);
_cts = new CancellationTokenSource();
for (var i = 0; i < concurrencyLevel; i++)
{
Task.Factory.StartNew(
() => ProcessMessagesAsync(_cts.Token),
_cts.Token,
TaskCreationOptions.LongRunning | TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);
}
}
public async Task StopAsync()
{
if (!Running) return;
Running = false;
_cts.Cancel();
_messageChannel.Writer.Complete();
while (_activeWorkers > 0)
{
await Task.Delay(50);
}
_cts.Dispose();
_cts = null;
}
private async Task ProcessMessagesAsync(CancellationToken ct)
{
Interlocked.Increment(ref _activeWorkers);
try
{
while (await _messageChannel.Reader.WaitToReadAsync(ct).ConfigureAwait(false))
while (_messageChannel.Reader.TryRead(out var message))
ProcessMessage(message.Sender, message.Message);
}
catch (OperationCanceledException)
{
}
finally
{
Interlocked.Decrement(ref _activeWorkers);
Log.Debug("Worker thread exiting");
}
}
private readonly ConcurrentQueue<(IMessage Message, BaseConnection Sender)> queue = new(); //线程安全
///
/// bfs遍历反射(也可以通过dfs减少代码量)
///
private void ProcessMessage(BaseConnection sender, IMessage message)
{
queue.Clear(); //清空上次残留的消息
queue.Enqueue((message, sender));
while (!queue.IsEmpty)
{
if (!queue.TryDequeue(out var result)) continue;
var (currentMessage, currentSender) = result;
TriggerMessageHandlers(currentSender, currentMessage);
var messageType = currentMessage.GetType();
var properties = MessagePropertiesCache.GetOrAdd(messageType, t =>
t.GetProperties()
.Where(p => !p.Name.Equals("Parser") &&
!p.Name.Equals("Descriptor") &&
typeof(IMessage).IsAssignableFrom(p.PropertyType))
.ToArray());
foreach (var property in properties)
{
if (property.GetValue(currentMessage) is IMessage childMessage)
{
queue.Enqueue((childMessage, currentSender));
}
}
}
}
private void TriggerMessageHandlers(BaseConnection sender, IMessage message)
{
var messageType = message.GetType();
var handler = FireDelegates.GetOrAdd(messageType, type =>
{
var method = FireMethod.MakeGenericMethod(type);
return CreateHandlerDelegate(method);
});
handler(this, sender, message);
}
private static Action<MessageRouter, BaseConnection, IMessage> CreateHandlerDelegate(MethodInfo method)
{
var routerParam = Expression.Parameter(typeof(MessageRouter));
var senderParam = Expression.Parameter(typeof(BaseConnection));
var messageParam = Expression.Parameter(typeof(IMessage));
var convertedMessage = Expression.Convert(messageParam, method.GetParameters()[1].ParameterType);
var callExpr = Expression.Call(
routerParam,
method,
senderParam,
convertedMessage);
return Expression.Lambda<Action<MessageRouter, BaseConnection, IMessage>>(
callExpr,
routerParam,
senderParam,
messageParam).Compile();
}
}
}