Transport Performance problems

Hi,

I keep encountering a bottleneck whilst sending mails through the transport.connect, then transport.send method.

I'm building a bulk emailer which has to send out personalised mails. A loop is used to scroll through recipient email address' and build a personalised mail but each time I get to send there is a long wait. It takes 18 seconds to send 80 emails. This is far too long as the app must be able to deal with a possible 100,000 recipients.

Is this because for each email sent a connection is opened and closed on the mail server? Or is there a way for me to force it to stay open?

Any help much appreciated

/**

* Starts off the process to send out all the mails

*/

publicsynchronizedvoid sendBulkMails(DBConn dbc,long[] recipientIds)

{

// Create the recipient

Recipient recipient =new Recipient();

// Create the message

Message message =new MimeMessage(session);

Transport transport =null;

long startTime = System.currentTimeMillis();

try{

// Create transport and open connection to mail server

transport = session.getTransport("smtp");

transport.connect();

// loop through recipient ids loading each one seperately. Then send mail

// and update database to reflect that it has been sent

boolean sent;

for (int i=0;i<recipientIds.length;i++){

try{

recipient.load(dbc,recipientIds[i]);

// Fill its headers

message.setFrom(new InternetAddress(from));

message.setRecipient(Message.RecipientType.TO,new InternetAddress(recipient.getEmailAddress()));

message.setContent(getMessageBody(MailBuilder.getBuildHTML(recipient,htmlTemplate)));

message.setSentDate(new Date());

// append recipient id so we can identify bouncebacks

message.setHeader("X-recipientId--",Long.toString(recipient.getId()));

// Send message

transport.send(message);

sent =true;

}

catch(AddressException e){

sent =false;

System.out.println("Error parsing the address: "+e);

}

catch(SendFailedException e){

sent =false;

System.out.println("Error sending the message: "+e);

}

catch(MessagingException e){

sent =false;

System.out.println("Unexpected error: "+e);

}

catch(Exception e){

sent =false;

System.out.println("Unexpected error, but probably failure to load recipient: "+e);

}

// Update database to reflect if mail was sent successfully or not

if (sent){

recipient.setSent(EmailerConstants.SENT);

recipient.setSentDate(getDateAndTime());

recipient.update(dbc);

}else{

recipient.setSent(EmailerConstants.NOT_SENT);

recipient.update(dbc);

}

}

}

catch(MessagingException e){

System.out.println("Error connecting to Transport Provider: "+e);

}

finally{

// Ensure connection to mail server is closed

try{

transport.close();

}catch (MessagingException e){

System.out.println("Connection to mail server could not be closed: "+e);

}

}

// Print out to console how long batch took to send

long stopTime = System.currentTimeMillis();

double elapsedTime = (stopTime - startTime)/1000.0;

System.out.println("It took " +elapsedTime+" seconds to send the batch of emails");

}

>

[6009 byte] By [richardsimmonds] at [2007-9-26 2:27:53]
# 1

Hi Richard,

Did you ever find a solution for this problem?

I have had to do the exact same thing. I also had the problem of not being able to use my ISP's SMTP server to relay the mails, so I had to resolve the MXs for each email and set that as the host for the smtp relay!

Anyhow, what I did was to load all the emails into memory first, then plonk them onto a stack. I then kicked off a set number of worker threads (i found about 20 was optimal) to pick each mail from the stack and send it. I managed to send 120,000 emails in 1/5 hours last night.

I am however having a few problems with some email addresses not resolving and getting errors from the DNS lookup. I think I need to work on the MX resolution and sending part again, or find a better way of bypassing the ISP.

I would be happy to send you some sample code if you still need it,

Steve

S_P_G at 2007-6-29 9:42:47 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 2

Hi Richard and Steve!

I have exactly the same problem as Richard in trying to send a newsletter to 15,000 people. I have the same loop to get all the InternetAdresses and do a "Transport.send(mimemessage)" for each, but it's too long and I'm timed out after nearly 200 mails are sent...

Steve, your solution looks great and it is exactly what I would try to do... You said to Richard that you could give him the source code, could you do it for me too? I'm waiting for your answer, thanks a lot! ;o)

Denis

deezielinski at 2007-6-29 9:42:47 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 3

Guys,

OK, Happy to send you some code. Contact me via my email: steve . goodsell @ blueyonder . co . uk (Obviously remove the spaces!)

In the mean time here is a sample routine that does what I described in a less complicated way. I have not posted the actual sender class as it is long and also I am pretty precious about it!!!! So contact me and I will send it direct.. HTH, Steve

import java.util.*;

import javax.mail.*;

import javax.mail.Message.*;

import javax.mail.internet.*;

import com.spg.email.services.smtp.*;

/**

* Bulk Email Sender.

* Simple mailer class used to send emails in bulk using MX lookup and using threads.

* <p>Copyright: Copyright Steve Goodsell(c) 2003</p>

* @author Steve Goodsell

* @version 1.0

*/

