转载地址:https://blog.csdn.net/ku360517703/article/details/50485710
背景
最近在做一个功能,需要批量或不定时散量发送邮件给不同的人。比方说注册功能,需要给注册人发送邮件,如果遇上系统新开放注册,有一定量的并发,如果一窝蜂地发,有可能造成smtp服务器拒绝421等状态
线程池
采用ThreadPoolExecutor线程池实现,他可以设定同时运行多少线程,多的任务可以排队。
-
public ThreadPoolExecutor(int corePoolSize,
-
int maximumPoolSize,
-
long keepAliveTime,
-
TimeUnit unit,
-
BlockingQueue<Runnable> workQueue,
-
RejectedExecutionHandler handler) {
-
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
-
Executors.defaultThreadFactory(), handler);
-
}
参数解释
corePoolSize: 核心线程数,会一直存活,即使没有任务,线程池也会维护线程的最少数量
maximumPoolSize: 线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间,当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。
unit: 线程池维护线程所允许的空闲时间的单位、可选参数值为:TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue: 线程池所使用的缓冲队列,常用的是:java.util.concurrent.ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue
handler: 线程池中的数量大于maximumPoolSize,对拒绝任务的处理策略,默认值ThreadPoolExecutor.AbortPolicy()。
-
package mailTest;
-
import java.util.concurrent.BlockingQueue;
-
import java.util.concurrent.ExecutorService;
-
import java.util.concurrent.LinkedBlockingQueue;
-
import java.util.concurrent.ThreadPoolExecutor;
-
import java.util.concurrent.TimeUnit;
-
public class PoolSend {
-
BlockingQueue<Runnable> workQueue;//任务队列
-
ExecutorService es;//线程池的接口
-
public PoolSend() {
-
workQueue = new LinkedBlockingQueue<>();//构造无界的任务队列,资源足够,理论可以支持无线个任务
-
es = new ThreadPoolExecutor(2, 4, //2 core; 4 max
-
30,TimeUnit.SECONDS, workQueue, //30s keep alive
-
new ThreadPoolExecutor.CallerRunsPolicy()); //任务失败重试
-
}
-
public void send(Runnable task) {
-
System.out.println("PoolSend start sending mail...");
-
es.execute(task);//将任务放入线程池
-
}
-
public void close() {// 关闭
-
es.shutdown();
-
}
-
}
任务
要将发送的功能封装成Runnable
-
package mailTest;
-
import java.io.BufferedReader;
-
import java.io.FileInputStream;
-
import java.io.InputStreamReader;
-
import java.util.Date;
-
import java.util.Properties;
-
import javax.mail.BodyPart;
-
import javax.mail.Message;
-
import javax.mail.MessagingException;
-
import javax.mail.Multipart;
-
import javax.mail.Session;
-
import javax.mail.Transport;
-
import javax.mail.internet.MimeBodyPart;
-
import javax.mail.internet.MimeMessage;
-
import javax.mail.internet.MimeMultipart;
-
public class Sender implements Runnable {
-
Properties props;
-
Session session;
-
MimeMessage msg;
-
public Sender() {
-
System.out.println("constructor...");
-
props = new Properties();
-
props.put("mail.smtp.host", "smtp.163.com");
-
session = Session.getInstance(props, null);
-
msg = new MimeMessage(session);
-
try {
-
msg.setFrom("[email protected]");
-
msg.setRecipients(Message.RecipientType.TO, "[email protected]");
-
msg.setSubject("JavaMail hello world example");
-
msg.setSentDate(new Date());
-
String filename = "C:\\Users\\Tony\\Desktop\\a.html";
-
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filename), "UTF-8"));// 解决读取中文乱码
-
String line = null;
-
StringBuffer sb = new StringBuffer();
-
while ((line = br.readLine()) != null) {
-
sb.append(line);
-
sb.append("\n");
-
}
-
br.close();
-
BodyPart bodyPart = new MimeBodyPart();
-
bodyPart.setContent(sb.toString(), "text/html;charset=UTF-8");
-
Multipart multiPart = new MimeMultipart();
-
multiPart.addBodyPart(bodyPart);
-
msg.setContent(multiPart);
-
msg.saveChanges();
-
System.out.println("sent");
-
// msg.setText("Hello, world!\n");
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
}
-
public void send() {
-
try {
-
Transport.send(msg, "[email protected]", "password");
-
System.out.println("sent success!");
-
} catch (MessagingException mex) {
-
System.out.println(new Date() + " send failed, exception: " + mex);
-
}
-
}
-
@Override
-
public void run() {
-
// TODO Auto-generated method stub
-
send();
-
}
-
}
专门一个任务类,实现Runnable接口,实现run函数,主要的内容跟发送的一样,就是多了接口的函数
然而这只是一个例子,性能不行
因为每个线程都读本地的一个文本来构造发送函数,简直不科学,具体怎么封装,还得看大家发挥啦
测试了同时来100个发送请求,应该能够支持系统的需求了,大约每秒发1~5封,大概也是163设置的限制了,没有失败哟
费时的io调用,使用了多线程,线程池,性能大大提高,缓冲了蜂拥的请求,因为对邮件发送,可以忍受几秒的延迟,但是如果页面卡住几秒就不好了
失败还有重发的功能,简直不能再棒了