node.js - Sendgrid 连接上的 Azure 应用服务 SNAT 端口耗尽

标签 node.js azure sendgrid

我有一个nodejs代码触发sendgrid电子邮件给1000多个用户。当用户数量较少(100-200)时,效果很好。但是,当用户数量超过 1000 时,它会在某个时刻失败,并且不会触发其余邮件。当我检查应用程序服务时,它显示为 SNAT 端口耗尽。

userList.forEach(async(Element) => {
        console.log(Element.userId);
        let textContentUser="";
        let emailContentUser="";
        textContentUser=textContent;
        var userData=await db.collection('masterUserDetails').find({"userId":Element.userId}).toArray();
       
        if(userData.length>0)
        {
            
            textContentUser=textContentUser.split("{{FirstName}}").join(userData[0]["givenName"]);
            textContentUser=textContentUser.split("{{FullName}}").join(userData[0]["preferredName"]);
        var leadReviewerData=await db.collection('masterUserDetails').find({"userId":userData[0]["counselorEmail"]}).toArray();
            if(leadReviewerData.length==0)
            {
                textContentUser=textContentUser.split("{{LeadReviewerFullName}}").join(userData[0]["counselorName"]);
                textContentUser=textContentUser.split("{{LeadReviewerFirstName}}").join(userData[0]["counselorName"]);
            }
            else
            {
                textContentUser=textContentUser.split("{{LeadReviewerFullName}}").join(leadReviewerData[0]["preferredName"]);
                textContentUser=textContentUser.split("{{LeadReviewerFirstName}}").join(leadReviewerData[0]["givenName"]);
            }
        
        }
            console.log("final Text cintent: ",textContentUser);
          emailContentUser=emailContent;  
        emailContentUser=emailContentUser.replace("***content***",textContentUser);
        //console.log("final email cintent: ",emailContentUser);
    const msg = {
        to: Element.userId, // Change to your recipient
         bcc:"<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c9babcb9b9a6bbbd96bdacbabd89a8abaae7aaa6a4" rel="noreferrer noopener nofollow">[email protected]</a>",
        from: {
            "email": "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="24575154544b5650644546470a474b49" rel="noreferrer noopener nofollow">[email protected]</a>",
            "name": "MY APP"
        }, // Change to your verified sender
        subject: emailSubject,
        html: emailContentUser,
        attachments: [
            {
                filename:"image002.gif",
                content: img2,
                content_id:"image002",
                disposition:"inline"
            },
            {
                filename:"image004.gif",
                content: img4,
                content_id:"image004",
                disposition:"inline"
            }],
            reply_to: {
                "email": "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2b585e5b5b44595f055f4e585f6b4a494805484446" rel="noreferrer noopener nofollow">[email protected]</a>",
                "name": "My APP Support"
            },
            send_at: sendAt
    }
   
    console.log("sending mail")
    sgMail
        .send(msg)
        .then((response) => {
            console.log("Success--------"+response[0].statusCode)
            //console.log(response[0].headers)
        })
        .catch((error) => {
           // console.log("error");
           console.log("Error--------"+JSON.stringify(error))
        })            
        
    });

这在本地有效。当部署在 Azure 中且用户较少时也可以工作。

最佳答案

正如其他人所指出的,您将耗尽 SNAT 端口来向 SendGrid API 发出 HTTP 请求。

我建议避免为您要发送的每一封电子邮件发出 HTTP 请求,而是使用内置 API 通过一个 HTTP 请求发送多封电子邮件。

为此,请使用数组将 personalizations 属性添加到您的 msg 中。将对象添加到数组中以覆盖特定个性化的 msg 属性,例如 subjectfromto 等属性等如 documented here 。您最多可以向 personalizations 数组添加 1000 个项目。因此,如果您想要发送超过 1000 封电子邮件,则需要为每 1000 封电子邮件创建一个单独的 msg,但现在您只需为每 1000 封电子邮件发送一个 HTTP 请求,因此您应该'不会很快遇到 SNAT 端口错误。

现在,如果您使用单个电子邮件正文模板但想要个性化电子邮件,则可以使用 Substitution Tags在您的模板中,将 substitutions 属性添加到您的个性化对象,其中包含一个保存您要替换的数据的对象。
注意:每个个性化对象的替换总大小不得超过 10,000 字节。

node.js 库的默认替换包装器是“{{”和“}}”。以下是如何使用一个msg,使用个性化替换发送多封电子邮件的示例:

