使用BotFramework向MS Teams千余用户发消息超时失败求助
问题背景
我正在开发一款MS Teams机器人,使用Microsoft.Bot.Builder(4.15.2)、**Microsoft.Bot.Connector(4.15.2)**异步向1000名用户发送消息,C#核心代码如下:
public async Task SendMessage(string tenantId, ConnectorClient connectorClient, UserDetails user, Activity personalMessageActivity) { ChannelAccount member = GetChannelAccount(user.id, user.name); ConversationParameters conversationParameters = GetConversationParameters(tenantId, member); string conversationId = user.conversationId; var resp = await connectorClient.Conversations.SendToConversationAsync(user.conversationId, personalMessageActivity); } public async Task SendMessageToUsers(ProactiveMessage proactiveMessage, string tenantId, ConnectorClient connectorClient, UserDetails member, Activity personalMessageActivity) { var result = await SendMessage(tenantId, connectorClient, member, personalMessageActivity); }
通过以下代码批量执行:
List<Task> TaskList = new List<Task>(); foreach (UserDetails member in allMembersArr) { TaskList.Add(SendMessageToUsers (proactiveMessage, tenantId, connectorClient, member, personalMessageActivity)); } await Task.WhenAll(TaskList);
执行后仅约600名用户成功收到消息,其余用户出现错误:连接尝试失败,因为连接方未在一段时间后正确响应,或已建立的连接失败,因为连接主机未能响应。尝试多线程与异步编程仍未解决。
后续改用Node.js(botbuilder@4.15.0、botframework-connector@4.15.0),核心代码如下:
const sendMessage = (membersArry, activity) => { membersArry.forEach((member) => { connectorClient.conversations.sendToConversation( member.conversationId, activity ); }); } // 依赖版本 "botbuilder": "^4.15.0", "botframework-connector": "^4.15.0",
问题依然存在:每次运行都会随机出现用户超时错误Error: connect ETIMEDOUT 52.xx.xxx.x:443,无法向全部1000名用户发送消息。
注:使用的Azure Bot定价层为Standard S1。
解决方案建议
1. 控制并发请求数
直接发起1000个并发请求会触发服务端限流或超出本地TCP连接池限制,建议分批并发执行,控制每批次请求数量(推荐20-50个),批次间可添加短暂延迟。
C# 分批示例:
int batchSize = 30; for (int i = 0; i < allMembersArr.Count; i += batchSize) { var currentBatch = allMembersArr.Skip(i).Take(batchSize); var batchTasks = currentBatch.Select(member => SendMessageToUsers(proactiveMessage, tenantId, connectorClient, member, personalMessageActivity) ); await Task.WhenAll(batchTasks); // 批次间延迟1秒,避免服务端压力过载 await Task.Delay(1000); }
Node.js 分批示例:
const sendMessageInBatches = async (membersArray, activity, batchSize = 30) => { for (let i = 0; i < membersArray.length; i += batchSize) { const batch = membersArray.slice(i, i + batchSize); // 等待当前批次所有请求完成 await Promise.all(batch.map(member => connectorClient.conversations.sendToConversation(member.conversationId, activity) )); // 批次间添加延迟 await new Promise(resolve => setTimeout(resolve, 1000)); } };
2. 添加重试机制
针对超时、临时网络波动等异常,添加指数退避重试策略,提升消息送达率。
C# 使用Polly库实现重试:
// 先安装Polly包:Install-Package Polly var retryPolicy = Policy .Handle<HttpRequestException>() .Or<TaskCanceledException>() // 捕获超时类异常 .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); // 在SendMessage方法中使用重试 await retryPolicy.ExecuteAsync(() => connectorClient.Conversations.SendToConversationAsync(user.conversationId, personalMessageActivity) );
Node.js 使用p-retry库实现重试:
// 先安装p-retry包:npm install p-retry const pRetry = require('p-retry'); const sendWithRetry = async (member, activity) => { await pRetry(() => connectorClient.conversations.sendToConversation(member.conversationId, activity), { retries: 3, factor: 2, // 指数退避因子 onFailedAttempt: (error) => { console.log(`发送给用户${member.id}失败,重试次数: ${error.attemptNumber}`); } } ); };
3. 优化ConnectorClient实例管理
避免复用单个ConnectorClient处理大量请求,可针对每个批次创建新的实例,或者调整客户端的连接池配置(比如增大最大连接数)。
C# 示例:
// 每个批次创建新的ConnectorClient实例 var credentials = new MicrosoftAppCredentials(appId, appPassword); var connectorClient = new ConnectorClient(new Uri(serviceUrl), credentials);
4. 检查Azure Bot服务限制
Standard S1层存在请求速率限制,可登录Azure门户查看Bot服务的监控指标(请求数、错误率、限流次数),确认是否触发了平台限流。若限流频繁,可考虑升级定价层或调整发送节奏。
内容的提问来源于stack exchange,提问作者Sandesh Sawant

