web-dev-qa-db-fra.com

Comment envoyer des mails en masse en utilisant l'API javax.mail efficacement? & Pouvons-nous utiliser des sessions authentifiées de réutilisation pour améliorer la vitesse?

Je peux envoyer un courrier en utilisant l'API javax.mail. Mais le problème ici est que, en moyenne, chaque courrier prend environ 4,3 secondes pour être envoyé à la destination.

Si j'envoie 20 mails séquentiellement, cela prend environ 86,599 secondes. Pour mon besoin, cette approche ne fonctionnera pas. Je recherche une approche qui puisse envoyer un grand nombre de mails en moins de temps.

Lorsque j'ai consulté le journal de débogage, l'API tente de s'authentifier auprès du serveur SMTP pour chaque message envoyé. Mais je ne crée une session qu'une seule fois et j'utilise la même session pour tous les courriers que j'envoie. Maintenant, ma question est la suivante: n’est-ce pas un processus de surcharge à chaque fois s’authentifier auprès du serveur smtp? N'y a-t-il pas une meilleure approche?

Vous trouverez ci-dessous la trace du journal que vous jugerez utile.

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

S'il vous plaît laissez-moi savoir vos pensées à ce sujet et toute aide à ce sujet est vraiment appréciée.

-Narendra

16
Narendra

Comment envoyez-vous les messages? JavaMail FAQ suggère que la méthode statique Transport.send ouvre une nouvelle connexion pour chaque message, car il s'agit d'une méthode pratique qui crée une instance Transport appropriée, la connecte, appelle sendMessage puis ferme la connexion à nouveau. Si vous obtenez votre propre instance Transport à partir de Session, vous pouvez vous connecter une fois, puis appelez sendMessage à plusieurs reprises pour envoyer plusieurs messages sur la même connexion et, enfin, close. Quelque chose dans le genre de (non testé):

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

J'ai la même exigence au travail. Je dois envoyer des emails en masse et des emails autonomes. Je ne trouve pas de réponse simple et satisfaisante: les e-mails en masse peuvent être envoyés à l'aide d'une seule connexion, mais les e-mails autonomes ne le peuvent pas jusqu'à ce que je crée une mise en mémoire tampon asynchrone pour envoyer des e-mails en batch.

Dernier point mais non le moindre, utiliser beaucoup de connexions Transport dans un court laps de temps peut conduire à un no more socket handles are available car tous les ports sont bloqués dans l'état TIME_WAIT.

Je conclus enfin que le meilleur serait un pool de connexions SMTP et qu’aucune bibliothèque n’existe (au moins gratuite) je crée le mien avec Apache Common Pool et 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("[email protected]");
    mimeMessageHelper.setFrom("[email protected]");
    mimeMessageHelper.setSubject("Hi!");
    mimeMessageHelper.setText("Hello World!", false);
    transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
}

//Close the pool, usually when the application shutdown
smtpConnectionPool.close();
11
Nicolas Labrot

Aucune idée si l'API de messagerie Java standard permet ce que vous essayez d'accomplir (réutilisation de session), mais vous pouvez envisager d'utiliser le multi-threading:

J'utiliserais un ThreadPool et lui soumettrais des travaux d'envoi de courrier. Ensuite, vous effectuez toutes les erreurs de traitement/renvoi dans le code de classe de travail, exécuté par le ThreadPool de manière asynchrone, et votre thread principal peut reprendre d'autres tâches. Soumettre un travail ne prend que quelques millisecondes. Cela faisait longtemps que je n'avais pas implémenté quelque chose avec les pools de threads en Java, mais je me souviens que c'était assez facile et simple. Si vous "Google ThreadPool" sur Google, vous trouverez beaucoup de matériel.

1
FelixD

Vous pouvez utiliser le pool de threads car il donne de très bonnes performances. J'ai implémenté et partagé le fragment de code ci-dessous.

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);
}
0
S Boot