const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);

const recipients = [
  {
    emailAddress: "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="bac8dfd9d3cad3dfd4ce8bfadfd7dbd3d694dbdedec8dfc9c9" rel="noreferrer noopener nofollow">[email protected]</a>",
    preferredName: "Jonny",
    counselorName: "Smith"
  },
  {
    emailAddress: "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b0c2d5d3d9c0d9d5dec482f0d5ddd1d9dc9ed1d4d4c2d5c3c3" rel="noreferrer noopener nofollow">[email protected]</a>",
    preferredName: "Jay",
    counselorName: "Smith"
  }
];

const msg = {
  // this will create an array of objects with `to` and `substitutions ` properties
  personalizations: recipients.map(r => {
    return {
      to: r.emailAddress,
      substitutions: {
        preferredName: r.preferredName,
        counselorName: r.counselorName
      }
    }
  }),
  from: '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="572e382225172432393330253e3379243239333225" rel="noreferrer noopener nofollow">[email protected]</a>',
  subject: 'Ahoy!',
  html: `<p>Ahoy {{preferredName}}!</p>
  <p>It is a pleasure to meet you.</p>
  <p>Best, {{counselorName}}</p>`
};

sgMail.send(msg).then(() => {
  console.log('emails sent successfully!');
}).catch(error => {
  console.log(error);
});

请注意包含 {{preferredName}}{{counselorName}} 等替换标记的 html 模板。这些标记名称必须与 substitutions 属性中对象的键匹配。

警告:替换标记不会自动对用户输入进行 HTML 编码,因此您必须自行对其进行 HTML 编码以防止 HTML 注入(inject)攻击。您可以了解更多HTML injection attacks here .

<小时/>

如果您不需要在代码中存储电子邮件正文模板,则可以使用 Dynamic Email Template as shown in this sample 。您可以在 SendGrid 应用程序中创建动态电子邮件模板,并使用 Handlebars 模板将模板数据添加到您的电子邮件中。

使用动态电子邮件模板更新前面的示例将如下所示:

const msg = {
  // this will create an array of objects with `to` and `dynamicTemplateData` properties
  personalizations: recipients.map(r => {
    return {
      to: r.emailAddress,
      dynamicTemplateData: {
        preferredName: r.preferredName,
        counselorName: r.counselorName
      }
    }
  }),
  from: '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c8b1a7bdba88bbada6acafbaa1ace6bbada6acadba" rel="noreferrer noopener nofollow">[email protected]</a>',
  templateId: 'd-[REPLACE_WITH_YOUR_TEMPLATE_ID]'
};

sgMail.send(msg).then(() => {
  console.log('emails sent successfully!');
}).catch(error => {
  console.log(error);
});

subjectsubstitutions 属性已被删除。已添加 templateIddynamicTemplateData

注意:动态电子邮件模板使用 Handlebars 模板,该模板将对用户输入进行 HTML 编码,从而保护您免受 HTML 注入(inject)。

<小时/>

最后建议:Twilio推荐schedule your bulk emails使用 sendAt 属性而不是立即发送。

引自SendGrid docs :

This technique allows for a more efficient way to distribute large email requests and can improve overall mail delivery time performance. This functionality:

  • Improves efficiency of processing and distributing large volumes of email.
  • Reduces email pre-processing time.
  • Enables you to time email arrival to increase open rates.
  • Is available for free to all SendGrid customers.
<小时/>

关于node.js - Sendgrid 连接上的 Azure 应用服务 SNAT 端口耗尽,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72200424/

相关文章:

azure - 自动更新从 github 存储库更改为 Azure DevOps

node.js - Vue.js 抛出多个警告 : Invalid prop: type check failed for prop "items". 预期数组,得到值为 ""的字符串

node.js - 当两个人向您的 Node.js 应用程序发送 POST 请求时会发生什么?

javascript - 无法访问对象的一个​​属性,它是未定义的

node.js - 设置 Sequelize 关联时出现问题 - 使用 'include' 查询失败

outlook-addin - 如何在 Microsoft Outlook 2010 中指定外发电子邮件的 X header

sql - Azure 数据传输身份列种子跃升 10,000

c# - 如何识别用于调用 Azure Functions 的 HTTP 方法(动词)

javascript - Sendgrid Node js 不发送电子邮件。程序没有错误信息

airflow - 尝试在 Airflow 上使用 SendGrid 时出现错误 400(Google Composer)