Java 创建用于发送电子邮件的后台线程
我需要在注册过程中发送一封电子邮件,因此我正在使用Java Mail API,这工作正常,但观察到 电子邮件处理过程花费了将近6秒(太长了),因此Ajax调用使用户等待响应的时间太长 因此,我决定使用后台线程发送电子邮件,这样用户就不必等待Ajax调用响应(Jersey REST Web服务调用) 我的问题是,在Web应用程序中为每个请求创建线程是一种好的做法吗Java 创建用于发送电子邮件的后台线程,java,multithreading,Java,Multithreading,我需要在注册过程中发送一封电子邮件,因此我正在使用Java Mail API,这工作正常,但观察到 电子邮件处理过程花费了将近6秒(太长了),因此Ajax调用使用户等待响应的时间太长 因此,我决定使用后台线程发送电子邮件,这样用户就不必等待Ajax调用响应(Jersey REST Web服务调用) 我的问题是,在Web应用程序中为每个请求创建线程是一种好的做法吗 @Path("/insertOrUpdateUser") public class InsertOrUpdateUser {
@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;
}
}
这是我发送电子邮件的实用程序类
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;
}
}
请告诉我这是否是在web应用程序中执行的最佳做法???最佳做法是使用单个
ExecutorService
为所有请求提供线程池。您可能希望为ExecutorService配置非零但数量有限的线程
这里的想法是,在应用程序的整个生命周期中,您将拥有一些可重用的线程。您还可以获得额外的好处,即如果发送电子邮件时出现暂时性的减速(或暂停),那么最终不会有越来越多的线程,而是会有越来越多的工作(要发送的电子邮件)需要执行,这比额外线程占用的资源少很多。在JavaWebSerer上手动创建
ExecutorService
是个坏主意。在每个请求的实现中创建10个线程
更好的解决方案是使用JEE7时使用ManagedExecutorService
(),使用Spring()时使用ThreadPoolTaskExecutor
如果您使用Tomcat,您应该阅读。有很多方法可以处理它,因此这完全取决于您的应用程序服务器是否有足够的资源(内存、线程等)来处理您的实现,因此它使您成为决定采用哪种方法的最佳人选 因此,如果设计证明是合理的,那么产生并行线程来执行某件事情并不是一种坏做法,但通常应该使用受控线程 请注意,无论您使用
newSingleThreadExecutor()
还是newFixedThreadPool(nThreads)
,在引擎盖下始终会创建一个ThreadPoolExecutor
对象
我的建议是使用下面列表中的秒选项,即“受控线程数”,并在其中指定最大线程数,如您所见
每个请求一个线程
在这种方法中,将为来自GUI的每个传入请求创建一个线程,因此,如果您收到10个插入/更新用户的请求,那么将生成10个线程,这些线程将发送电子邮件
这种方法的缺点是无法控制线程的数量,所以可以以StackOverflowException结束,或者可能是内存问题
请确保关闭executor服务,否则将浪费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
的优势
您可以在应用程序服务器启动时初始化singletonThreadPoolExecutor
,如果没有请求,则不会出现线程,这样做是安全的。事实上,我对prod应用程序使用了类似的配置
我使用的生存时间是1秒,所以如果一个线程在JVM中的理想时间超过1秒,那么它就会死亡
请注意,由于相同的线程池用于处理所有请求,所以它应该是单线程池,不要关闭此线程池,否则您的任务将永远不会执行
// 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);
}
}
});
电子邮件是从服务器端发送的,不是吗?因此,在哪个线程中运行它并不重要,因为它总是需要相同的时间。也许您想向用户显示一个循环的中间进度条,表明他的请求可能需要几秒钟才能完成。您是否也考虑了您的互联网速度?对于造成的混乱,很抱歉,在后台线程的帮助下,Ajax调用没有等待电子邮件进程,因此Ajax响应没有问题,但我关心的是正在创建的线程的数量。您的示例并没有显示为每个请求创建一个新线程。它显示了向线程池提交任务,这是一个更好的计划。@NathanHughes,所以下面这一行不是问题??ExecutorService ExecutorService=Executors.newFixedThreadPool(10);与我的问题相关,这似乎很有用,是否可以为我的案例配置ThreadPool?是的,只需在类级别将ExecutorService设置为静态(如记录器),并且在类加载并激活时将创建一次,直到应用程序停止。您可以实现一个
ServletContextListener
来明确管理生命周期,并确保正确关闭和清理(例如,您的应用程序将等待所有消息发送后再关闭)。我正在使用tomcat,非常感谢,我可以使用它作为完成此任务的起点。在最后一个选项中,ExecutorService是否也应该使用Singleton概念创建,还是可以为每个传入请求创建emailExecutor?@zee我在代码中提到-//这应该是一个Singleton
。如果您执行的是ExecutorService emailExecutor=Executors.newCachedThreadPool()
对于每个请求,基本上就是为eac创建一个新的线程池
// 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);
}
}
});