简体   繁体   English

Spring Boot 电子邮件发送抛出 SocketTimeoutException:读取超时

[英]Spring Boot email sending throws SocketTimeoutException: Read timed out

I've already researched the topic for a few days but none of the answers I found online did the trick for me.我已经研究了这个话题几天了,但我在网上找到的答案都没有对我有用。

Context: I've got a Spring Boot web application which sends automatic emails notifications using Java Mail API and Spring Boot Starter Mail.上下文:我有一个 Spring Boot Web 应用程序,它使用 Java Mail API 和 Spring Boot Starter Mail 发送自动电子邮件通知。

It is using GMail SMTP server with a GSuite account.它使用带有 GSuite 帐户的 GMail SMTP 服务器。 I recently upgraded to use Spring 5.0.6 and Spring Boot 2.0.2 and the email sending stopped working.我最近升级为使用 Spring 5.0.6 和 Spring Boot 2.0.2,并且电子邮件发送停止工作。

A few clues:几个线索:

  • the Java code sending the email is the same as before发送电子邮件的 Java 代码与之前相同
  • Gmail SMTP still works correctly (from another VM using older version of the application with the same settings and authentication, the emails are sent properly). Gmail SMTP 仍然正常工作(从另一个使用具有相同设置和身份验证的旧版本应用程序的虚拟机,电子邮件可以正确发送)。
  • unless I am missing something, the application configuration remains the same as before除非我遗漏了一些东西,否则应用程序配置与以前相同

The things that have changed:已经改变的事情:

  • upgrade to Spring 5.0.6升级到 Spring 5.0.6
  • upgrade to Spring Boot 2.0.2升级到 Spring Boot 2.0.2
  • changes in many places in the Java code to match this upgrades and add features in other parts of the app在 Java 代码中的许多地方进行更改以匹配此升级并在应用程序的其他部分添加功能
  • The IP address of the VM is different than before (AWS EC2 instance) VM 的 IP 地址与以前不同(AWS EC2 实例)

Here are the relevant dependencies in pom.xml :以下是 pom.xml 中的相关依赖项:

    <!-- https://mvnrepository.com/artifact/javax.mail/javax.mail-api -->
    <dependency>
        <groupId>javax.mail</groupId>
        <artifactId>javax.mail-api</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>

Here is the application.yml relevant to Spring mail:这是与 Spring 邮件相关的 application.yml:

spring:
  mail:
    host: ${FT_MAIL_SMTP_HOST}
     port: ${FT_MAIL_SMTP_PORT}
     username: ${FT_MAIL_SMTP_USERNAME}
     password: ${FT_MAIL_SMTP_PASSWORD}
     debug: false
     properties:
       mail:
         smtp:
           starttls:
             enable: ${FT_MAIL_SMTP_STARTTLS}
             required: ${FT_MAIL_SMTP_TLSREQUIRED}
           auth: ${FT_MAIL_SMTP_AUTH}
           connectiontimeout: ${FT_MAIL_SMTP_CONN_TIMEOUT}
           timeout: ${FT_MAIL_SMTP_TIMEOUT}
           writetimeout: ${FT_MAIL_SMTP_WRITE_TIMEOUT}

These variables are defined in the environment:这些变量在环境中定义:

FT_MAIL_SMTP_HOST=smtp.gmail.com
FT_MAIL_SMTP_PORT=587
FT_MAIL_SMTP_USERNAME=myaccount@myapp.com
FT_MAIL_SMTP_PASSWORD=mypassword

FT_MAIL_SMTP_STARTTLS=true
FT_MAIL_SMTP_TLSREQUIRED=true
FT_MAIL_SMTP_AUTH=true
FT_MAIL_SMTP_CONN_TIMEOUT=5000
FT_MAIL_SMTP_TIMEOUT=5000
FT_MAIL_SMTP_WRITE_TIMEOUT=5000

Here is the Spring @Service used to send the email (unchanged):这是用于发送电子邮件的 Spring @Service(未更改):

