You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Teams Bot通知:存储600+对话后FindMember API响应极慢

问题分析与解决方案

问题根源

当前使用findMember方法时,会遍历存储中所有对话记录,对每条记录都调用Microsoft Teams API获取成员信息后再匹配邮箱。当对话量达到600条时,串行的API请求堆积加上Blob存储无索引导致的全量遍历IO开销,最终让耗时从7秒飙升至15分钟。

优化方案

1. 为对话存储添加邮箱索引(最优解)

针对兼容Azure Blob的存储,修改对话记录的存储规则,将用户邮箱作为Blob的命名标识或元数据:

  • 存储阶段:保存对话记录时,以用户邮箱作为Blob文件名(例如user@domain.com.json),或给Blob添加email元字段存储邮箱地址。
  • 查找阶段:直接通过邮箱定位对应的Blob,无需遍历所有对话,也不用重复调用Teams API匹配邮箱。

示例修改存储与查找逻辑:

// 保存对话时按邮箱命名Blob
await blobStore.save(`conv_${member.account.email}.json`, conversationData);

// 自定义邮箱查找方法
async function findMemberByEmail(email: string) {
  const targetBlobName = `conv_${email}.json`;
  const conversationData = await blobStore.read(targetBlobName);
  
  if (conversationData) {
    // 构造符合原有调用逻辑的Member对象
    return {
      sendAdaptiveCard: async (card) => {
        await notificationApp.notification.sendAdaptiveCard(conversationData, card);
      }
    } as any;
  }
  return null;
}

2. 缓存成员信息,减少重复API调用

将已获取的“邮箱-对话关联”数据缓存(可用Redis或内存缓存),避免每次查找都调用Teams API:

import Redis from 'ioredis';
const redisClient = new Redis();

async function findMemberByEmail(email: string) {
  // 先查缓存
  const cachedConvId = await redisClient.get(`email_conv_map:${email}`);
  if (cachedConvId) {
    const conversationData = await blobStore.read(cachedConvId);
    return conversationData ? { /* 构造Member对象 */ } : null;
  }

  // 缓存未命中时遍历查找,找到后更新缓存
  const allConvIds = await blobStore.list();
  for (const convId of allConvIds) {
    const member = await notificationApp.notification.getMember(convId);
    if (member.account.email === email) {
      // 缓存1天
      await redisClient.set(`email_conv_map:${email}`, convId, 'EX', 86400);
      return member;
    }
  }
  return null;
}

3. 并行处理遍历,压缩串行耗时

如果暂时无法修改存储结构,将串行遍历改为有限并发的并行处理,同时控制并发数避免触发Teams API速率限制:

async function findMemberByEmail(email: string) {
  const allConvIds = await blobStore.list();
  const batchSize = 10; // 每次并行处理10条
  let targetMember = null;

  for (let i = 0; i < allConvIds.length; i += batchSize) {
    const batch = allConvIds.slice(i, i + batchSize);
    const promises = batch.map(async (convId) => {
      const member = await notificationApp.notification.getMember(convId);
      if (member.account.email === email) {
        targetMember = member;
      }
    });
    await Promise.all(promises);
    if (targetMember) break; // 找到目标后提前终止遍历
  }
  return targetMember;
}

4. 切换到带索引的存储服务

若Blob存储的索引能力不足,可将对话记录迁移到Azure Cosmos DB或SQL数据库,利用数据库的索引功能快速定位匹配邮箱的对话,彻底避免全量遍历。

总结

优先选择添加邮箱索引的方案,从根源解决遍历和重复API调用问题;若暂时无法修改存储结构,用并行处理或缓存作为临时优化,可大幅降低耗时。

内容的提问来源于stack exchange,提问作者user2872741

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 00:45:03