20

我可以使用 javax.mail API 发送邮件。但这里的问题是平均每封邮件需要大约 4.3 秒才能发送到目的地。

如果我按顺序发送 20 封邮件,大约需要 86.599 秒。对于我的要求,这种方法不起作用。我正在寻找一种可以在更短的时间内发送大量邮件的方法。

当我查看调试日志时,API 正在尝试对其发送的每条消息向 SMTP 服务器进行身份验证。但我只创建一次会话,并对我发送的所有邮件使用相同的会话。现在我的问题是,每次向 smtp 服务器进行身份验证时,这不是一个开销过程吗?没有更好的方法吗?

以下是您可能会发现有用的日志跟踪。

250-AUTH LOGIN PLAIN XOAUTH XOAUTH2
250 ENHANCEDSTATUSCODES
DEBUG SMTP: Found extension "SIZE", arg "35882577"
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "AUTH", arg "LOGIN PLAIN XOAUTH XOAUTH2"
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Attempt to authenticate
DEBUG SMTP: check mechanisms: LOGIN PLAIN DIGEST-MD5 NTLM 
DEBUG SMTP: AUTH LOGIN command trace suppressed
DEBUG SMTP: AUTH LOGIN succeeded

请让我知道您对此的想法,非常感谢您对此的任何帮助。

-纳伦德拉

4

5 回答 5

27

你是如何发送消息的?JavaMail FAQ建议静态Transport.send方法将为每条消息打开一个新连接,因为它是一种方便的方法,可以创建合适的实例Transport、连接它、调用sendMessage然后再次关闭连接。如果您从您获得自己的Transport实例,Session您可以连接一次,然后sendMessage重复调用以在一个连接上发送多条消息,最后close是它。类似于(未经测试)的东西:

Transport t = session.getTransport();
t.connect();
try {
  for(Message m : messages) {
    m.saveChanges();
    t.sendMessage(m, m.getAllRecipients());
  }
} finally {
  t.close();
}

更新为使用资源块尝试:

try (Transport t = session.getTransport()) {
    t.connect();
    for(Message m : messages) {
        m.saveChanges();
        t.sendMessage(m, m.getAllRecipients());
    }
}
于 2012-11-08T11:34:09.683 回答
10

我在工作中也有同样的要求。我必须发送批量电子邮件和独立电子邮件。我没有找到简单而令人满意的答案:批量电子邮件可以使用单个连接发送,但独立电子邮件不能,直到我创建异步缓冲以批量发送电子邮件。

Transport最后但并非最不重要的一点是,在短时间内使用大量连接会导致 a no more socket handles are available,因为所有端口都卡在该TIME_WAIT状态。

我最终得出结论,最好的将是一个 SMTP 连接池,因为不存在库(至少是免费的),所以我使用 Apache Common Pool 和 Java Mail 创建了我的库:

//Declare the factory and the connection pool, usually at the application startup
SmtpConnectionPool smtpConnectionPool = new SmtpConnectionPool(SmtpConnectionFactoryBuilder.newSmtpBuilder().build());

//borrow an object in a try-with-resource statement or call `close` by yourself
try (ClosableSmtpConnection transport = smtpConnectionPool.borrowObject()) {
    MimeMessage mimeMessage = new MimeMessage(session);
    MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, false);
    mimeMessageHelper.addTo("to@example.com");
    mimeMessageHelper.setFrom("from@example.com");
    mimeMessageHelper.setSubject("Hi!");
    mimeMessageHelper.setText("Hello World!", false);
    transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
}

//Close the pool, usually when the application shutdown
smtpConnectionPool.close();
于 2015-05-15T14:33:29.153 回答
1

上述答案要么不相关,要么太复杂而无法实施。所以这是我发布在 Spring Boot 中发送批量电子邮件的最简单解决方案

只需使用

mimeMessageHelper.setBcc(emailList);

在哪里,

mimeMessageHelperMimeMessageHelperemailListString[] emailList

笔记:

确保您没有使用

mimeMessageHelper.setTo(emailList)

否则,它将在收件人收到的电子邮件中显示所有收件人的电子邮件地址。

有关更多参考,ThisThis可以帮助您学习如何在 Spring Boot 中发送电子邮件

于 2021-04-14T07:31:08.810 回答
0

不知道标准 Java 邮件 API 是否允许您尝试完成的任务(会话重用),但您可以考虑使用多线程:

我会使用 ThreadPool 并向其提交邮件发送作业。然后您在作业类代码中执行任何错误处理/重新发送,该代码由 ThreadPool 异步执行,您的主线程可以继续执行其他操作。提交作业只需几毫秒。自从我在 Java 中使用线程池实现某些东西已经有一段时间了,但我记得它相当简单明了。如果你谷歌“Java ThreadPool”,你会发现很多资料。

于 2012-11-08T11:07:03.367 回答
-1

您可以使用线程池,因为它提供了非常好的性能。我已经实现并分享了下面的代码片段。

try  {
    ExecutorService executor = Executors.newFixedThreadPool("no. of threads"); // no. of threads is depend on your cpu/memory usage it's better to test with diff. no. of threads.
    Runnable worker = new MyRunnable(message); // message is the javax.mail.Message
    executor.execute(worker);
    executor.shutdown();
    executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}
于 2015-10-14T15:50:00.843 回答