C#多Web流读取性能优化:Discord Bot加载过慢问题求助
优化Discord Bot中OSRS排行榜API调用的加载速度
你的命令加载慢的核心原因就是同步串行调用了4个OSRS排行榜接口——每个请求都要等前一个完全完成才会开始,网络延迟、服务器响应时间都会被累加,总耗时是4个请求的时间总和。下面是具体的优化方案和代码改进建议:
1. 核心优化:并行异步请求
用HttpClient的异步方法同时发起所有请求,通过Task.WhenAll等待全部完成,这样总耗时会接近最慢的那个请求的时间,而不是4个请求的时间相加。另外,HttpClient要复用,不要每次请求都创建新实例(频繁创建会耗尽TCP连接资源)。
2. 其他代码优化点
- 提取重复的API请求逻辑成复用方法,减少冗余代码,方便后续维护
- 不要吞掉所有异常,至少记录错误日志,方便排查请求失败的原因
- 用
long.TryParse代替Convert.ToInt64,避免格式错误导致的崩溃 - 对
player参数进行URL编码,防止特殊字符导致请求失败 - 修复账号判断逻辑中的bug:原代码里
Convert.ToInt64(UIMExp + HCExp)是把两个字符串拼接后转数字,逻辑错误,应该分别转数字后再相加
优化后的完整代码示例
// 建议把HttpClient作为Bot的全局实例,不要每次请求都创建 private static readonly HttpClient _httpClient = new HttpClient(); // 提取复用的API请求方法 private async Task<string> GetTotalExpAsync(string player, string hiscoreType) { var encodedPlayer = Uri.EscapeDataString(player); var url = $"https://secure.runescape.com/m=hiscore_oldschool{hiscoreType}/index_lite.ws?player={encodedPlayer}"; try { var response = await _httpClient.GetStringAsync(url); var lines = response.Split('\n'); if (lines.Length > 0) { var totalParts = lines[0].Split(','); if (totalParts.Length >= 3) { return totalParts[2]; } } } catch (Exception ex) { // 这里可以加日志记录,比如Console.WriteLine($"请求失败: {hiscoreType}, 错误: {ex.Message}"); } return "0"; } // 命令处理方法 public async Task HandleHiscoreCommand(string player) { // 并行发起所有请求 var normalTask = GetTotalExpAsync(player, ""); var ironTask = GetTotalExpAsync(player, "_ironman"); var hcTask = GetTotalExpAsync(player, "_hardcore_ironman"); var uimTask = GetTotalExpAsync(player, "_ultimate"); // 等待所有请求完成 await Task.WhenAll(normalTask, ironTask, hcTask, uimTask); // 获取结果 var normalExp = normalTask.Result; var ironExp = ironTask.Result; var hcExp = hcTask.Result; var uimExp = uimTask.Result; // 发送结果 await ReplyAsync( $"**Normal: ** {normalExp}\n" + $"**Ironman: ** {ironExp}\n" + $"**Hardcore: ** {hcExp}\n" + $"**UIM: ** {uimExp}"); // 修复后的账号判断逻辑 if (long.TryParse(uimExp, out var uimNum) && long.TryParse(normalExp, out var normalNum) && uimNum == normalNum) { await ReplyAsync("Account is a UIM"); } else if (long.TryParse(hcExp, out var hcNum) && hcNum == normalNum) { await ReplyAsync("Account is a HC"); } else if (long.TryParse(ironExp, out var ironNum) && ironNum == normalNum) { var uimVal = long.TryParse(uimExp, out var u) ? u : 0; var hcVal = long.TryParse(hcExp, out var h) ? h : 0; if (ironNum > uimVal + hcVal) { if (uimVal > 1) { await ReplyAsync("Account is a ~~UIM~~ Normal Ironman"); } else if (hcVal > 1) { await ReplyAsync("Account is a ~~HC~~ Normal Ironman"); } else { await ReplyAsync("Account is a Normal Ironman"); } } } else { var uimVal = long.TryParse(uimExp, out var u) ? u : 0; var ironVal = long.TryParse(ironExp, out var i) ? i : 0; var hcVal = long.TryParse(hcExp, out var h) ? h : 0; if (uimVal > 1 && ironVal > 1) { await ReplyAsync("Account is a ~~UIM~~, ~~Ironman~~, normal player."); } else if (hcVal > 1 && ironVal > 1) { await ReplyAsync("Account is a ~~HC~~, ~~Ironman~~, normal player."); } else if (ironVal > 1 && hcVal == 0 && uimVal == 0) { await ReplyAsync("Account is a ~~Ironman~~ normal player."); } else { await ReplyAsync("Account is a Normal Player"); } } }
额外建议
- 可以加入缓存机制:如果同一个玩家短时间内再次查询,直接返回缓存结果,避免重复请求API
- 检查OSRS的API是否有批量查询的接口,减少请求次数(不过目前OSRS的hiscore接口好像没有批量查询不同模式的功能)
内容的提问来源于stack exchange,提问作者LepEatWorld
相关产品推荐
相关产品推荐