@Service
public class EmailServiceImpl {

@Autowired
public JavaMailSender emailSender;

@Autowired
private SpringTemplateEngine templateEngine;

@Value("${myapp.mail.from}")
private String fromAddress;

@Value("${myapp.mail.replyto}")
private String replyToAddress;

public void sendTemplatedMessage(String template, String to, String subject, Map<String, Object> model) throws MailException, MessagingException {  
    sendTemplatedMessage(template, to, fromAddress, replyToAddress, subject, model);
}

public void sendTemplatedMessage(String template, String to, String from, String subject, Map<String, Object> model) throws MailException, MessagingException { 
    sendTemplatedMessage(template, to, from, replyToAddress, subject, model);
}

private void sendTemplatedMessage(String template, String to, String from, String replyTo, String subject, Map<String, Object> model) throws MailException, MessagingException {    

    MimeMessage message = emailSender.createMimeMessage();

    MimeMessageHelper helper = new MimeMessageHelper(message,
            MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED,
            StandardCharsets.UTF_8.name());

    //helper.addAttachment("logo.png", new ClassPathResource("memorynotfound-logo.png"));
    Context context = new Context();
    context.setVariables(model);
    String html = templateEngine.process(template, context);

    helper.setTo(to);
    helper.setFrom(from);
    helper.setReplyTo(from);
    helper.setSubject(subject);
    helper.setText(html, true);

    emailSender.send(message);
}

public void sendSimpleMessage(String to, String from, String subject, String text) {
    try {           
        SimpleMailMessage message = new SimpleMailMessage(); 
        message.setTo(to);
        message.setFrom(from);
        message.setSubject(subject); 
        message.setText(text);
        emailSender.send(message);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
}

Now here is the error I get when trying to send an email:现在这是我尝试发送电子邮件时遇到的错误:

04:42:19.900 [https-jsse-nio-443-exec-3] ERROR c.f.controller.StayController - Could not send Guest confirmation email to gfgorostidi@protonmail.com
org.springframework.mail.MailSendException: Failed to close server connection after message sending; nested exception is javax.mail.MessagingException: Exception reading response;
  nested exception is:
 java.net.SocketTimeoutException: Read timed out
 at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:482)
 at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:359)
 at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:354)
 at com.myapp.util.EmailServiceImpl.sendTemplatedMessage(EmailServiceImpl.java:61)
 at com.myapp.util.EmailServiceImpl.sendTemplatedMessage(EmailServiceImpl.java:35)
 at com.myapp.controller.StayController.sendConfirmEmailToGuest(StayController.java:437)
 at com.myapp.controller.StayController.saveStay(StayController.java:383)
 at com.myapp.controller.StayController.createStay(StayController.java:163)
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 ......
 ......
 ......
 at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
 at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1135)
 at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
 at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
 at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: javax.mail.MessagingException: Exception reading response;
  nested exception is:
 java.net.SocketTimeoutException: Read timed out
 at com.sun.mail.smtp.SMTPTransport.readServerResponse(SMTPTransport.java:2202)
 at com.sun.mail.smtp.SMTPTransport.close(SMTPTransport.java:1212)
 at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:473)
 ... 104 more
Caused by: java.net.SocketTimeoutException: Read timed out
 at java.base/java.net.SocketInputStream.socketRead0(Native Method)
 at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
 at java.base/java.net.SocketInputStream.read(SocketInputStream.java:171)
 at java.base/java.net.SocketInputStream.read(SocketInputStream.java:141)
 at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:425)
 at java.base/sun.security.ssl.SSLSocketInputRecord.bytesInCompletePacket(SSLSocketInputRecord.java:65)
 at java.base/sun.security.ssl.SSLSocketImpl.bytesInCompletePacket(SSLSocketImpl.java:918)
 at java.base/sun.security.ssl.AppInputStream.read(AppInputStream.java:144)
 at com.sun.mail.util.TraceInputStream.read(TraceInputStream.java:124)
 at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:252)
 at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:271)
 at com.sun.mail.util.LineInputStream.readLine(LineInputStream.java:89)
 at com.sun.mail.smtp.SMTPTransport.readServerResponse(SMTPTransport.java:2182)
 ... 106 more