public class BasicMailer {

private Vector emailList;

public static void main(String[] args) {

String[] recips = new String[]{"steve.goodsell@blueyonder.co.uk",

"steve.goodsell@visual-media-software.co.uk"

};

String from = "test@testing.co.uk";

String subject = "Testing " + new Date();

String text = "Hello Mailing World!";

BasicMailer mailer= new BasicMailer();

mailer.sendEmails(recips,from,subject,text);

}

public BasicMailer() {

emailList = new Vector();

}

public void sendEmails(String[] recipients, String from, String subject, String text){

//BUILD THE EMAIL STACK

System.out.println("Creating Emails...");

if( recipients != null){

for(int i=0; i < recipients.length;i++){

try{

emailList.add(createEmail(recipients,from,subject,text));

System.out.println(" Added recipient " + recipients);

}catch(Exception err){

System.out.println(err.getMessage());

}

}

}

//NOW SET UP A THREAD TO READ THEM

System.out.println("Setting up mailer threads...");

int maxThreads = 5;

Thread[] mailerThreads = new Thread[maxThreads];

for(int i=0; i < maxThreads; i++){

mailerThreads = new MailSender("Sender " + (i+1));

mailerThreads.start();

}

//NOW WAIT FOR ALL THREADS TO STOP!

System.out.println("Waiting for threads to finish");

for(int i=0; i < maxThreads; i++){

try{

mailerThreads.join();

System.out.println(" " + mailerThreads.getName() + " completes");

}catch(InterruptedException err){}

}

//DONE

System.out.println("DONE!!!!");

}

/**

* Simply returns the next email from the list.

* Return null if empty.

* Is thread safe.

*/

private synchronized Message getNextEmailToSend(){

if( emailList.size()==0){

System.out.println("No more mails to send");

return null;

}

System.out.println(emailList.size() + " more mails to send");

return (Message)emailList.remove(0);

}

/**

* Very basic set up of a mail to be sent.

* In real life you would set other things li X-Sender etc, as per your needs.

* @throws MessagingException

*/

private Message createEmail(String emailAddress,String fromAddress, String subject, String textToSend)throws MessagingException{

//USe a default session as we do not use this to send anymore!

Session s = Session.getDefaultInstance(new Properties());

Message message = new MimeMessage(s);

message.setRecipients(RecipientType.TO,createRecipientAddress(emailAddress));

message.setFrom(new InternetAddress(fromAddress));

message.setSubject(subject);

message.setSentDate(new Date());

//You may have to use a data hanlder for HTML!

message.setText(textToSend);

return message;

}

/**

* Helper to create an internet address.

* I use this so I can put test data here instead.

* @throws AddressException

*/

private InternetAddress[] createRecipientAddress(String address)throws AddressException{

InternetAddress[] destinationInetAddr = InternetAddress.parse(address, false);

return destinationInetAddr;

}

private class MailSender extends Thread{

public MailSender(String name){

super(name);

}

public void run(){

System.out.println(getName() + " Started");

Message msg = null;

while( (msg=getNextEmailToSend())!=null && !interrupted()){

int numTries = 0;

boolean success=false;

while(numTries<3){

try {

SMTPRemoteSender sender = new SMTPRemoteSender();

sender.sendMessage(msg);

success=true;

break;

}

catch (Exception err) {

System.out.println(getName() + " " + err.getMessage());

numTries++;

//Wait 5 secs before retry

try{sleep(5000);}catch(Exception e){}

}

}

if(!success){

System.out.println("Mail Failed..");

}

}

System.out.println(getName() + " Ends");

}

}

}

S_P_G at 2007-6-29 9:42:47 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 4

I think the problem is because you are doing the connect and then using the transport.send() method.

The send() method does a connect and close of its own. I'm guessing the latency is occuring because of your trying to open 2 connections. The docs say you're supposed to get an error if you try this.

Just replace the

transport.send(message);

with

transport.sendMessage(message);

and I'm guessing you will be ok. Please let me know if this works, I'm curious as well.

somaiah at 2007-6-29 9:42:47 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 5
Could you send me the code. I really could use itBrian
brian662 at 2007-6-29 9:42:47 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 6
Hi guys,I am also interested in this buld email work, please send me if any one has a complete working source code in java.Thanks in Advance,
ashiqkhan at 2007-6-29 9:42:47 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 7
Any solution to the problem. I got the code form Steve, and it runs without error, but messagees are not sent.Any clues
brian662 at 2007-6-29 9:42:47 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 8
Hi Steve,I am also working on similar kind of project where i need to send bulk sms in a day whenever a request comes. But performance is the issue. Can i have your code as referance for performance improvement?Thanks,Munjal
Monji at 2007-6-29 9:42:47 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...