15 changed files with 1082 additions and 287 deletions
@ -0,0 +1,99 @@ |
|||
using System.Collections.Concurrent; |
|||
using Microsoft.Extensions.Logging; |
|||
using CellularManagement.WebSocket.Models; |
|||
|
|||
namespace CellularManagement.WebSocket.Connection; |
|||
|
|||
/// <summary>
|
|||
/// 连接管理协调器
|
|||
/// 负责协调连接管理器和消息队列管理器的工作
|
|||
/// </summary>
|
|||
public class ConnectionManagerCoordinator |
|||
{ |
|||
private readonly IWebSocketConnectionManager _connectionManager; |
|||
private readonly IWebSocketMessageQueueManager _messageQueueManager; |
|||
private readonly ILogger<ConnectionManagerCoordinator> _logger; |
|||
private readonly ConcurrentDictionary<string, bool> _processingConnections; |
|||
private readonly SemaphoreSlim _processingSemaphore; |
|||
private readonly int _maxConcurrentProcessing; |
|||
|
|||
public ConnectionManagerCoordinator( |
|||
IWebSocketConnectionManager connectionManager, |
|||
IWebSocketMessageQueueManager messageQueueManager, |
|||
ILogger<ConnectionManagerCoordinator> logger, |
|||
int maxConcurrentProcessing = 100) |
|||
{ |
|||
_connectionManager = connectionManager; |
|||
_messageQueueManager = messageQueueManager; |
|||
_logger = logger; |
|||
_maxConcurrentProcessing = maxConcurrentProcessing; |
|||
_processingConnections = new ConcurrentDictionary<string, bool>(); |
|||
_processingSemaphore = new SemaphoreSlim(maxConcurrentProcessing); |
|||
_logger.LogInformation("初始化连接管理协调器,最大并发处理数:{MaxConcurrentProcessing}", maxConcurrentProcessing); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取连接管理器
|
|||
/// </summary>
|
|||
public IWebSocketConnectionManager GetConnectionManager() => _connectionManager; |
|||
|
|||
/// <summary>
|
|||
/// 获取消息队列管理器
|
|||
/// </summary>
|
|||
public IWebSocketMessageQueueManager GetMessageQueueManager() => _messageQueueManager; |
|||
|
|||
/// <summary>
|
|||
/// 开始连接处理
|
|||
/// </summary>
|
|||
public async Task<bool> BeginConnectionProcessingAsync(string connectionId) |
|||
{ |
|||
if (string.IsNullOrEmpty(connectionId)) |
|||
{ |
|||
throw new ArgumentException("连接ID不能为空", nameof(connectionId)); |
|||
} |
|||
|
|||
if (_processingConnections.TryGetValue(connectionId, out var isProcessing) && isProcessing) |
|||
{ |
|||
_logger.LogWarning("连接正在处理中,连接ID:{ConnectionId}", connectionId); |
|||
return false; |
|||
} |
|||
|
|||
await _processingSemaphore.WaitAsync(); |
|||
_processingConnections[connectionId] = true; |
|||
_logger.LogDebug("开始处理连接,连接ID:{ConnectionId}", connectionId); |
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 结束连接处理
|
|||
/// </summary>
|
|||
public async Task EndConnectionProcessingAsync(string connectionId) |
|||
{ |
|||
if (string.IsNullOrEmpty(connectionId)) |
|||
{ |
|||
throw new ArgumentException("连接ID不能为空", nameof(connectionId)); |
|||
} |
|||
|
|||
if (_processingConnections.TryRemove(connectionId, out _)) |
|||
{ |
|||
_processingSemaphore.Release(); |
|||
_logger.LogDebug("结束处理连接,连接ID:{ConnectionId}", connectionId); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查连接是否正在处理
|
|||
/// </summary>
|
|||
public bool IsConnectionProcessing(string connectionId) |
|||
{ |
|||
return _processingConnections.TryGetValue(connectionId, out var isProcessing) && isProcessing; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取当前处理中的连接数
|
|||
/// </summary>
|
|||
public int GetProcessingConnectionCount() |
|||
{ |
|||
return _processingConnections.Count; |
|||
} |
|||
} |
@ -1,11 +1,27 @@ |
|||
namespace CellularManagement.WebSocket.Connection; |
|||
|
|||
/// <summary>
|
|||
/// 连接状态枚举
|
|||
/// WebSocket 连接状态
|
|||
/// </summary>
|
|||
public enum ConnectionStatus |
|||
{ |
|||
Connected, // 已连接
|
|||
Disconnected, // 已断开
|
|||
Error // 错误状态
|
|||
/// <summary>
|
|||
/// 已连接
|
|||
/// </summary>
|
|||
Connected, |
|||
|
|||
/// <summary>
|
|||
/// 已断开
|
|||
/// </summary>
|
|||
Disconnected, |
|||
|
|||
/// <summary>
|
|||
/// 错误状态
|
|||
/// </summary>
|
|||
Error, |
|||
|
|||
/// <summary>
|
|||
/// 正在重连
|
|||
/// </summary>
|
|||
Reconnecting |
|||
} |
@ -0,0 +1,36 @@ |
|||
using System.Net.WebSockets; |
|||
using CellularManagement.WebSocket.Models; |
|||
|
|||
namespace CellularManagement.WebSocket.Connection; |
|||
|
|||
/// <summary>
|
|||
/// WebSocket 连接管理器接口
|
|||
/// 定义连接管理的基本操作
|
|||
/// </summary>
|
|||
public interface IWebSocketConnectionManager : IDisposable |
|||
{ |
|||
/// <summary>
|
|||
/// 添加新连接
|
|||
/// </summary>
|
|||
Task<string> AddConnectionAsync(System.Net.WebSockets.WebSocket socket); |
|||
|
|||
/// <summary>
|
|||
/// 移除连接
|
|||
/// </summary>
|
|||
Task<bool> RemoveConnectionAsync(string connectionId); |
|||
|
|||
/// <summary>
|
|||
/// 获取连接
|
|||
/// </summary>
|
|||
WebSocketConnection? GetConnection(string connectionId); |
|||
|
|||
/// <summary>
|
|||
/// 更新连接活动时间
|
|||
/// </summary>
|
|||
void UpdateConnectionActivity(string connectionId); |
|||
|
|||
/// <summary>
|
|||
/// 获取所有连接
|
|||
/// </summary>
|
|||
IEnumerable<WebSocketConnection> GetAllConnections(); |
|||
} |
@ -0,0 +1,35 @@ |
|||
using CellularManagement.WebSocket.Models; |
|||
|
|||
namespace CellularManagement.WebSocket.Connection; |
|||
|
|||
/// <summary>
|
|||
/// WebSocket 消息队列管理器接口
|
|||
/// 定义消息队列管理的基本操作
|
|||
/// </summary>
|
|||
public interface IWebSocketMessageQueueManager |
|||
{ |
|||
/// <summary>
|
|||
/// 消息到达事件
|
|||
/// </summary>
|
|||
event Func<WebSocketMessage, Task>? OnMessageReceived; |
|||
|
|||
/// <summary>
|
|||
/// 入队入站消息
|
|||
/// </summary>
|
|||
ValueTask QueueIncomingMessage(WebSocketMessage message); |
|||
|
|||
/// <summary>
|
|||
/// 入队出站消息
|
|||
/// </summary>
|
|||
ValueTask QueueOutgoingMessage(WebSocketMessage message); |
|||
|
|||
/// <summary>
|
|||
/// 读取入站消息
|
|||
/// </summary>
|
|||
IAsyncEnumerable<WebSocketMessage> ReadIncomingMessagesAsync(CancellationToken cancellationToken); |
|||
|
|||
/// <summary>
|
|||
/// 读取出站消息
|
|||
/// </summary>
|
|||
IAsyncEnumerable<WebSocketMessage> ReadOutgoingMessagesAsync(CancellationToken cancellationToken); |
|||
} |
@ -0,0 +1,137 @@ |
|||
using System.Threading.Channels; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Options; |
|||
using CellularManagement.WebSocket.Models; |
|||
|
|||
namespace CellularManagement.WebSocket.Connection; |
|||
|
|||
/// <summary>
|
|||
/// WebSocket 消息队列管理器
|
|||
/// 负责管理消息的入队和出队,是消息处理系统的核心组件
|
|||
/// 主要功能:
|
|||
/// 1. 管理入站消息队列(接收客户端消息)
|
|||
/// 2. 管理出站消息队列(发送消息到客户端)
|
|||
/// 3. 提供消息到达事件通知机制
|
|||
/// 4. 支持异步消息读写操作
|
|||
/// </summary>
|
|||
public class WebSocketMessageQueueManager : IWebSocketMessageQueueManager |
|||
{ |
|||
/// <summary>
|
|||
/// 入站消息通道,用于存储从客户端接收到的消息
|
|||
/// 使用有界通道,防止内存溢出
|
|||
/// </summary>
|
|||
private readonly Channel<WebSocketMessage> _incomingMessages; |
|||
|
|||
/// <summary>
|
|||
/// 出站消息通道,用于存储待发送到客户端的消息
|
|||
/// 使用有界通道,防止内存溢出
|
|||
/// </summary>
|
|||
private readonly Channel<WebSocketMessage> _outgoingMessages; |
|||
|
|||
/// <summary>
|
|||
/// 日志记录器
|
|||
/// </summary>
|
|||
private readonly ILogger<WebSocketMessageQueueManager> _logger; |
|||
|
|||
/// <summary>
|
|||
/// WebSocket 配置选项
|
|||
/// </summary>
|
|||
private readonly WebSocketOptions _options; |
|||
|
|||
/// <summary>
|
|||
/// 消息到达事件
|
|||
/// 当新消息入队时触发,用于通知消息处理器处理新消息
|
|||
/// </summary>
|
|||
public event Func<WebSocketMessage, Task>? OnMessageReceived; |
|||
|
|||
/// <summary>
|
|||
/// 构造函数
|
|||
/// </summary>
|
|||
/// <param name="logger">日志记录器</param>
|
|||
/// <param name="options">WebSocket 配置选项</param>
|
|||
public WebSocketMessageQueueManager( |
|||
ILogger<WebSocketMessageQueueManager> logger, |
|||
IOptions<WebSocketOptions> options) |
|||
{ |
|||
_logger = logger; |
|||
_options = options.Value; |
|||
|
|||
// 创建入站消息通道
|
|||
_incomingMessages = Channel.CreateBounded<WebSocketMessage>(new BoundedChannelOptions(_options.MessageQueueSize) |
|||
{ |
|||
FullMode = BoundedChannelFullMode.Wait, // 队列满时等待
|
|||
SingleReader = false, // 允许多个读取器
|
|||
SingleWriter = false // 允许多个写入器
|
|||
}); |
|||
|
|||
// 创建出站消息通道
|
|||
_outgoingMessages = Channel.CreateBounded<WebSocketMessage>(new BoundedChannelOptions(_options.MessageQueueSize) |
|||
{ |
|||
FullMode = BoundedChannelFullMode.Wait, // 队列满时等待
|
|||
SingleReader = false, // 允许多个读取器
|
|||
SingleWriter = false // 允许多个写入器
|
|||
}); |
|||
|
|||
_logger.LogInformation("创建消息队列完成,入站队列大小:{IncomingSize},出站队列大小:{OutgoingSize}", |
|||
_options.MessageQueueSize, _options.MessageQueueSize); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 入队入站消息
|
|||
/// 将接收到的客户端消息放入入站消息队列,并触发消息到达事件
|
|||
/// </summary>
|
|||
/// <param name="message">WebSocket 消息</param>
|
|||
public async ValueTask QueueIncomingMessage(WebSocketMessage message) |
|||
{ |
|||
_logger.LogDebug("入队入站消息,连接ID:{ConnectionId},消息类型:{MessageType},数据大小:{DataSize}字节", |
|||
message.ConnectionId, message.MessageType, message.Data.Length); |
|||
|
|||
// 将消息写入入站消息通道
|
|||
await _incomingMessages.Writer.WriteAsync(message); |
|||
|
|||
// 触发消息到达事件
|
|||
if (OnMessageReceived != null) |
|||
{ |
|||
_logger.LogDebug("触发消息到达事件,连接ID:{ConnectionId}", message.ConnectionId); |
|||
await OnMessageReceived(message); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 入队出站消息
|
|||
/// 将待发送的消息放入出站消息队列
|
|||
/// </summary>
|
|||
/// <param name="message">WebSocket 消息</param>
|
|||
public async ValueTask QueueOutgoingMessage(WebSocketMessage message) |
|||
{ |
|||
_logger.LogDebug("入队出站消息,连接ID:{ConnectionId},消息类型:{MessageType},数据大小:{DataSize}字节", |
|||
message.ConnectionId, message.MessageType, message.Data.Length); |
|||
|
|||
// 将消息写入出站消息通道
|
|||
await _outgoingMessages.Writer.WriteAsync(message); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 读取入站消息
|
|||
/// 从入站消息队列中异步读取消息
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">取消令牌</param>
|
|||
/// <returns>入站消息的异步枚举器</returns>
|
|||
public IAsyncEnumerable<WebSocketMessage> ReadIncomingMessagesAsync(CancellationToken cancellationToken) |
|||
{ |
|||
_logger.LogDebug("开始读取入站消息"); |
|||
return _incomingMessages.Reader.ReadAllAsync(cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 读取出站消息
|
|||
/// 从出站消息队列中异步读取消息
|
|||
/// </summary>
|
|||
/// <param name="cancellationToken">取消令牌</param>
|
|||
/// <returns>出站消息的异步枚举器</returns>
|
|||
public IAsyncEnumerable<WebSocketMessage> ReadOutgoingMessagesAsync(CancellationToken cancellationToken) |
|||
{ |
|||
_logger.LogDebug("开始读取出站消息"); |
|||
return _outgoingMessages.Reader.ReadAllAsync(cancellationToken); |
|||
} |
|||
} |
@ -0,0 +1,146 @@ |
|||
using CellularManagement.WebSocket.Connection; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Options; |
|||
using CellularManagement.WebSocket.Models; |
|||
|
|||
namespace CellularManagement.WebSocket.Services; |
|||
|
|||
/// <summary>
|
|||
/// 连接健康检查服务
|
|||
/// 负责定期检查连接状态,清理不活跃的连接
|
|||
/// </summary>
|
|||
public class ConnectionHealthCheckService : BackgroundService |
|||
{ |
|||
private readonly ConnectionManagerCoordinator _coordinator; |
|||
private readonly ILogger<ConnectionHealthCheckService> _logger; |
|||
private readonly WebSocketOptions _options; |
|||
private readonly TimeSpan _checkInterval; |
|||
private readonly TimeSpan _inactivityTimeout; |
|||
private bool _disposed; |
|||
|
|||
public ConnectionHealthCheckService( |
|||
ConnectionManagerCoordinator coordinator, |
|||
ILogger<ConnectionHealthCheckService> logger, |
|||
IOptions<WebSocketOptions> options) |
|||
{ |
|||
_coordinator = coordinator; |
|||
_logger = logger; |
|||
_options = options.Value; |
|||
_checkInterval = _options.HeartbeatInterval; |
|||
_inactivityTimeout = _options.ConnectionTimeout; |
|||
_logger.LogInformation("初始化连接健康检查服务,检查间隔:{CheckInterval}秒,超时时间:{Timeout}秒", |
|||
_checkInterval.TotalSeconds, _inactivityTimeout.TotalSeconds); |
|||
} |
|||
|
|||
protected override async Task ExecuteAsync(CancellationToken stoppingToken) |
|||
{ |
|||
_logger.LogInformation("连接健康检查服务开始运行"); |
|||
|
|||
while (!stoppingToken.IsCancellationRequested) |
|||
{ |
|||
try |
|||
{ |
|||
//await CheckConnectionsAsync();
|
|||
await Task.Delay(_checkInterval, stoppingToken); |
|||
} |
|||
catch (OperationCanceledException) |
|||
{ |
|||
_logger.LogInformation("连接健康检查服务正在停止"); |
|||
break; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "连接健康检查服务发生错误"); |
|||
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); |
|||
} |
|||
} |
|||
|
|||
_logger.LogInformation("连接健康检查服务已停止"); |
|||
} |
|||
|
|||
private async Task CheckConnectionsAsync() |
|||
{ |
|||
var connectionManager = _coordinator.GetConnectionManager(); |
|||
var connections = connectionManager.GetAllConnections(); |
|||
var now = DateTime.UtcNow; |
|||
var inactiveConnections = new List<string>(); |
|||
|
|||
foreach (var connection in connections) |
|||
{ |
|||
try |
|||
{ |
|||
// 检查连接是否超时
|
|||
if (now - connection.LastActivityTime > _inactivityTimeout) |
|||
{ |
|||
_logger.LogWarning("连接超时,连接ID:{ConnectionId},最后活动时间:{LastActivityTime}", |
|||
connection.Id, connection.LastActivityTime); |
|||
inactiveConnections.Add(connection.Id); |
|||
continue; |
|||
} |
|||
|
|||
// 检查连接是否正在处理
|
|||
if (_coordinator.IsConnectionProcessing(connection.Id)) |
|||
{ |
|||
_logger.LogDebug("连接正在处理中,跳过检查,连接ID:{ConnectionId}", connection.Id); |
|||
continue; |
|||
} |
|||
|
|||
// 检查连接状态
|
|||
if (connection.Socket.State != System.Net.WebSockets.WebSocketState.Open) |
|||
{ |
|||
_logger.LogWarning("连接状态异常,连接ID:{ConnectionId},状态:{State}", |
|||
connection.Id, connection.Socket.State); |
|||
inactiveConnections.Add(connection.Id); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "检查连接时发生错误,连接ID:{ConnectionId}", connection.Id); |
|||
inactiveConnections.Add(connection.Id); |
|||
} |
|||
} |
|||
|
|||
// 清理不活跃的连接
|
|||
foreach (var connectionId in inactiveConnections) |
|||
{ |
|||
try |
|||
{ |
|||
await connectionManager.RemoveConnectionAsync(connectionId); |
|||
_logger.LogInformation("已清理不活跃连接,连接ID:{ConnectionId}", connectionId); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "清理连接时发生错误,连接ID:{ConnectionId}", connectionId); |
|||
} |
|||
} |
|||
|
|||
_logger.LogInformation("连接健康检查完成,检查连接数:{TotalConnections},清理连接数:{CleanedConnections}", |
|||
connections.Count(), inactiveConnections.Count); |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
if (_disposed) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_disposed = true; |
|||
_logger.LogInformation("正在释放连接健康检查服务资源"); |
|||
|
|||
try |
|||
{ |
|||
// 执行清理操作
|
|||
_logger.LogInformation("连接健康检查服务资源已释放"); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "释放连接健康检查服务资源时发生错误"); |
|||
} |
|||
finally |
|||
{ |
|||
base.Dispose(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,33 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using CellularManagement.WebSocket.Handlers; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace CellularManagement.WebSocket.Services; |
|||
|
|||
/// <summary>
|
|||
/// 负责注册 WebSocket 消息处理器到 HandlerManager
|
|||
/// </summary>
|
|||
public class HandlerRegistrar |
|||
{ |
|||
private readonly HandlerManager _handlerManager; |
|||
private readonly ILoggerFactory _loggerFactory; |
|||
private readonly ILogger<HandlerRegistrar> _logger; |
|||
|
|||
public HandlerRegistrar(HandlerManager handlerManager, ILoggerFactory loggerFactory) |
|||
{ |
|||
_handlerManager = handlerManager; |
|||
_loggerFactory = loggerFactory; |
|||
_logger = loggerFactory.CreateLogger<HandlerRegistrar>(); |
|||
} |
|||
|
|||
public async Task RegisterHandlersAsync(IEnumerable<IWebSocketMessageHandler> messageHandlers) |
|||
{ |
|||
foreach (var handler in messageHandlers) |
|||
{ |
|||
_logger.LogInformation("注册消息处理器,消息类型:{MessageType}", handler.MessageType); |
|||
var adapter = new WebSocketMessageHandlerAdapter(handler, _loggerFactory.CreateLogger<WebSocketMessageHandlerAdapter>()); |
|||
await _handlerManager.RegisterHandlerAsync(handler.MessageType, adapter); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,307 @@ |
|||
using System.Text.Json; |
|||
using CellularManagement.WebSocket.Connection; |
|||
using CellularManagement.WebSocket.Models; |
|||
using CellularManagement.WebSocket.Pipeline; |
|||
using CellularManagement.WebSocket.Pipeline.Steps; |
|||
using Microsoft.Extensions.Logging; |
|||
using System.Threading.Channels; |
|||
using CellularManagement.WebSocket.Handlers; |
|||
|
|||
namespace CellularManagement.WebSocket.Services; |
|||
|
|||
/// <summary>
|
|||
/// 入站消息处理器
|
|||
/// 负责处理从客户端接收到的消息,是消息处理系统的核心组件之一
|
|||
///
|
|||
/// 处理流程:
|
|||
/// 1. 接收消息:通过消息队列管理器接收客户端消息
|
|||
/// 2. 消息验证:验证消息格式和内容
|
|||
/// 3. 消息路由:根据消息类型路由到对应的处理器
|
|||
/// 4. 消息处理:执行具体的业务逻辑
|
|||
/// 5. 响应处理:将处理结果发送回客户端
|
|||
///
|
|||
/// 特点:
|
|||
/// - 支持并发处理:通过信号量控制并发数量
|
|||
/// - 消息顺序保证:使用单一读取器确保消息顺序处理
|
|||
/// - 错误处理:完整的错误处理和恢复机制
|
|||
/// - 资源管理:自动释放资源,防止内存泄漏
|
|||
/// </summary>
|
|||
public class IncomingMessageProcessor : IDisposable |
|||
{ |
|||
/// <summary>
|
|||
/// 连接管理协调器
|
|||
/// 用于协调连接状态和消息处理,确保连接状态的一致性
|
|||
/// </summary>
|
|||
private readonly ConnectionManagerCoordinator _coordinator; |
|||
|
|||
/// <summary>
|
|||
/// 消息队列管理器
|
|||
/// 用于接收客户端消息和发送响应消息
|
|||
/// </summary>
|
|||
private readonly IWebSocketMessageQueueManager _messageQueueManager; |
|||
|
|||
/// <summary>
|
|||
/// 日志记录器
|
|||
/// 用于记录处理过程中的关键信息和错误
|
|||
/// </summary>
|
|||
private readonly ILogger<IncomingMessageProcessor> _logger; |
|||
|
|||
/// <summary>
|
|||
/// 消息处理管道
|
|||
/// 包含消息验证和路由等处理步骤,按顺序执行
|
|||
/// </summary>
|
|||
private readonly IPipelineStep<WebSocketMessage, WebSocketMessage> _pipeline; |
|||
|
|||
/// <summary>
|
|||
/// 处理并发控制信号量
|
|||
/// 限制同时处理的消息数量,防止系统过载
|
|||
/// </summary>
|
|||
private readonly SemaphoreSlim _processingSemaphore; |
|||
|
|||
/// <summary>
|
|||
/// 最大并发处理数
|
|||
/// 同时处理的最大消息数量,超过此数量将等待
|
|||
/// </summary>
|
|||
private readonly int _maxConcurrentProcesses; |
|||
|
|||
/// <summary>
|
|||
/// 消息通道
|
|||
/// 用于存储待处理的消息,确保消息按顺序处理
|
|||
/// 使用单一读取器模式,保证消息处理的顺序性
|
|||
/// </summary>
|
|||
private readonly Channel<WebSocketMessage> _messageChannel; |
|||
|
|||
/// <summary>
|
|||
/// 停止令牌源
|
|||
/// 用于控制处理器的优雅停止
|
|||
/// </summary>
|
|||
private readonly CancellationTokenSource _stoppingCts; |
|||
|
|||
/// <summary>
|
|||
/// 消息处理器管理器
|
|||
/// 管理和分发消息到对应的业务处理器
|
|||
/// </summary>
|
|||
private readonly HandlerManager _handlerManager; |
|||
|
|||
/// <summary>
|
|||
/// 日志工厂
|
|||
/// 用于创建其他组件的日志记录器
|
|||
/// </summary>
|
|||
private readonly ILoggerFactory _loggerFactory; |
|||
|
|||
/// <summary>
|
|||
/// 资源释放标志
|
|||
/// 防止重复释放资源
|
|||
/// </summary>
|
|||
private bool _disposed; |
|||
|
|||
/// <summary>
|
|||
/// 构造函数
|
|||
/// 初始化入站消息处理器及其依赖组件
|
|||
/// </summary>
|
|||
/// <param name="coordinator">连接管理协调器,用于协调连接状态</param>
|
|||
/// <param name="messageQueueManager">消息队列管理器,用于消息收发</param>
|
|||
/// <param name="logger">日志记录器,用于记录处理过程</param>
|
|||
/// <param name="loggerFactory">日志工厂,用于创建其他组件的日志记录器</param>
|
|||
/// <param name="handlerManager">消息处理器管理器,用于管理和分发消息</param>
|
|||
/// <param name="maxConcurrentProcesses">最大并发处理数,默认10</param>
|
|||
public IncomingMessageProcessor( |
|||
ConnectionManagerCoordinator coordinator, |
|||
IWebSocketMessageQueueManager messageQueueManager, |
|||
ILogger<IncomingMessageProcessor> logger, |
|||
ILoggerFactory loggerFactory, |
|||
HandlerManager handlerManager, |
|||
int maxConcurrentProcesses = 10) |
|||
{ |
|||
_coordinator = coordinator; |
|||
_messageQueueManager = messageQueueManager; |
|||
_logger = logger; |
|||
_loggerFactory = loggerFactory; |
|||
_maxConcurrentProcesses = maxConcurrentProcesses; |
|||
_processingSemaphore = new SemaphoreSlim(_maxConcurrentProcesses); |
|||
_messageChannel = Channel.CreateUnbounded<WebSocketMessage>(new UnboundedChannelOptions |
|||
{ |
|||
SingleReader = true, // 单一读取器,确保消息顺序处理
|
|||
SingleWriter = false // 允许多个写入器,支持并发写入
|
|||
}); |
|||
_stoppingCts = new CancellationTokenSource(); |
|||
_handlerManager = handlerManager; |
|||
|
|||
// 构建消息处理管道
|
|||
var pipelineBuilder = new PipelineBuilder<WebSocketMessage, WebSocketMessage>(loggerFactory); |
|||
_pipeline = pipelineBuilder |
|||
.AddStep(new MessageValidationStep(loggerFactory.CreateLogger<MessageValidationStep>())) // 添加消息验证步骤
|
|||
.AddStep(new MessageRoutingStep(loggerFactory.CreateLogger<MessageRoutingStep>(), _handlerManager)) // 添加消息路由步骤
|
|||
.Build(); |
|||
|
|||
// 订阅消息到达事件
|
|||
_messageQueueManager.OnMessageReceived += OnMessageReceived; |
|||
_logger.LogInformation("初始化入站消息处理器,最大并发处理数:{MaxConcurrentProcesses}", _maxConcurrentProcesses); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 消息到达事件处理
|
|||
/// 当新消息到达时,将其写入消息通道等待处理
|
|||
/// </summary>
|
|||
/// <param name="message">接收到的 WebSocket 消息</param>
|
|||
/// <returns>处理任务</returns>
|
|||
private Task OnMessageReceived(WebSocketMessage message) |
|||
{ |
|||
try |
|||
{ |
|||
if (!_messageChannel.Writer.TryWrite(message)) |
|||
{ |
|||
_logger.LogWarning("消息通道已满,无法处理新消息,连接ID:{ConnectionId}", message.ConnectionId); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "处理消息时发生错误,连接ID:{ConnectionId}", message.ConnectionId); |
|||
} |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 处理入站消息
|
|||
/// 从消息通道中读取消息并进行处理
|
|||
/// </summary>
|
|||
/// <param name="stoppingToken">停止令牌,用于控制处理器的停止</param>
|
|||
/// <returns>处理任务</returns>
|
|||
public async Task ProcessIncomingMessagesAsync(CancellationToken stoppingToken) |
|||
{ |
|||
_logger.LogInformation("入站消息处理服务开始运行"); |
|||
|
|||
try |
|||
{ |
|||
await foreach (var message in _messageChannel.Reader.ReadAllAsync(stoppingToken)) |
|||
{ |
|||
await ProcessMessageAsync(message); |
|||
} |
|||
} |
|||
catch (OperationCanceledException) |
|||
{ |
|||
_logger.LogInformation("入站消息处理服务正在停止"); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "入站消息处理服务发生错误"); |
|||
} |
|||
finally |
|||
{ |
|||
_logger.LogInformation("入站消息处理服务已停止"); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 处理单个消息
|
|||
/// 包括消息验证、路由和处理
|
|||
/// </summary>
|
|||
/// <param name="message">待处理的 WebSocket 消息</param>
|
|||
/// <returns>处理任务</returns>
|
|||
private async Task ProcessMessageAsync(WebSocketMessage message) |
|||
{ |
|||
_logger.LogDebug("开始处理消息,连接ID:{ConnectionId},消息类型:{MessageType},数据大小:{DataSize}字节", |
|||
message.ConnectionId, message.MessageType, message.Data.Length); |
|||
|
|||
// 标记连接正在处理
|
|||
if (!await _coordinator.BeginConnectionProcessingAsync(message.ConnectionId)) |
|||
{ |
|||
_logger.LogWarning("连接正在处理中,跳过消息处理,连接ID:{ConnectionId}", message.ConnectionId); |
|||
return; |
|||
} |
|||
|
|||
await _processingSemaphore.WaitAsync(); |
|||
try |
|||
{ |
|||
// 通过管道处理消息
|
|||
var processedMessage = await _pipeline.ProcessWithErrorHandlingAsync(message, _stoppingCts.Token); |
|||
_logger.LogDebug("消息处理完成,连接ID:{ConnectionId},处理结果:{Processed}", |
|||
message.ConnectionId, processedMessage != null); |
|||
|
|||
if (processedMessage != null) |
|||
{ |
|||
// 将处理后的消息入队
|
|||
await _messageQueueManager.QueueOutgoingMessage(processedMessage); |
|||
_logger.LogDebug("处理后的消息已入队,连接ID:{ConnectionId}", message.ConnectionId); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "处理消息时发生错误,连接ID:{ConnectionId}", message.ConnectionId); |
|||
await HandleProcessingError(message, ex); |
|||
} |
|||
finally |
|||
{ |
|||
_processingSemaphore.Release(); |
|||
await _coordinator.EndConnectionProcessingAsync(message.ConnectionId); |
|||
_logger.LogDebug("消息处理完成,释放信号量,连接ID:{ConnectionId}", message.ConnectionId); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 处理错误
|
|||
/// 当消息处理发生错误时,生成错误响应并发送给客户端
|
|||
/// </summary>
|
|||
/// <param name="message">原始消息</param>
|
|||
/// <param name="exception">异常信息</param>
|
|||
/// <returns>处理任务</returns>
|
|||
private async Task HandleProcessingError(WebSocketMessage message, Exception exception) |
|||
{ |
|||
_logger.LogError(exception, "处理消息错误,连接ID:{ConnectionId},错误信息:{ErrorMessage}", |
|||
message.ConnectionId, exception.Message); |
|||
|
|||
try |
|||
{ |
|||
// 创建错误响应消息
|
|||
var errorResponse = new WebSocketMessage |
|||
{ |
|||
ConnectionId = message.ConnectionId, |
|||
Data = System.Text.Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new |
|||
{ |
|||
type = "error", |
|||
error = exception.Message |
|||
})), |
|||
MessageType = System.Net.WebSockets.WebSocketMessageType.Text |
|||
}; |
|||
|
|||
// 将错误响应入队
|
|||
await _messageQueueManager.QueueOutgoingMessage(errorResponse); |
|||
_logger.LogDebug("错误响应已发送,连接ID:{ConnectionId}", message.ConnectionId); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "发送错误响应时发生错误,连接ID:{ConnectionId}", message.ConnectionId); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 释放资源
|
|||
/// 释放所有占用的资源,包括信号量、取消令牌等
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
if (_disposed) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_disposed = true; |
|||
_logger.LogInformation("正在释放入站消息处理器资源"); |
|||
|
|||
try |
|||
{ |
|||
// 取消订阅事件
|
|||
_messageQueueManager.OnMessageReceived -= OnMessageReceived; |
|||
// 停止所有正在进行的操作
|
|||
_stoppingCts.Cancel(); |
|||
// 释放资源
|
|||
_processingSemaphore.Dispose(); |
|||
_stoppingCts.Dispose(); |
|||
_logger.LogInformation("入站消息处理器资源已释放"); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
_logger.LogError(ex, "释放入站消息处理器资源时发生错误"); |
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue