创建后台用于发送电子邮件的线程

时间:2016-09-06 15:44:18

标签: java multithreading

我需要在注册过程中发送电子邮件,因此我正在使用 Java Mail API ,这很好用,但是观察到了         电子邮件进程花了将近6秒(这太长了)所以Ajax调用让用户等待响应太长时间

因此我决定使用后台线程来发送电子邮件,以便用户无需等待Ajax呼叫响应(Jersey REST Web服务调用)

我的问题是,为每个请求在Web应用程序中创建线程是一个好习惯吗?

@Path("/insertOrUpdateUser")
public class InsertOrUpdateUser {
        final static Logger logger = Logger.getLogger(InsertOrUpdateUser.class);
        @GET
        @Consumes("application/text")
        @Produces("application/json")
        public String getSalesUserData(@QueryParam(value = "empId") String empId
                        )
                        throws JSONException, SQLException {
                JSONObject final_jsonobject = new JSONObject();
            ExecutorService executorService = Executors.newFixedThreadPool(10);
                                executorService.execute(new Runnable() {
                                 public void run() {
                                         try {
                                                        SendEmailUtility.sendmail(emaildummy);
                                                } catch (IOException e) {
                                                        logger.error("failed",e);

                                                }
                                 }
                              });

                        }


                } catch (SQLException e) {

                } catch (Exception e) {


                }


                   finally {

                        }


                return response;
        }




}

这是我发送电子邮件的Utility类

public class SendEmailUtility
{
    public static String sendmail(String sendto)
        throws IOException
    {
        String result = "fail";
        Properties props_load = getProperties();
        final String username = props_load.getProperty("username");
        final String password = props_load.getProperty("password");
        Properties props_send = new Properties();
        props_send.put("mail.smtp.auth", "true");
        props_send.put("mail.smtp.starttls.enable", "true");
        props_send.put("mail.smtp.host", props_load.getProperty("mail.smtp.host"));
        props_send.put("mail.smtp.port", props_load.getProperty("mail.smtp.port"));

        Session session = Session.getInstance(props_send,
            new javax.mail.Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication()
                {
                    return new PasswordAuthentication(username, password);
                }
            });

        try {
            Message message = new MimeMessage(session);
            message.setFrom(new InternetAddress(props_load.getProperty("setFrom")));
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(sendto));
            message.setText("Some Text to be send in mail");
            Transport.send(message);
            result = "success";
        } catch (MessagingException e) {
            result = "fail";
            logger.error("Exception Occured - sendto: " + sendto, e);
        }
        return result;
    }
}

如果这是在网络应用程序中做的最佳做法,请告诉我吗?

3 个答案:

答案 0 :(得分:5)

有许多方法可以处理它,所以这一切都取决于你的应用服务器是否有那么多资源(内存,线程等)来处理你的实现,所以它让你最好的人决定采用哪种方法走。

因此,如果通过设计证明它是合理的,那么产生并行线程的做法并不坏,但通常你应该使用受控线程。

请注意,无论您使用的是newSingleThreadExecutor()还是newFixedThreadPool(nThreads),都会创建一个ThreadPoolExecutor对象。

我的建议将在下面的列表中使用秒选项,即"受控线程数",并在其中指定最大线程数,如您所见。

每个请求一个线程

在这种方法中,将为来自GUI的每个传入请求创建一个线程,因此如果您收到10个插入/更新用户的请求,则将生成10个将发送电子邮件的线程。

这种方法的缺点是没有对线程数的控制,所以你可以用StackOverflowException结束或者可能是内存问题。

请确保关闭执行程序服务,否则最终会浪费JVM资源。

// inside your getSalesUserData() method
ExecutorService emailExecutor = Executors.newSingleThreadExecutor();
        emailExecutor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    SendEmailUtility.sendmail(emaildummy);
                } catch (IOException e) {
                    logger.error("failed", e);
                }
            }
        });
        emailExecutor.shutdown(); // it is very important to shutdown your non-singleton ExecutorService.

受控线程数

在这种方法中,将存在一些预定义数量的线程,这些线程将处理您的电子邮件发送要求。在下面的示例中,我正在启动一个最多10个线程的线程池,然后我使用LinkedBlockingQueue实现,这样可以确保如果有超过10个请求,并且当前所有10个线程都忙,那么请求过多排队等待而不会丢失,这是LinkedBlockingQueue Queue ThreadPoolExecutor实施的优势。

您可以在应用程序服务器启动时初始化单例// creating a thread pool with 10 threads, max alive time is 1 seconds, and linked blocking queue for unlimited queuing of requests. // if you want to process with 100 threads then replace both instances of 10 with 100, rest can remain same... // this should be a singleton ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); // inside your getSalesUserData() method executor.execute(new Runnable() { @Override public void run() { try { SendEmailUtility.sendmail(emaildummy); } catch (IOException e) { logger.error("failed", e); } } }); ,如果没有请求则不会出现任何线程,因此可以安全地执行此操作。事实上,我为我的prod应用程序使用了类似的配置。

我使用的时间是1秒,所以如果一个线程在JVM中理想的时间超过1秒,那么它就会死掉。

请注意,由于相同的线程池用于处理您的所有请求,因此它应该是单例并且不要关闭此线程池,否则您的任务将永远不会被执行。

ThreadPoolExecutor

Java的默认缓存线程池

这种方法与上面类似,只是Java会将ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());初始化为Integer.MAX_VALUE

此处最大线程数为// this should be a singleton ExecutorService emailExecutor = Executors.newCachedThreadPool(); // from you getSalesUserData() method emailExecutor.execute(new Runnable() { @Override public void run() { try { SendEmailUtility.sendmail(emaildummy); } catch (IOException e) { logger.error("failed", e); } } }); ,因此将根据需要创建线程,并且生存时间为60秒。

如果您想使用这种方式,那就是下面的方法。

{{1}}

答案 1 :(得分:2)

在java web serer上手动创建thing2是个坏主意。在每个请求的实现中,您将创建10个线程。

更好的解决方案是使用ExecutorServiceexample),如果您使用JEE7,或ManagedExecutorService使用Spring(docs)。

如果您使用Tomcat,请阅读this thread

答案 2 :(得分:1)

最佳做法是使用单个ExecutorService为所有请求提供线程池。您可能希望使用非零但有限的线程数来配置ExecutorService。

这里的想法是,您将拥有一些在应用程序的整个生命周期中重用的线程。如果在发送电子邮件时出现临时减速(或暂停),您将获得额外的好处,您最终会得到越来越多的线程相反,您最终会得到越来越多的工作(要发送的电子邮件)要执行,这比额外的线程要少得多。