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");
}
>
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 >
