多线程高并发邮件发送
本章将带大家从零开始搭建一个多线程高并发邮件发送系统,
阅读本章的前提:
1.J2SE基本学完
2.JMeter学会基本使用
3.下载本章全部代码
下载地址:https://www.java123.vip/javaproc.php
代码下载完毕后,在vip.java123.mail包中将会有如下结构
接下来将对这7种程序逐一说明:
代码说明(sample1)
在网站进行会员注册时,都会发送注册邮件,本教程我们将从零开始构建一个支持高并发的实时邮件发送系统。
首先,我们来看一下邮件发送的简单流程:
1.服务器监听在8000端口,等待客户端连接。
2.接收到客户端的连接,得到目标邮件,用户名等邮件发送信息。
3.连接邮件服务器,进行发送邮件。
4.返回发送结果给客户。
5.回到步骤1。
如下图所示
实现代码如下:
・模拟邮件发送服务器程序
・接收客户端请求的服务器程序
・客户端程序
Eclipse下运行结果如下:
启动服务器后服务器输出
启动客户端后客户端输出
服务器输出
我们用JMeter对该系统进行测试,假设发送一封邮件需要耗时3秒。
设置一个请求
设置连接地址,端口号,发送的消息
执行后结果如下:
可以看出该请求耗时3秒钟
如果多个请求同时到达,会有什么问题?
假设发送邮件的时间为3秒,10个请求同时到达,要依次等待每一个邮件发送完毕才能发送下一封邮件。
也就是要30秒才能处理10个请求。
我们用Jmeter来试一下,并发数改成10个
执行结果如下:
可以看到最快的请求用了3秒,最慢的请求用了近30秒。
代码说明(sample2)
我们可以针对每一个请求开一个单独的线程来处理,这样服务器只需要处理接收请求,开线程,然后再接收请求这个动作,避免了因为业务(发送邮件)的耗时而影响服务器的性能。
如下图所示:
・接收客户端请求的服务器程序
・Socket线程程序
这样处理10个请求的时间就是3秒钟+启动10个线程的时间(暂时忽略不计),比串行方式快了近10倍。
我们来用10个并发测试一下:
可以看到平均三秒钟左右处理了10个请求
但每个请求3秒钟的处理远远不能满足高并发系统的要求。
我们希望将每个请求控制在毫秒级。
代码说明(sample3)
我们继续将系统改成这样:
用一个发送队列来存放邮件信息,接收到邮件信息后,放入发送队列然后马上返回给客户端成功消息。
这样处理一个请求的时间就是将信息放入队列的时间,达到了毫秒级的要求。
(因为邮件的发送在网络上本身就有长时间的延迟,所以可以放入发送队列后,提示客户端邮件已经发送,请检查邮件。如果后续发送失败,可以网站短消息等其他方式通知客户,或建立重发机制等。我们这里不去讨论具体的业务问题)
如下图所示:
在上图中我们做了一个消费者来对队列中的邮件进行发送。
这种方式我们称为生产者-消费者模式,一般来说,生产者和消费者中间有个缓冲,我们可以通过调整缓冲的大小达到接收多个请求的目的,如下图所示:
也可以多个生产者多个消费者,如下图:
代码修改如下:
・接收客户端请求的服务器程序
在服务器启动前,我们启动了两个处理邮件发送的消费者线程。
・Socket线程程序(生产者线程,放入队列后立即返回)
・发送邮件的消费者线程
・队列
我们来测试10个并发:
可以看到在3到11毫秒之间,完成了邮件的发送请求处理。
代码说明(sample4)
如果对每个请求都启动一个新线程对象来处理的话,对于瞬间的高并发请求,会占用大量的系统资源(内存)和线程切换的开销(CPU)。
所以我们可以用线程池来管理线程,达到控制线程的数量等目的。
如下图所示:
代码修改如下:
・接收客户端请求的服务器程序
代码说明(sample5)
为了阅读方便,我们把程序模块化,如下图所示:
代码说明(sample6)
反垃圾邮件:
如果对同一个域名下的邮件发送过于频繁,会被该服务器拒绝接受,甚至加入黑名单。
所以,我们需要控制对指定域名的发送速度。
可以通过发送令牌的方式来控制速度,比如对于A域名,做一个阻塞队列,每隔5秒钟,生产者向该队列放入一个发送令牌,消费者拿到令牌后,从该A域下的待发送队列中提取一封邮件进行发送。
如下图所示:
我们对如下代码进行了修改:
・邮件发送线程
・发送令牌队列
・发送令牌生成Task
开10个并发进行测试
2到20毫秒内请求完成,后台在根据延迟时间发邮件
代码说明(sample7)
发送邮件部分请根据自己的邮件情况进行修改,下面给出代码例子:
如有问题,大家来我的网站进行提问。
https://www.java123.vip/qa
版权声明:本教程版权归java123.vip所有,禁止任何形式的转载与引用。