压测工具优化
This commit is contained in:
parent
94b3b21924
commit
e2d4231bd3
@ -21,14 +21,18 @@ namespace Logic.Editor
|
||||
{
|
||||
public class NetworkStressEditorWindow : EditorWindow
|
||||
{
|
||||
private const float TestSeconds = 60f;
|
||||
private const float MessagesPerSecond = 30f;
|
||||
private const float TotalSeconds = 60f;
|
||||
private const float SendSeconds = 50f;
|
||||
private const float ReportWaitSeconds = TotalSeconds - SendSeconds;
|
||||
private const float ClientReportRetrySeconds = 10f;
|
||||
private const float ClientReportRetryInterval = 1f;
|
||||
private const float MessagesPerSecond = 10f;
|
||||
private const int SmallPayloadBytes = 512;
|
||||
private const int LargePayloadBytes = 256 * 1024;
|
||||
private const int LargeEveryMessages = 60;
|
||||
private const int LargePayloadBytes = 128 * 1024;
|
||||
private const int LargeEveryMessages = 120;
|
||||
private const int JitterMaxMs = 80;
|
||||
private const float DropPercent = 2f;
|
||||
private const float UnreliablePercent = 5f;
|
||||
private const float DropPercent = 1f;
|
||||
private const float UnreliablePercent = 2f;
|
||||
private const int RandomSeed = 1214;
|
||||
private const string ScenarioName = "OneClick-60s";
|
||||
|
||||
@ -36,6 +40,7 @@ namespace Logic.Editor
|
||||
{
|
||||
Idle,
|
||||
Running,
|
||||
WaitingForReports,
|
||||
}
|
||||
|
||||
private class PeerStats
|
||||
@ -96,6 +101,9 @@ namespace Logic.Editor
|
||||
private string _lastExportPath = string.Empty;
|
||||
private string _eventLog = string.Empty;
|
||||
private double _startTime;
|
||||
private double _reportWaitUntil;
|
||||
private double _reportRetryUntil;
|
||||
private double _nextReportRetryTime;
|
||||
private double _lastUpdateTime;
|
||||
private double _nextRepaintTime;
|
||||
private double _sendAccumulator;
|
||||
@ -164,7 +172,7 @@ namespace Logic.Editor
|
||||
private void DrawStatus(ILobby lobby)
|
||||
{
|
||||
EditorGUILayout.LabelField("Steam P2P 一键压测", EditorStyles.boldLabel);
|
||||
EditorGUILayout.HelpBox("房主点一次开始。测试固定 60 秒,覆盖小包、周期性大包、ACK、轻量抖动和轻量丢包;到时自动停止并导出报告。", MessageType.Info);
|
||||
EditorGUILayout.HelpBox("房主点一次开始。总流程固定 60 秒:前 50 秒发包,后 10 秒等待从端报告;房主收齐报告会立刻自动导出。", MessageType.Info);
|
||||
|
||||
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
|
||||
{
|
||||
@ -194,11 +202,11 @@ namespace Logic.Editor
|
||||
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
|
||||
{
|
||||
GUI.enabled = lobby != null && lobby.IsInitialized() && lobby.IsInLobby();
|
||||
var buttonText = _state == RunState.Running ? "停止并导出报告" : "开始 60 秒一键压测";
|
||||
var buttonText = _state == RunState.Idle ? "开始 60 秒一键压测" : "停止并导出报告";
|
||||
if (GUILayout.Button(buttonText, GUILayout.Height(52)))
|
||||
{
|
||||
if (_state == RunState.Running)
|
||||
FinishAndExport(true, true);
|
||||
if (_state != RunState.Idle)
|
||||
FinishAndExportNow(true, true);
|
||||
else
|
||||
StartOneClickTest();
|
||||
}
|
||||
@ -216,8 +224,9 @@ namespace Logic.Editor
|
||||
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
|
||||
{
|
||||
EditorGUILayout.LabelField("实时结果", EditorStyles.boldLabel);
|
||||
var elapsed = _state == RunState.Running ? EditorApplication.timeSinceStartup - _startTime : 0d;
|
||||
EditorGUILayout.LabelField($"进度: {Math.Min(elapsed, TestSeconds):0.0}/{TestSeconds:0}s Pending ACK: {_pendingAcks.Count} 延迟队列: {_scheduledProbes.Count}");
|
||||
var elapsed = _state == RunState.Idle ? 0d : EditorApplication.timeSinceStartup - _startTime;
|
||||
var waitLeft = _state == RunState.WaitingForReports ? Math.Max(0d, _reportWaitUntil - EditorApplication.timeSinceStartup) : 0d;
|
||||
EditorGUILayout.LabelField($"进度: {Math.Min(elapsed, TotalSeconds):0.0}/{TotalSeconds:0}s 等待报告: {waitLeft:0.0}s Pending ACK: {_pendingAcks.Count} 延迟队列: {_scheduledProbes.Count}");
|
||||
|
||||
DrawStatsLine(GetSelfStats(), "本机");
|
||||
if (lobby != null && lobby.IsInLobby())
|
||||
@ -268,9 +277,18 @@ namespace Logic.Editor
|
||||
GenerateProbes(now, delta);
|
||||
DrainScheduledProbes(now);
|
||||
|
||||
if (now - _startTime >= TestSeconds)
|
||||
FinishAndExport(true, true);
|
||||
if (now - _startTime >= SendSeconds)
|
||||
FinishSendingAndWaitForReports(true);
|
||||
}
|
||||
else if (_state == RunState.WaitingForReports)
|
||||
{
|
||||
if (LobbyManager.Instance.Lobby?.IsLobbyOwner() == true && HasAllReports())
|
||||
ExportHostReportAndStop("房主已收齐报告,自动导出");
|
||||
else if (now >= _reportWaitUntil)
|
||||
ExportHostReportAndStop("等待从端报告超时,导出现有报告");
|
||||
}
|
||||
|
||||
RetryReportToHost(now);
|
||||
|
||||
if (now >= _nextRepaintTime)
|
||||
{
|
||||
@ -307,6 +325,9 @@ namespace Logic.Editor
|
||||
_state = RunState.Running;
|
||||
_statusLine = "运行中";
|
||||
_startTime = EditorApplication.timeSinceStartup;
|
||||
_reportWaitUntil = 0d;
|
||||
_reportRetryUntil = 0d;
|
||||
_nextReportRetryTime = 0d;
|
||||
_lastUpdateTime = _startTime;
|
||||
_sendAccumulator = 0d;
|
||||
_nextSequence = 0;
|
||||
@ -319,7 +340,36 @@ namespace Logic.Editor
|
||||
AppendEvent($"开始 60 秒压测: session={_sessionId}, run={_runId}");
|
||||
}
|
||||
|
||||
private void FinishAndExport(bool broadcastStop, bool sendReportToHost)
|
||||
private void FinishSendingAndWaitForReports(bool broadcastStop)
|
||||
{
|
||||
if (_state != RunState.Running) return;
|
||||
|
||||
foreach (var pending in _pendingAcks.Values.ToList())
|
||||
GetStats(pending.TargetMemberId).AckTimeouts++;
|
||||
GetSelfStats().AckTimeouts += _pendingAcks.Count;
|
||||
_scheduledProbes.Clear();
|
||||
_pendingAcks.Clear();
|
||||
_state = RunState.WaitingForReports;
|
||||
_statusLine = "等待从端报告";
|
||||
_reportWaitUntil = EditorApplication.timeSinceStartup + ReportWaitSeconds;
|
||||
_lastReport = BuildReport();
|
||||
_reports[$"{ScenarioName}/{_runId}/{GetSelfMemberId()}"] = _lastReport;
|
||||
|
||||
if (broadcastStop && LobbyManager.Instance.Lobby != null && LobbyManager.Instance.Lobby.IsLobbyOwner())
|
||||
BroadcastControl(NetworkStressMessageKind.ControlStop);
|
||||
|
||||
if (LobbyManager.Instance.Lobby == null || !LobbyManager.Instance.Lobby.IsLobbyOwner())
|
||||
StartReportRetryToHost();
|
||||
|
||||
if (LobbyManager.Instance.Lobby?.IsLobbyOwner() != true)
|
||||
ExportHostReportAndStop("从端压测结束,本地报告已导出并发送给房主");
|
||||
else if (HasAllReports())
|
||||
ExportHostReportAndStop("房主已收齐报告,自动导出");
|
||||
else
|
||||
AppendEvent("房主等待从端报告");
|
||||
}
|
||||
|
||||
private void FinishAndExportNow(bool broadcastStop, bool sendReportToHost)
|
||||
{
|
||||
if (_state == RunState.Running)
|
||||
{
|
||||
@ -328,8 +378,6 @@ namespace Logic.Editor
|
||||
GetSelfStats().AckTimeouts += _pendingAcks.Count;
|
||||
}
|
||||
|
||||
_state = RunState.Idle;
|
||||
_statusLine = "已完成";
|
||||
_scheduledProbes.Clear();
|
||||
_pendingAcks.Clear();
|
||||
_lastReport = BuildReport();
|
||||
@ -339,10 +387,17 @@ namespace Logic.Editor
|
||||
BroadcastControl(NetworkStressMessageKind.ControlStop);
|
||||
|
||||
if (sendReportToHost)
|
||||
SendReportToHost();
|
||||
StartReportRetryToHost();
|
||||
|
||||
ExportHostReportAndStop("手动停止,报告已导出");
|
||||
}
|
||||
|
||||
private void ExportHostReportAndStop(string reason)
|
||||
{
|
||||
_state = RunState.Idle;
|
||||
_statusLine = "已完成";
|
||||
ExportReport();
|
||||
AppendEvent("压测结束,报告已自动导出");
|
||||
AppendEvent(reason);
|
||||
}
|
||||
|
||||
private void GenerateProbes(double now, double delta)
|
||||
@ -386,7 +441,7 @@ namespace Logic.Editor
|
||||
PayloadHash = ComputeHash(payload),
|
||||
Reliable = reliable,
|
||||
RequiresAck = true,
|
||||
DurationSeconds = TestSeconds,
|
||||
DurationSeconds = SendSeconds,
|
||||
MessagesPerSecond = MessagesPerSecond,
|
||||
SmallPayloadBytes = SmallPayloadBytes,
|
||||
LargePayloadBytes = LargePayloadBytes,
|
||||
@ -518,13 +573,14 @@ namespace Logic.Editor
|
||||
private void HandleControlStop(NetworkStressMessage message)
|
||||
{
|
||||
if (message.RunId != _runId) return;
|
||||
FinishAndExport(false, true);
|
||||
FinishAndExportNow(false, true);
|
||||
AppendEvent("收到房主停止");
|
||||
}
|
||||
|
||||
private void HandleProbe(NetworkStressMessage message)
|
||||
{
|
||||
if (message.RunId != _runId) return;
|
||||
if (_state != RunState.Running) return;
|
||||
|
||||
var stats = GetStats(message.SenderMemberId);
|
||||
stats.ReceivedProbes++;
|
||||
@ -582,6 +638,7 @@ namespace Logic.Editor
|
||||
private void HandleAck(NetworkStressMessage message)
|
||||
{
|
||||
if (message.RunId != _runId) return;
|
||||
if (_state != RunState.Running) return;
|
||||
var key = (message.SenderMemberId, message.AckSequence);
|
||||
if (!_pendingAcks.TryGetValue(key, out var sentProbe))
|
||||
{
|
||||
@ -600,9 +657,15 @@ namespace Logic.Editor
|
||||
|
||||
private void HandleReport(NetworkStressMessage message)
|
||||
{
|
||||
_reports[$"{message.RunId}/{message.SenderMemberId}"] = message.Text ?? string.Empty;
|
||||
if (_state == RunState.Idle && !string.IsNullOrEmpty(_lastExportPath))
|
||||
_reports[$"{ScenarioName}/{message.RunId}/{message.SenderMemberId}"] = message.Text ?? string.Empty;
|
||||
AppendEvent($"收到从端报告: {message.SenderMemberId}");
|
||||
if (LobbyManager.Instance.Lobby?.IsLobbyOwner() == true && _state == RunState.WaitingForReports && HasAllReports())
|
||||
ExportHostReportAndStop("房主已收齐报告,自动导出");
|
||||
else if (LobbyManager.Instance.Lobby?.IsLobbyOwner() == true && _state == RunState.Idle && !string.IsNullOrEmpty(_lastExportPath))
|
||||
{
|
||||
ExportReport();
|
||||
AppendEvent("收到迟到从端报告,已更新导出文件");
|
||||
}
|
||||
}
|
||||
|
||||
private void BroadcastControl(NetworkStressMessageKind kind)
|
||||
@ -614,7 +677,7 @@ namespace Logic.Editor
|
||||
RunId = _runId,
|
||||
ScenarioName = ScenarioName,
|
||||
SenderMemberId = GetSelfMemberId(),
|
||||
DurationSeconds = TestSeconds,
|
||||
DurationSeconds = SendSeconds,
|
||||
MessagesPerSecond = MessagesPerSecond,
|
||||
SmallPayloadBytes = SmallPayloadBytes,
|
||||
LargePayloadBytes = LargePayloadBytes,
|
||||
@ -629,12 +692,28 @@ namespace Logic.Editor
|
||||
AppendEvent($"{kind} 广播失败");
|
||||
}
|
||||
|
||||
private void SendReportToHost()
|
||||
private void StartReportRetryToHost()
|
||||
{
|
||||
if (LobbyManager.Instance.Lobby?.IsLobbyOwner() == true) return;
|
||||
_reportRetryUntil = EditorApplication.timeSinceStartup + ClientReportRetrySeconds;
|
||||
_nextReportRetryTime = 0d;
|
||||
RetryReportToHost(EditorApplication.timeSinceStartup);
|
||||
}
|
||||
|
||||
private void RetryReportToHost(double now)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_lastReport)) return;
|
||||
if (now > _reportRetryUntil || now < _nextReportRetryTime) return;
|
||||
_nextReportRetryTime = now + ClientReportRetryInterval;
|
||||
SendReportToHost();
|
||||
}
|
||||
|
||||
private bool SendReportToHost()
|
||||
{
|
||||
var lobby = LobbyManager.Instance.Lobby;
|
||||
if (lobby == null || !lobby.IsInLobby() || lobby.IsLobbyOwner()) return;
|
||||
if (lobby == null || !lobby.IsInLobby() || lobby.IsLobbyOwner()) return false;
|
||||
var host = lobby.GetLobbyOwnerId();
|
||||
if (host == 0) return;
|
||||
if (host == 0) return false;
|
||||
|
||||
var report = new NetworkStressMessage
|
||||
{
|
||||
@ -646,7 +725,7 @@ namespace Logic.Editor
|
||||
TargetMemberId = host,
|
||||
Text = _lastReport,
|
||||
};
|
||||
TrySendMessage(report, false, host, true, out _);
|
||||
return TrySendMessage(report, false, host, true, out _);
|
||||
}
|
||||
|
||||
private bool TrySendMessage(NetworkStressMessage message, bool broadcast, ulong target, bool reliable,
|
||||
@ -740,6 +819,20 @@ namespace Logic.Editor
|
||||
return memberId.ToString();
|
||||
}
|
||||
|
||||
private bool HasAllReports()
|
||||
{
|
||||
var lobby = LobbyManager.Instance.Lobby;
|
||||
if (lobby == null || !lobby.IsInLobby()) return false;
|
||||
|
||||
foreach (var memberId in lobby.GetAllMemberIds())
|
||||
{
|
||||
if (!_reports.ContainsKey($"{ScenarioName}/{_runId}/{memberId}"))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string BuildReport()
|
||||
{
|
||||
var sb = new StringBuilder(4096);
|
||||
@ -750,7 +843,8 @@ namespace Logic.Editor
|
||||
AppendJson(sb, "memberId", GetSelfMemberId().ToString(), true, false);
|
||||
AppendJson(sb, "memberName", GetMemberName(GetSelfMemberId()), true);
|
||||
AppendJson(sb, "state", _statusLine, true);
|
||||
AppendJson(sb, "durationSeconds", TestSeconds.ToString("0"), true, false);
|
||||
AppendJson(sb, "sendSeconds", SendSeconds.ToString("0"), true, false);
|
||||
AppendJson(sb, "totalSeconds", TotalSeconds.ToString("0"), true, false);
|
||||
AppendJson(sb, "messagesPerSecond", MessagesPerSecond.ToString("0"), true, false);
|
||||
AppendJson(sb, "smallPayloadBytes", SmallPayloadBytes.ToString(), true, false);
|
||||
AppendJson(sb, "largePayloadBytes", LargePayloadBytes.ToString(), true, false);
|
||||
|
||||
@ -23,8 +23,10 @@ namespace TH1_Logic.Steam
|
||||
private const int MaxLargeMessageBytes = 64 * 1024 * 1024;
|
||||
private const int MaxLargeWireMessageBytes = MaxLargeMessageBytes + OrderedMessageHeaderSize;
|
||||
private const float LargeMessageTimeout = 30f;
|
||||
private const float OutgoingMessageSendInterval = 0.05f;
|
||||
private const float OutgoingMessageSendInterval = 0.01f;
|
||||
private const float OutgoingMessageLimitRetryDelay = 0.25f;
|
||||
private const int MaxOutgoingMessagesPerUpdate = 8;
|
||||
private const int MaxOutgoingBytesPerUpdate = 512 * 1024;
|
||||
private const int OrderedMessageMagic = 0x31514f54; // TOQ1
|
||||
private const int OrderedMessageVersion = 1;
|
||||
private const int OrderedMessageHeaderSize = 20;
|
||||
@ -1168,9 +1170,12 @@ namespace TH1_Logic.Steam
|
||||
if (_outgoingPeerOrder.Count == 0) return;
|
||||
|
||||
var peerCount = _outgoingPeerOrder.Count;
|
||||
var sentCount = 0;
|
||||
var sentBytes = 0;
|
||||
for (int attempt = 0; attempt < peerCount; attempt++)
|
||||
{
|
||||
if (_outgoingPeerOrder.Count == 0) return;
|
||||
if (sentCount >= MaxOutgoingMessagesPerUpdate || sentBytes >= MaxOutgoingBytesPerUpdate) return;
|
||||
if (_outgoingPeerRoundRobinIndex >= _outgoingPeerOrder.Count) _outgoingPeerRoundRobinIndex = 0;
|
||||
var target = _outgoingPeerOrder[_outgoingPeerRoundRobinIndex];
|
||||
_outgoingPeerRoundRobinIndex = (_outgoingPeerRoundRobinIndex + 1) % _outgoingPeerOrder.Count;
|
||||
@ -1231,11 +1236,14 @@ namespace TH1_Logic.Steam
|
||||
var result = SendRawToResult(pending.Target, conn, pending.Data, pending.Reliable, pending.UseNoNagle, false);
|
||||
if (result == EResult.k_EResultOK)
|
||||
{
|
||||
var sentLength = pending.Data.Length;
|
||||
DequeueOutgoingMessage(pending.Target, queue);
|
||||
if (pending.IsLastLargeChunk)
|
||||
LogSystem.LogInfo($"Queued large P2P message sent to {pending.Target}, messageId: {pending.MessageId}, chunks: {pending.ChunkCount}");
|
||||
sentCount++;
|
||||
sentBytes += sentLength;
|
||||
queue.NextSendTime = Time.time + OutgoingMessageSendInterval;
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
pending.Attempts++;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user