org.springframework.mail.MailSendException: Failed to close server connection after message sending; nested exception is javax.mail.MessagingException: Exception reading response;
  nested exception is:
 java.net.SocketTimeoutException: Read timed out
 at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:482)
 at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:359)
 at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:354)
 at com.myapp.util.EmailServiceImpl.sendTemplatedMessage(EmailServiceImpl.java:61)
 at com.myapp.util.EmailServiceImpl.sendTemplatedMessage(EmailServiceImpl.java:35)
 at com.myapp.controller.StayController.sendConfirmEmailToGuest(StayController.java:437)
 at com.myapp.controller.StayController.saveStay(StayController.java:383)
 at com.myapp.controller.StayController.createStay(StayController.java:163)
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 ......
 ......
 ......
 at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
 at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
 at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1135)
 at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
 at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
 at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: javax.mail.MessagingException: Exception reading response;
  nested exception is:
 java.net.SocketTimeoutException: Read timed out
 at com.sun.mail.smtp.SMTPTransport.readServerResponse(SMTPTransport.java:2202)
 at com.sun.mail.smtp.SMTPTransport.close(SMTPTransport.java:1212)
 at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:473)
 ... 104 more
Caused by: java.net.SocketTimeoutException: Read timed out
 at java.base/java.net.SocketInputStream.socketRead0(Native Method)
 at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
 at java.base/java.net.SocketInputStream.read(SocketInputStream.java:171)
 at java.base/java.net.SocketInputStream.read(SocketInputStream.java:141)
 at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:425)
 at java.base/sun.security.ssl.SSLSocketInputRecord.bytesInCompletePacket(SSLSocketInputRecord.java:65)
 at java.base/sun.security.ssl.SSLSocketImpl.bytesInCompletePacket(SSLSocketImpl.java:918)
 at java.base/sun.security.ssl.AppInputStream.read(AppInputStream.java:144)
 at com.sun.mail.util.TraceInputStream.read(TraceInputStream.java:124)
 at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:252)
 at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:271)
 at com.sun.mail.util.LineInputStream.readLine(LineInputStream.java:89)
 at com.sun.mail.smtp.SMTPTransport.readServerResponse(SMTPTransport.java:2182)
 ... 106 more

I have tried setting an incorrect SMTP server or bad credentials and this made the connection fail, so I assumed the server and credentials are correct as they are, and the error happens after a successful connection.我曾尝试设置不正确的 SMTP 服务器或错误的凭据,这导致连接失败,因此我假设服务器和凭据是正确的,并且在成功连接后发生错误。

The account used hasn't reached its limit, as another VM uses the same credentials and sends emails without problem.使用的帐户尚未达到其限制,因为另一个 VM 使用相同的凭据并毫无问题地发送电子邮件。

I've tried changing "Start TLS" settings to false and use port 465 instead, but this isn't working either.我尝试将“启动 TLS”设置更改为 false 并改用端口 465,但这也不起作用。

Any help is appreciated !!任何帮助表示赞赏! Thanks in advance!提前致谢!

After much more trial and error with the configuration, I found out that it required an application property "spring.mail.protocol" in the configuration.在对配置进行了多次反复试验后,我发现它在配置中需要一个应用程序属性“spring.mail.protocol”。

I've added the line protocol: smtp in application.yml:我在 application.yml 中添加了线路protocol: smtp

spring:
  mail:
    protocol: smtp

And that fixed the read timeout issue, email are now sent properly.并且解决了读取超时问题,现在可以正确发送电子邮件。 Hope that may help someone in the future.希望将来可以帮助某人。

I did face the same issue, but my scenario was a bit different我确实遇到了同样的问题,但我的情况有点不同

I was trying to send in a schedule manner using quartz我试图使用石英按计划方式发送

When I did not use Quartz, it was all working fine, but with quartz it started failing当我不使用 Quartz 时,一切正常,但是使用 Quartz 时它开始出现故障

The above solution did not help me, but pointed me in direction of looking at the properties that I had set.上述解决方案对我没有帮助,但为我指明了查看我设置的属性的方向。

Increasing the connection timeout did the job for me增加连接超时对我有用

Thus changed the application properties从而改变了应用程序属性

from:从:

spring.mail.properties.mail.smtp.timeout=3000

to:到:

spring.mail.properties.mail.smtp.timeout=25000

Hope it works for others as well希望它也适用于其他人

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM