Using java mail SMTPTransport to send an email, it arrives locally in seconds. Once on the server, it takes about 20-30 seconds. In the production environment, sending directly fails.

1. Code

pom file

 <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--Pom.xml adds a reference to javax.mail, or the project introduces the javax.mail jar package-->
    <dependency>
      <groupId>com.sun.mail</groupId>
      <artifactId>javax.mail</artifactId>
      <version>1.6.2</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>5.3.20</version>
    </dependency>

Send case

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.mail.util.ByteArrayDataSource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Properties;

public class App {
    public static void main(String[] args) {
        Properties property = new Properties();
        property.put("mail.smtp.auth", "true");
        // Email sending timeout settings
        property.put("mail.smtp.connectiontimeout", 3000);
        property.put("mail.smtp.timeout", 30000);
        property.put("mail.smtp.host", "This is the email server address");
        Authenticator authenticator = new MailAuthenticator("Email Account", "Password");
        
        try {
            String sendto = "[email protected]"; //Receiver
            Session mailSession = Session.getDefaultInstance(property, authenticator);
            MimeMessage message = new MimeMessage(mailSession);
            String nick = "";
            try {
                nick = MimeUtility.encodeText("Topic");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            message.setFrom(new InternetAddress(nick + " <" + "Email Account" + ">"));
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(sendto));
            message.setSubject("Test Email");
            Multipart multipart = new MimeMultipart();
            BodyPart contentPart = new MimeBodyPart();
            contentPart.setContent("Test body", "text/html;charset=UTF-8");
            multipart.addBodyPart(contentPart);
            message.setContent(multipart);
            // This is an attachment, placed locally
            InputStream inputStream = App.class.getResourceAsStream("/fapiao.pdf");
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buffer;
            try {
                buffer = new byte[inputStream.available()];
                int n = 0;
                while (-1 != (n = inputStream.read(buffer))) {
                    output.write(buffer, 0, n);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            byte[] content = output.toByteArray();
            // Here we simulate adding three pdf attachments
            for(int i=0; i<3; i + + ) {
                BodyPart attachmentBodyPart = new MimeBodyPart();
                DataSource source = new ByteArrayDataSource(content, "application/pdf");
                attachmentBodyPart.setDataHandler(new DataHandler(source));
                try {
                    attachmentBodyPart.setFileName(MimeUtility.encodeWord("mypdf.pdf"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                multipart.addBodyPart(attachmentBodyPart);
            }
            Long start = System.currentTimeMillis();
            System.out.println("Send mail to " + sendto + ", with attachments, total size: " + message.getSize());
            Transport.send(message);
            System.out.println("Email sent successfully, time consuming=" + (System.currentTimeMillis()-start) + "ms");
        } catch (MessagingException e) {
            e.printStackTrace();
        }
        return;
    }
}

2. Phenomenon

When tested locally, the email was received in a few seconds, but when sent to the server, it took about 20-30 seconds to receive. In a production environment, a large number of emails are accumulated, which directly leads to failure to send.

Error message:

Could not connect to SMTP host: mail.bjgas.com, port: 25, response: 421]

or

Exception EOF when sending email

3. Troubleshooting

1. Tracking

By tracing the source code of the local mail sending program, we finally found that the server did not configure the host name of the machine in hosts.

Through the above code tracking, it is found that when executing ehlo xxx , the value of xxx will be adjusted by default to getlocalHost() , because the localHostName is empty at the beginning. , so call the InetAddress.getLocalHost() method. The server’s local IP is obtained through the InetAddress.getAddressesFromNameService(local, null); method.

Finally, the nameService.lookupAllHostAddr(host) method is called. When the host name is not added to the hosts file, the getaddrinfo call returns an error code. At this time, jdk will instead call the lookupIfLocalhost method, which internally calls the operating system’s getifaddrs method to obtain all the IP addresses of the machine (this step alone takes about 10 seconds on my side); after obtaining all the IP addresses, a series of parsing processes are performed, which ultimately leads to slow delivery, timeout or other problems when sending emails. Report an error.

2. Summary:

When no host name is added to the hosts file, all IP addresses of the local machine will be returned;

When the host name is added to the hosts file, only the configured IP address of 127.0.01 will be returned;

4. Solution

1. Configure the local IP or domain name corresponding to the host name in the local /etc/hosts file; (it is recommended to configure both)

127.0.0.1 hostname
ip/domain name hostname

2. Write the configuration in the code and prevent it from calling the getlocalhost() method by default

 property.put("mail.smtp.localhost","host name");
 property.put("mail.smtp.localaddress","host ip");

Method 2 has not been verified yet, please use it with caution.