我需要在注册过程中发送电子邮件,因此我正在使用 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;
}
}
如果这是在网络应用程序中做的最佳做法,请告诉我吗?
答案 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会将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个线程。
更好的解决方案是使用ExecutorService
(example),如果您使用JEE7,或ManagedExecutorService
使用Spring(docs)。
如果您使用Tomcat,请阅读this thread。
答案 2 :(得分:1)
最佳做法是使用单个ExecutorService
为所有请求提供线程池。您可能希望使用非零但有限的线程数来配置ExecutorService。
这里的想法是,您将拥有一些在应用程序的整个生命周期中重用的线程。如果在发送电子邮件时出现临时减速(或暂停),您将获得额外的好处,您最终会得到越来越多的线程相反,您最终会得到越来越多的工作(要发送的电子邮件)要执行,这比额外的线程要少得多。